diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index e6597a67d0ba..1a308ee334d3 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -363,6 +363,7 @@ pub(super) fn maybe_report_ambiguity( if impl_candidates.len() < 40 { self.report_similar_impl_candidates( impl_candidates.as_slice(), + obligation, trait_pred, obligation.cause.body_id, &mut err, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 4a36cab36336..0d6cd53dacab 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1973,6 +1973,7 @@ pub(super) fn find_similar_impl_candidates( pub(super) fn report_similar_impl_candidates( &self, impl_candidates: &[ImplCandidate<'tcx>], + obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, body_def_id: LocalDefId, err: &mut Diag<'_>, @@ -2037,6 +2038,20 @@ pub(super) fn report_similar_impl_candidates( }; if let [single] = &impl_candidates { + let self_ty = trait_pred.skip_binder().self_ty(); + if !self_ty.has_escaping_bound_vars() { + let self_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty()); + if let ty::Ref(_, inner_ty, _) = self_ty.kind() + && self.can_eq(param_env, single.trait_ref.self_ty(), *inner_ty) + && !self.where_clause_expr_matches_failed_self_ty(obligation, self_ty) + { + // Avoid pointing at a nearby impl like `String: Borrow` when the + // failing obligation comes from something nested inside an enclosing call + // expression such as `foo(&[String::from("a")])`. + return true; + } + } + // If we have a single implementation, try to unify it with the trait ref // that failed. This should uncover a better hint for what *is* implemented. if self.probe(|_| { @@ -2456,6 +2471,7 @@ fn report_similar_impl_candidates_for_root_obligation( let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( &impl_candidates, + obligation, trait_pred, body_def_id, err, @@ -3137,6 +3153,7 @@ fn try_to_add_help_message( let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( &impl_candidates, + obligation, trait_predicate, body_def_id, err, 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 98504d2e0553..562c5956c9a8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1584,6 +1584,39 @@ pub fn extract_callable_info( if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } + pub(super) fn where_clause_expr_matches_failed_self_ty( + &self, + obligation: &PredicateObligation<'tcx>, + old_self_ty: Ty<'tcx>, + ) -> bool { + let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() else { + return true; + }; + let (Some(typeck_results), Some(body)) = ( + self.typeck_results.as_ref(), + self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id), + ) else { + return true; + }; + + let mut expr_finder = FindExprBySpan::new(obligation.cause.span, self.tcx); + expr_finder.visit_expr(body.value); + let Some(expr) = expr_finder.result else { + return true; + }; + + let inner_old_self_ty = match old_self_ty.kind() { + ty::Ref(_, inner_ty, _) => Some(*inner_ty), + _ => None, + }; + + [typeck_results.expr_ty_adjusted_opt(expr)].into_iter().flatten().any(|expr_ty| { + self.can_eq(obligation.param_env, expr_ty, old_self_ty) + || inner_old_self_ty + .is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty)) + }) + } + pub(super) fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, @@ -1857,6 +1890,15 @@ pub(super) fn suggest_add_reference_to_arg( if let hir::ExprKind::AddrOf(_, _, _) = expr.kind { return false; } + let old_self_ty = old_pred.skip_binder().self_ty(); + if !old_self_ty.has_escaping_bound_vars() + && !self.where_clause_expr_matches_failed_self_ty( + obligation, + self.tcx.instantiate_bound_regions_with_erased(old_pred.self_ty()), + ) + { + return false; + } let needs_parens_post = expr_needs_parens(expr); let needs_parens_pre = match self.tcx.parent_hir_node(expr.hir_id) { Node::Expr(e) diff --git a/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.rs b/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.rs new file mode 100644 index 000000000000..d8e2e7091d6e --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.rs @@ -0,0 +1,8 @@ +use std::borrow::Borrow; + +fn foo(_v: impl IntoIterator>) {} + +fn main() { + foo(&[String::from("a")]); + //~^ ERROR the trait bound `&String: Borrow` is not satisfied +} diff --git a/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.stderr b/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.stderr new file mode 100644 index 000000000000..8910ed892ec0 --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-borrow-whole-call-issue-155088.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `&String: Borrow` is not satisfied + --> $DIR/dont-suggest-borrow-whole-call-issue-155088.rs:6:5 + | +LL | foo(&[String::from("a")]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Borrow` is not implemented for `&String` + | +note: required by a bound in `foo` + --> $DIR/dont-suggest-borrow-whole-call-issue-155088.rs:3:42 + | +LL | fn foo(_v: impl IntoIterator>) {} + | ^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.