From 9b3fcf9ad44faa6362be348ef233bbc81d128308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 14 Feb 2024 17:48:40 +0000 Subject: [PATCH] Detect when method call on argument could be removed to fulfill failed trait bound When encountering ```rust struct Foo; struct Bar; impl From for Foo { fn from(_: Bar) -> Self { Foo } } fn qux(_: impl From) {} 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` note: required by a bound in `qux` --> f100.rs:6:16 | 6 | fn qux(_: impl From) {} | ^^^^^^^^^ required by this bound in `qux` help: try using a fully qualified path to specify the expected types | 8 | qux(>::into(Bar)); | +++++++++++++++++++++++ ~ help: consider removing this method call, as the receiver has type `Bar` and `Bar: From` can be fulfilled | 8 - qux(Bar.into()); 8 + qux(Bar); | ``` Fix #71252 --- .../src/traits/error_reporting/suggestions.rs | 20 +++++++++++++- tests/ui/async-await/issue-72442.stderr | 5 ++++ tests/ui/error-should-say-copy-not-pod.stderr | 5 ++++ .../suggestions/issue-84973-blacklist.stderr | 5 ++++ .../argument-with-unnecessary-method-call.rs | 11 ++++++++ ...gument-with-unnecessary-method-call.stderr | 27 +++++++++++++++++++ 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/ui/trait-bounds/argument-with-unnecessary-method-call.rs create mode 100644 tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 335e6ff28226e..00672af52eada 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -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 @@ -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, .. }, .. }, diff --git a/tests/ui/async-await/issue-72442.stderr b/tests/ui/async-await/issue-72442.stderr index 313f6079c7c16..e68f7a299908f 100644 --- a/tests/ui/async-await/issue-72442.stderr +++ b/tests/ui/async-await/issue-72442.stderr @@ -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` 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 diff --git a/tests/ui/error-should-say-copy-not-pod.stderr b/tests/ui/error-should-say-copy-not-pod.stderr index 658584e2ff4bb..6aa129fa29b53 100644 --- a/tests/ui/error-should-say-copy-not-pod.stderr +++ b/tests/ui/error-should-say-copy-not-pod.stderr @@ -11,6 +11,11 @@ note: required by a bound in `check_bound` | LL | fn check_bound(_: 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 diff --git a/tests/ui/suggestions/issue-84973-blacklist.stderr b/tests/ui/suggestions/issue-84973-blacklist.stderr index 8e980997089e6..3333822832856 100644 --- a/tests/ui/suggestions/issue-84973-blacklist.stderr +++ b/tests/ui/suggestions/issue-84973-blacklist.stderr @@ -11,6 +11,11 @@ note: required by a bound in `f_copy` | LL | fn f_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 diff --git a/tests/ui/trait-bounds/argument-with-unnecessary-method-call.rs b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.rs new file mode 100644 index 0000000000000..d8fd1d44a9858 --- /dev/null +++ b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.rs @@ -0,0 +1,11 @@ +struct Foo; +struct Bar; +impl From for Foo { + fn from(_: Bar) -> Self { Foo } +} +fn qux(_: impl From) {} +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` trivially holds +} diff --git a/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr new file mode 100644 index 0000000000000..49230c98a12c1 --- /dev/null +++ b/tests/ui/trait-bounds/argument-with-unnecessary-method-call.stderr @@ -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` +note: required by a bound in `qux` + --> $DIR/argument-with-unnecessary-method-call.rs:6:16 + | +LL | fn qux(_: impl From) {} + | ^^^^^^^^^ required by this bound in `qux` +help: try using a fully qualified path to specify the expected types + | +LL | qux(>::into(Bar)); + | +++++++++++++++++++++++ ~ +help: consider removing this method call, as the receiver has type `Bar` and `Bar: From` 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`.