From a61412069d0fbddffa223faef5212e3c796d47ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 11 Mar 2026 23:37:14 +0000 Subject: [PATCH 1/2] Account for ownership mismatch on argument that doesn't meet bound ``` error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied --> $DIR/ownership-mismatch-on-arg.rs:42:13 | LL | foo(hi, hi, hi); | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound | | | | | `needs_borrow::Hello` doesn't satisfy the trait bound | unsatisfied trait bound | help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` --> $DIR/ownership-mismatch-on-arg.rs:27:5 | LL | struct Hello; | ^^^^^^^^^^^^ help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` --> $DIR/ownership-mismatch-on-arg.rs:30:5 | LL | impl Tr for &Hello {} | ^^^^^^^^^^^^^^^^^^ note: required by a bound in `needs_borrow::foo` --> $DIR/ownership-mismatch-on-arg.rs:32:15 | LL | fn foo(_v: T, _w: T, _k: K) {} | ^^ required by this bound in `foo` help: consider borrowing these argument | LL | foo(&hi, &hi, hi); | + + ``` --- .../src/error_reporting/traits/suggestions.rs | 256 +++++++++++++----- .../self-mapping-arguments-errors.stderr | 15 +- .../as_expression.current.stderr | 4 +- ...d-intrinsic-monomorphization-bounds.stderr | 5 +- .../trait-bounds/ownership-mismatch-on-arg.rs | 47 ++++ .../ownership-mismatch-on-arg.stderr | 96 +++++++ 6 files changed, 353 insertions(+), 70 deletions(-) create mode 100644 tests/ui/trait-bounds/ownership-mismatch-on-arg.rs create mode 100644 tests/ui/trait-bounds/ownership-mismatch-on-arg.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 8c72f0d90bb58..7301f29b0fd7f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1643,85 +1643,215 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.predicate_must_hold_modulo_regions(&obligation) }; + 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 code = match obligation.cause.code() { ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, // FIXME(compiler-errors): This is kind of a mess, but required for obligations // that come from a path expr to affect the *call* expr. - c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _) + c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) 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, 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 - { - // 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())) - }); + if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id) { + // If that obligation comes from a type in an associated method call, we need + // special handling here. + if let hir::ExprKind::Call(base, _) = expr.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 + { + // 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 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( - sugg_msg(mtbl.prefix_str()), - vec![ - (outer.span.shrink_to_lo(), "<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ); + 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( + 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( + 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( + sugg_msg(""), + vec![ + ( + outer.span.shrink_to_lo().until(span), + "<&mut ".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( - sugg_msg("mut "), - vec![ - (outer.span.shrink_to_lo().until(span), "<&".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; + } else if let hir::ExprKind::Call(_, args) = expr.kind { + if let Some(typeck_results) = &self.typeck_results + && let Some(pred) = self + .tcx + .predicates_of(*def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(*idx) + && let Some(pred) = pred.as_trait_clause() + // This feature allows for `for T: Trait`, which fails + // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. + && !self.tcx.features().non_lifetime_binders() + { + let pred_ty = self.tcx.instantiate_bound_regions_with_erased( + pred.self_ty().skip_norm_wip(), ); - } - (_, true, hir::Mutability::Not) => { - err.multipart_suggestion( - sugg_msg(""), - vec![ - (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, + let fn_sig = self.tcx.instantiate_bound_regions_with_erased( + self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); + + let mut spans = vec![]; + for (arg, input) in args.into_iter().zip(fn_sig.inputs()) { + if let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) { + let ty = self.tcx.instantiate_bound_regions_with_erased( + poly_trait_pred.self_ty(), + ); + let pred_has_arg_type = + self.infcx.can_eq(param_env, arg_ty, ty); + let arg_is_type_param = + self.infcx.can_eq(param_env, pred_ty, *input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans + .iter() + .map(|sp| (sp.shrink_to_lo(), "&".into())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans + .iter() + .map(|sp| (sp.shrink_to_lo(), "&mut ".into())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + return false; + } } - _ => {} } - // If we didn't return early here, we would instead suggest `&&str::from("")`. - return false; } c } + ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) + if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) + && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind + && let Some(typeck_results) = &self.typeck_results + && let Some(pred) = self + .tcx + .predicates_of(*def_id) + .instantiate_identity(self.tcx) + .predicates + .into_iter() + .nth(*idx) + && let Some(pred) = pred.as_trait_clause() + // This feature allows for `for T: Trait`, which fails + // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. + && !self.tcx.features().non_lifetime_binders() => + { + // We've got a method call where likely one of the arguments didn't meet a bound. + let pred_ty = + self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); + let fn_sig = self.tcx.instantiate_bound_regions_with_erased( + self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), + ); + + let mut spans = vec![]; + for (arg, input) in [rcvr].into_iter().chain(args.into_iter()).zip(fn_sig.inputs()) + { + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) else { continue }; + let ty = + self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); + let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, ty); + let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, *input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + } + return false; + } c if matches!( span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(DesugaringKind::ForLoop) diff --git a/tests/ui/delegation/self-mapping-arguments-errors.stderr b/tests/ui/delegation/self-mapping-arguments-errors.stderr index a508721e68640..cd38ad84aeabf 100644 --- a/tests/ui/delegation/self-mapping-arguments-errors.stderr +++ b/tests/ui/delegation/self-mapping-arguments-errors.stderr @@ -22,11 +22,16 @@ LL | | } error[E0277]: the trait bound `(): target_expr_doesnt_relower_when_defs_inside::MyAdd` is not satisfied --> $DIR/self-mapping-arguments-errors.rs:14:5 | -LL | / reuse impl MyAdd for W { -... | -LL | | self.0 -LL | | } - | |_____^ the trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` is not implemented for `()` +LL | reuse impl MyAdd for W { + | _____^ - + | |____________________________| +... || +LL | || self.0 +LL | || } + | || ^ + | ||_____| + | |_____`{type error}` doesn't satisfy the trait bound + | the trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` is not implemented for `()` | help: the following other types implement trait `target_expr_doesnt_relower_when_defs_inside::MyAdd` --> $DIR/self-mapping-arguments-errors.rs:8:5 diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr index 0cb117d3fc4c3..fb5a3e2172da5 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.current.stderr @@ -16,7 +16,9 @@ error[E0277]: the trait bound `X: A` is not satisfied --> $DIR/as_expression.rs:60:15 | LL | X.start().foo().finish(); - | ^^^ unsatisfied trait bound + | --------- ^^^ unsatisfied trait bound + | | + | `X` doesn't satisfy the trait bound | help: the trait `A` is not implemented for `X` --> $DIR/as_expression.rs:70:1 diff --git a/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr b/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr index db2a222c8f2c4..148e6ea1e6aca 100644 --- a/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr +++ b/tests/ui/intrinsics/bad-intrinsic-monomorphization-bounds.stderr @@ -2,7 +2,10 @@ error[E0277]: the trait bound `Foo: intrinsics::bounds::FloatPrimitive` is not s --> $DIR/bad-intrinsic-monomorphization-bounds.rs:16:5 | LL | intrinsics::fadd_fast(a, b) - | ^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | ^^^^^^^^^^^^^^^^^^^^^ - - `Foo` doesn't satisfy the trait bound + | | | + | | `Foo` doesn't satisfy the trait bound + | unsatisfied trait bound | help: the nightly-only, unstable trait `intrinsics::bounds::FloatPrimitive` is not implemented for `Foo` --> $DIR/bad-intrinsic-monomorphization-bounds.rs:13:1 diff --git a/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs b/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs new file mode 100644 index 0000000000000..a3d3b8a9a0826 --- /dev/null +++ b/tests/ui/trait-bounds/ownership-mismatch-on-arg.rs @@ -0,0 +1,47 @@ +// #134805 +mod needs_deref { + #[derive(Clone, Copy, Debug)] + struct Hello; + + trait Tr: Clone + Copy {} + impl Tr for Hello {} + + fn foo(_v: T, _w: T, _k: K) {} + + struct S; + impl S { + fn foo(&self, _v: T, _w: T, _k: K) {} + } + + fn bar() { + let hellos = [Hello; 3]; + for hi in hellos.iter() { + foo(hi, hi, hi); //~ ERROR: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + S.foo(hi, hi, hi); //~ ERROR: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + } + } +} + +mod needs_borrow { + #[derive(Clone, Copy, Debug)] + struct Hello; + + trait Tr: Clone + Copy {} + impl Tr for &Hello {} + + fn foo(_v: T, _w: T, _k: K) {} + + struct S; + impl S { + fn foo(&self, _v: T, _w: T, _k: K) {} + } + + fn bar() { + let hellos = [Hello; 3]; + for hi in hellos { + foo(hi, hi, hi); //~ ERROR: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + S.foo(hi, hi, hi); //~ ERROR: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + } + } +} +fn main() {} diff --git a/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr b/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr new file mode 100644 index 0000000000000..0d1f944dacfe8 --- /dev/null +++ b/tests/ui/trait-bounds/ownership-mismatch-on-arg.stderr @@ -0,0 +1,96 @@ +error[E0277]: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:19:13 + | +LL | foo(hi, hi, hi); + | ^^^ -- -- `&needs_deref::Hello` doesn't satisfy the trait bound + | | | + | | `&needs_deref::Hello` doesn't satisfy the trait bound + | the trait `needs_deref::Tr` is not implemented for `&needs_deref::Hello` + | +note: required by a bound in `needs_deref::foo` + --> $DIR/ownership-mismatch-on-arg.rs:9:15 + | +LL | fn foo(_v: T, _w: T, _k: K) {} + | ^^ required by this bound in `foo` + +error[E0277]: the trait bound `&needs_deref::Hello: needs_deref::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:20:15 + | +LL | S.foo(hi, hi, hi); + | ^^^ -- -- `&needs_deref::Hello` doesn't satisfy the trait bound + | | | + | | `&needs_deref::Hello` doesn't satisfy the trait bound + | the trait `needs_deref::Tr` is not implemented for `&needs_deref::Hello` + | +help: the trait `needs_deref::Tr` is implemented for `needs_deref::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:7:5 + | +LL | impl Tr for Hello {} + | ^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_deref::S::foo` + --> $DIR/ownership-mismatch-on-arg.rs:13:39 + | +LL | fn foo(&self, _v: T, _w: T, _k: K) {} + | ^^ required by this bound in `S::foo` + +error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:42:13 + | +LL | foo(hi, hi, hi); + | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound + | | | + | | `needs_borrow::Hello` doesn't satisfy the trait bound + | unsatisfied trait bound + | +help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:27:5 + | +LL | struct Hello; + | ^^^^^^^^^^^^ +help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:30:5 + | +LL | impl Tr for &Hello {} + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_borrow::foo` + --> $DIR/ownership-mismatch-on-arg.rs:32:15 + | +LL | fn foo(_v: T, _w: T, _k: K) {} + | ^^ required by this bound in `foo` +help: consider borrowing these argument + | +LL | foo(&hi, &hi, hi); + | + + + +error[E0277]: the trait bound `needs_borrow::Hello: needs_borrow::Tr` is not satisfied + --> $DIR/ownership-mismatch-on-arg.rs:43:15 + | +LL | S.foo(hi, hi, hi); + | ^^^ -- -- `needs_borrow::Hello` doesn't satisfy the trait bound + | | | + | | `needs_borrow::Hello` doesn't satisfy the trait bound + | unsatisfied trait bound + | +help: the trait `needs_borrow::Tr` is not implemented for `needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:27:5 + | +LL | struct Hello; + | ^^^^^^^^^^^^ +help: the trait `needs_borrow::Tr` is implemented for `&needs_borrow::Hello` + --> $DIR/ownership-mismatch-on-arg.rs:30:5 + | +LL | impl Tr for &Hello {} + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `needs_borrow::S::foo` + --> $DIR/ownership-mismatch-on-arg.rs:36:19 + | +LL | fn foo(&self, _v: T, _w: T, _k: K) {} + | ^^ required by this bound in `S::foo` +help: consider borrowing these argument + | +LL | S.foo(&hi, &hi, hi); + | + + + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. From 1e1a6990917f1f0292e35387659a3260e99f09d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 Jul 2026 08:47:59 +0000 Subject: [PATCH 2/2] deduplicate method/function call "point at arg" logic --- .../src/error_reporting/traits/suggestions.rs | 152 ++++++++---------- 1 file changed, 65 insertions(+), 87 deletions(-) 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 7301f29b0fd7f..75937ff5531b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1653,6 +1653,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { 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 mut point_at_relevant_args = + |pred_ty: Ty<'tcx>, args_and_inputs: Vec<(hir::Expr<'_>, Ty<'tcx>)>| { + let Some(typeck_results) = &self.typeck_results else { return false }; + + let erased_self_ty = + self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); + let mut spans = vec![]; + for (arg, input) in args_and_inputs { + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(&arg) else { continue }; + let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, erased_self_ty); + let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, input); + if pred_has_arg_type && arg_is_type_param { + err.span_label( + arg.span, + format!("`{arg_ty}` doesn't satisfy the trait bound"), + ); + spans.push(arg.span); + } + } + let this = pluralize!("this", spans.len()); + if !spans.is_empty() { + if imm_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + if mut_ref_self_ty_satisfies_pred { + err.multipart_suggestion( + format!("consider mutably borrowing {this} argument"), + spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), + Applicability::MaybeIncorrect, + ); + } + } + !spans.is_empty() + }; let code = match obligation.cause.code() { ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, // FIXME(compiler-errors): This is kind of a mess, but required for obligations @@ -1726,12 +1764,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If we didn't return early here, we would instead suggest `&&str::from("")`. return false; } else if let hir::ExprKind::Call(_, args) = expr.kind { - if let Some(typeck_results) = &self.typeck_results - && let Some(pred) = self + if let Some(pred) = self .tcx - .predicates_of(*def_id) + .clauses_of(*def_id) .instantiate_identity(self.tcx) - .predicates + .clauses .into_iter() .nth(*idx) && let Some(pred) = pred.as_trait_clause() @@ -1745,48 +1782,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let fn_sig = self.tcx.instantiate_bound_regions_with_erased( self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); - - let mut spans = vec![]; - for (arg, input) in args.into_iter().zip(fn_sig.inputs()) { - if let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) { - let ty = self.tcx.instantiate_bound_regions_with_erased( - poly_trait_pred.self_ty(), - ); - let pred_has_arg_type = - self.infcx.can_eq(param_env, arg_ty, ty); - let arg_is_type_param = - self.infcx.can_eq(param_env, pred_ty, *input); - if pred_has_arg_type && arg_is_type_param { - err.span_label( - arg.span, - format!("`{arg_ty}` doesn't satisfy the trait bound"), - ); - spans.push(arg.span); - } - } - } - let this = pluralize!("this", spans.len()); - if !spans.is_empty() { - if imm_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider borrowing {this} argument"), - spans - .iter() - .map(|sp| (sp.shrink_to_lo(), "&".into())) - .collect(), - Applicability::MaybeIncorrect, - ); - } - if mut_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider mutably borrowing {this} argument"), - spans - .iter() - .map(|sp| (sp.shrink_to_lo(), "&mut ".into())) - .collect(), - Applicability::MaybeIncorrect, - ); - } + if point_at_relevant_args( + pred_ty, + args.into_iter() + .zip(fn_sig.inputs()) + .map(|(e, t)| (*e, *t)) + .collect(), + ) { return false; } } @@ -1794,15 +1796,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } c } - ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) + c @ ObligationCauseCode::WhereClauseInExpr(def_id, _, hir_id, idx) if let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) && let hir::ExprKind::MethodCall(_segment, rcvr, args, ..) = expr.kind - && let Some(typeck_results) = &self.typeck_results && let Some(pred) = self .tcx - .predicates_of(*def_id) + .clauses_of(*def_id) .instantiate_identity(self.tcx) - .predicates + .clauses .into_iter() .nth(*idx) && let Some(pred) = pred.as_trait_clause() @@ -1810,47 +1811,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // `instantiate_bound_regions_with_erased`. Avoid suggesting for now. && !self.tcx.features().non_lifetime_binders() => { - // We've got a method call where likely one of the arguments didn't meet a bound. - let pred_ty = - self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); let fn_sig = self.tcx.instantiate_bound_regions_with_erased( self.tcx.fn_sig(*def_id).instantiate_identity().skip_norm_wip(), ); - - let mut spans = vec![]; - for (arg, input) in [rcvr].into_iter().chain(args.into_iter()).zip(fn_sig.inputs()) - { - let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(arg) else { continue }; - let ty = - self.tcx.instantiate_bound_regions_with_erased(poly_trait_pred.self_ty()); - let pred_has_arg_type = self.infcx.can_eq(param_env, arg_ty, ty); - let arg_is_type_param = self.infcx.can_eq(param_env, pred_ty, *input); - if pred_has_arg_type && arg_is_type_param { - err.span_label( - arg.span, - format!("`{arg_ty}` doesn't satisfy the trait bound"), - ); - spans.push(arg.span); - } - } - let this = pluralize!("this", spans.len()); - if !spans.is_empty() { - if imm_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider borrowing {this} argument"), - spans.iter().map(|sp| (sp.shrink_to_lo(), "&".into())).collect(), - Applicability::MaybeIncorrect, - ); - } - if mut_ref_self_ty_satisfies_pred { - err.multipart_suggestion( - format!("consider mutably borrowing {this} argument"), - spans.iter().map(|sp| (sp.shrink_to_lo(), "&mut ".into())).collect(), - Applicability::MaybeIncorrect, - ); - } + // We've got a method call where likely one of the arguments didn't meet a bound. + let pred_ty = + self.tcx.instantiate_bound_regions_with_erased(pred.self_ty().skip_norm_wip()); + if point_at_relevant_args( + pred_ty, + [rcvr] + .into_iter() + .chain(args.into_iter()) + .zip(fn_sig.inputs()) + .map(|(e, t)| (*e, *t)) + .collect(), + ) { + return false; } - return false; + c } c if matches!( span.ctxt().outer_expn_data().kind,