Auto merge of #11135 - smoelius:unwrap_or_else_default-fp, r=Centri3

Fix `unwrap_or_else_default` false positive

This PR fixes a false positive in the handling of `unwrap_or_else` with a default value when the value is needed for type inference.

An easy example to exhibit the false positive is the following:
```rust
    let option = None;
    option.unwrap_or_else(Vec::new).push(1);
```
The following code would not compile, because the fact that the value is a `Vec` has been lost:
```rust
    let option = None;
    option.unwrap_or_default().push(1);
```
The fix is to:
- implement a heuristic to tell whether an expression's type can be determined purely from its subexpressions, and the arguments and locals they use;
- apply the heuristic to `unwrap_or_else`'s receiver.

The heuristic returns false when applied to `option` in the above example, but it returns true when applied to `option` in either of the following examples:
```rust
    let option: Option<Vec<u64>> = None;
    option.unwrap_or_else(Vec::new).push(1);
```
```rust
    let option = None::<Vec<u64>>;
    option.unwrap_or_else(Vec::new).push(1);
```

(Aside: https://github.com/rust-lang/rust-clippy/pull/10120 unfairly contained multiple changes in one PR. I am trying to break that PR up into smaller pieces.)

---

changelog: FP: [`unwrap_or_else_default`]: No longer lints if the default value is needed for type inference
This commit is contained in:
bors 2023-07-19 11:37:30 +00:00
commit 7a34143fa3
7 changed files with 568 additions and 2 deletions

View file

@ -4,7 +4,7 @@ use super::UNWRAP_OR_ELSE_DEFAULT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_default_equivalent_call;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::ty::{expr_type_is_certain, is_type_diagnostic_item};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
@ -17,6 +17,10 @@ pub(super) fn check<'tcx>(
recv: &'tcx hir::Expr<'_>,
u_arg: &'tcx hir::Expr<'_>,
) {
if !expr_type_is_certain(cx, recv) {
return;
}
// something.unwrap_or_else(Default::default)
// ^^^^^^^^^- recv ^^^^^^^^^^^^^^^^- u_arg
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr