From bb74f4732739d41bfca4e0689a3f00dda0439c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 1 Nov 2024 19:00:05 +0000 Subject: [PATCH 1/2] Do not suggest borrow that is already there in fully-qualified call When encountering `&str::from("value")` do not suggest `&&str::from("value")`. Fix #132041. --- .../src/error_reporting/traits/suggestions.rs | 9 +++++++ .../dont-suggest-borrowing-existing-borrow.rs | 5 ++++ ...t-suggest-borrowing-existing-borrow.stderr | 26 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs create mode 100644 tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 2bbf90ed3ed1e..a81f7574c5e51 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1195,6 +1195,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) if self.tcx.hir_span(*hir_id).lo() == span.lo() => { + if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::Call(base, _) = expr.kind + && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, _)) = base.kind + && ty.span == span + { + // Do not suggest borrowing when we already do so. This would happen with + // `let _ = &str::from("");` where the expression corresponds to the `str`. + return false; + } c } c if matches!( diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs new file mode 100644 index 0000000000000..c25bde5d0a23d --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs @@ -0,0 +1,5 @@ +fn main() { + let _ = &str::from("value"); + //~^ ERROR the trait bound `str: From<_>` is not satisfied + //~| ERROR the size for values of type `str` cannot be known at compilation time +} diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr new file mode 100644 index 0000000000000..025014aa02290 --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr @@ -0,0 +1,26 @@ +error[E0277]: the trait bound `str: From<_>` is not satisfied + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:2:14 + | +LL | let _ = &str::from("value"); + | ^^^ the trait `From<_>` is not implemented for `str` + | + = help: the following other types implement trait `From`: + `String` implements `From<&String>` + `String` implements `From<&mut str>` + `String` implements `From<&str>` + `String` implements `From>` + `String` implements `From>` + `String` implements `From` + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:2:14 + | +LL | let _ = &str::from("value"); + | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: the return type of a function must have a statically known size + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 7603adc381012af0ee32f3aa1919bdfe5cc49393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 3 Jul 2025 16:14:20 +0000 Subject: [PATCH 2/2] Rework logic and provide structured suggestion --- .../src/error_reporting/traits/suggestions.rs | 81 ++++++++++++++++--- ...nt-suggest-borrowing-existing-borrow.fixed | 17 ++++ .../dont-suggest-borrowing-existing-borrow.rs | 12 +++ ...t-suggest-borrowing-existing-borrow.stderr | 38 ++++++++- 4 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.fixed diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index a81f7574c5e51..6a35fae347d09 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1187,6 +1187,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { has_custom_message: bool, ) -> bool { let span = obligation.cause.span; + let param_env = obligation.param_env; + + let mk_result = |trait_pred_and_new_ty| { + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); + self.predicate_must_hold_modulo_regions(&obligation) + }; let code = match obligation.cause.code() { ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, @@ -1195,13 +1202,74 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) if self.tcx.hir_span(*hir_id).lo() == span.lo() => { + // `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation. + // If that obligation comes from a type in an associated method call, we need + // special handling here. if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) && let hir::ExprKind::Call(base, _) = expr.kind - && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, _)) = base.kind + && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = base.kind + && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind && ty.span == span { - // Do not suggest borrowing when we already do so. This would happen with - // `let _ = &str::from("");` where the expression corresponds to the `str`. + // We've encountered something like `&str::from("")`, where the intended code + // was likely `<&str>::from("")`. The former is interpreted as "call method + // `from` on `str` and borrow the result", while the latter means "call method + // `from` on `&str`". + + let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| { + (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) + }); + let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| { + (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty())) + }); + + let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); + let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + let sugg_msg = |pre: &str| { + format!( + "you likely meant to call the associated function `{FN}` for type \ + `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \ + type `{TY}`", + FN = segment.ident, + TY = poly_trait_pred.self_ty(), + ) + }; + match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) { + (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => { + err.multipart_suggestion_verbose( + sugg_msg(mtbl.prefix_str()), + vec![ + (outer.span.shrink_to_lo(), "<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + (true, _, hir::Mutability::Mut) => { + // There's an associated function found on the immutable borrow of the + err.multipart_suggestion_verbose( + sugg_msg("mut "), + vec![ + (outer.span.shrink_to_lo().until(span), "<&".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + (_, true, hir::Mutability::Not) => { + err.multipart_suggestion_verbose( + sugg_msg(""), + vec![ + (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + _ => {} + } + // If we didn't return early here, we would instead suggest `&&str::from("")`. return false; } c @@ -1229,8 +1297,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { never_suggest_borrow.push(def_id); } - let param_env = obligation.param_env; - // Try to apply the original trait bound by borrowing. let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| @@ -1252,11 +1318,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }); - let mk_result = |trait_pred_and_new_ty| { - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); - self.predicate_must_hold_modulo_regions(&obligation) - }; let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.fixed b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.fixed new file mode 100644 index 0000000000000..95fd920dec229 --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +struct S; +trait Trait { + fn foo() {} +} +impl Trait for &S {} +impl Trait for &mut S {} +fn main() { + let _ = <&str>::from("value"); + //~^ ERROR the trait bound `str: From<_>` is not satisfied + //~| ERROR the size for values of type `str` cannot be known at compilation time + let _ = <&mut S>::foo(); + //~^ ERROR the trait bound `S: Trait` is not satisfied + let _ = <&S>::foo(); + //~^ ERROR the trait bound `S: Trait` is not satisfied +} diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs index c25bde5d0a23d..f79d2465062ac 100644 --- a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.rs @@ -1,5 +1,17 @@ +//@ run-rustfix + +struct S; +trait Trait { + fn foo() {} +} +impl Trait for &S {} +impl Trait for &mut S {} fn main() { let _ = &str::from("value"); //~^ ERROR the trait bound `str: From<_>` is not satisfied //~| ERROR the size for values of type `str` cannot be known at compilation time + let _ = &mut S::foo(); + //~^ ERROR the trait bound `S: Trait` is not satisfied + let _ = &S::foo(); + //~^ ERROR the trait bound `S: Trait` is not satisfied } diff --git a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr index 025014aa02290..ac96ec76da7b1 100644 --- a/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr +++ b/tests/ui/suggestions/dont-suggest-borrowing-existing-borrow.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `str: From<_>` is not satisfied - --> $DIR/dont-suggest-borrowing-existing-borrow.rs:2:14 + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:10:14 | LL | let _ = &str::from("value"); | ^^^ the trait `From<_>` is not implemented for `str` @@ -11,9 +11,41 @@ LL | let _ = &str::from("value"); `String` implements `From>` `String` implements `From>` `String` implements `From` +help: you likely meant to call the associated function `from` for type `&str`, but the code as written calls associated function `from` on type `str` + | +LL | let _ = <&str>::from("value"); + | + + + +error[E0277]: the trait bound `S: Trait` is not satisfied + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:13:18 + | +LL | let _ = &mut S::foo(); + | ^ the trait `Trait` is not implemented for `S` + | + = help: the following other types implement trait `Trait`: + &S + &mut S +help: you likely meant to call the associated function `foo` for type `&mut S`, but the code as written calls associated function `foo` on type `S` + | +LL | let _ = <&mut S>::foo(); + | + + + +error[E0277]: the trait bound `S: Trait` is not satisfied + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:15:14 + | +LL | let _ = &S::foo(); + | ^ the trait `Trait` is not implemented for `S` + | + = help: the following other types implement trait `Trait`: + &S + &mut S +help: you likely meant to call the associated function `foo` for type `&S`, but the code as written calls associated function `foo` on type `S` + | +LL | let _ = <&S>::foo(); + | + + error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/dont-suggest-borrowing-existing-borrow.rs:2:14 + --> $DIR/dont-suggest-borrowing-existing-borrow.rs:10:14 | LL | let _ = &str::from("value"); | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time @@ -21,6 +53,6 @@ LL | let _ = &str::from("value"); = help: the trait `Sized` is not implemented for `str` = note: the return type of a function must have a statically known size -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`.