Skip to content

Commit

Permalink
Detect when method call on argument could be removed to fulfill faile…
Browse files Browse the repository at this point in the history
…d trait bound

When encountering

```rust
struct Foo;
struct Bar;
impl From<Bar> for Foo {
    fn from(_: Bar) -> Self { Foo }
}
fn qux(_: impl From<Bar>) {}
fn main() {
    qux(Bar.into());
}
```

Suggest removing `.into()`:

```
error[E0283]: type annotations needed
 --> f100.rs:8:13
  |
8 |     qux(Bar.into());
  |     ---     ^^^^
  |     |
  |     required by a bound introduced by this call
  |
  = note: cannot satisfy `_: From<Bar>`
note: required by a bound in `qux`
 --> f100.rs:6:16
  |
6 | fn qux(_: impl From<Bar>) {}
  |                ^^^^^^^^^ required by this bound in `qux`
help: try using a fully qualified path to specify the expected types
  |
8 |     qux(<Bar as Into<T>>::into(Bar));
  |         +++++++++++++++++++++++   ~
help: consider removing this method call, as the receiver has type `Bar` and `Bar: From<Bar>` can be fulfilled
  |
8 -     qux(Bar.into());
8 +     qux(Bar);
  |
```

Fix #71252
  • Loading branch information
estebank committed Feb 16, 2024
1 parent 0f806a9 commit 9b3fcf9
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -3961,6 +3961,24 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Node::Expr(expr) = tcx.hir_node(arg_hir_id)
&& let Some(typeck_results) = &self.typeck_results
{
if let hir::Expr { kind: hir::ExprKind::MethodCall(_, rcvr, _, _), .. } = expr
&& let Some(ty) = typeck_results.node_type_opt(rcvr.hir_id)
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
&& let pred = failed_pred.map_bound(|pred| pred.with_self_ty(tcx, ty))
&& self.predicate_must_hold_modulo_regions(&Obligation::misc(
tcx, expr.span, body_id, param_env, pred,
))
{
err.span_suggestion_verbose(
expr.span.with_lo(rcvr.span.hi()),
format!(
"consider removing this method call, as the receiver has type `{ty}` and \
`{pred}` trivially holds",
),
"",
Applicability::MaybeIncorrect,
);
}
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
let inner_expr = expr.peel_blocks();
let ty = typeck_results
Expand Down Expand Up @@ -4096,7 +4114,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}

if let Node::Expr(expr) = tcx.hir_node(call_hir_id) {
if let Node::Expr(expr) = call_node {
if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
| hir::ExprKind::MethodCall(
hir::PathSegment { ident: Ident { span, .. }, .. },
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/async-await/issue-72442.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ LL | let mut f = File::open(path.to_str())?;
|
note: required by a bound in `File::open`
--> $SRC_DIR/std/src/fs.rs:LL:COL
help: consider removing this method call, as the receiver has type `&Path` and `&Path: AsRef<Path>` trivially holds
|
LL - let mut f = File::open(path.to_str())?;
LL + let mut f = File::open(path)?;
|

error: aborting due to 1 previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/error-should-say-copy-not-pod.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ note: required by a bound in `check_bound`
|
LL | fn check_bound<T:Copy>(_: T) {}
| ^^^^ required by this bound in `check_bound`
help: consider removing this method call, as the receiver has type `&'static str` and `&'static str: Copy` trivially holds
|
LL - check_bound("nocopy".to_string());
LL + check_bound("nocopy");
|

error: aborting due to 1 previous error

Expand Down
5 changes: 5 additions & 0 deletions tests/ui/suggestions/issue-84973-blacklist.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ note: required by a bound in `f_copy`
|
LL | fn f_copy<T: Copy>(t: T) {}
| ^^^^ required by this bound in `f_copy`
help: consider removing this method call, as the receiver has type `&'static str` and `&'static str: Copy` trivially holds
|
LL - f_copy("".to_string());
LL + f_copy("");
|

error[E0277]: the trait bound `S: Clone` is not satisfied
--> $DIR/issue-84973-blacklist.rs:16:13
Expand Down
11 changes: 11 additions & 0 deletions tests/ui/trait-bounds/argument-with-unnecessary-method-call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
struct Foo;
struct Bar;
impl From<Bar> for Foo {
fn from(_: Bar) -> Self { Foo }
}
fn qux(_: impl From<Bar>) {}
fn main() {
qux(Bar.into()); //~ ERROR type annotations needed
//~| HELP try using a fully qualified path to specify the expected types
//~| HELP consider removing this method call, as the receiver has type `Bar` and `Bar: From<Bar>` trivially holds
}
27 changes: 27 additions & 0 deletions tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0283]: type annotations needed
--> $DIR/argument-with-unnecessary-method-call.rs:8:13
|
LL | qux(Bar.into());
| --- ^^^^
| |
| required by a bound introduced by this call
|
= note: cannot satisfy `_: From<Bar>`
note: required by a bound in `qux`
--> $DIR/argument-with-unnecessary-method-call.rs:6:16
|
LL | fn qux(_: impl From<Bar>) {}
| ^^^^^^^^^ required by this bound in `qux`
help: try using a fully qualified path to specify the expected types
|
LL | qux(<Bar as Into<T>>::into(Bar));
| +++++++++++++++++++++++ ~
help: consider removing this method call, as the receiver has type `Bar` and `Bar: From<Bar>` trivially holds
|
LL - qux(Bar.into());
LL + qux(Bar);
|

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.

0 comments on commit 9b3fcf9

Please sign in to comment.