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 5bf9be87b57..b369c733871 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,6 +1,6 @@ use super::{ - EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, - SelectionContext, + DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation, + ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, }; use crate::autoderef::Autoderef; @@ -496,50 +496,78 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { // It only make sense when suggesting dereferences for arguments - let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } = - obligation.cause.code() - { - parent_code.clone() - } else { + let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else { return false; }; let param_env = obligation.param_env; let body_id = obligation.cause.body_id; let span = obligation.cause.span; - let real_trait_pred = match &*code { - ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred, - ObligationCauseCode::DerivedObligation(cause) - | ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred, - _ => trait_pred, - }; - let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else { - return false; - }; + let mut real_trait_pred = trait_pred; + let mut code = obligation.cause.code(); + loop { + match &code { + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + code = &parent_code; + } + ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause { + derived: DerivedObligationCause { parent_code, parent_trait_pred }, + .. + }) + | ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause { + parent_code, + parent_trait_pred, + }) + | ObligationCauseCode::DerivedObligation(DerivedObligationCause { + parent_code, + parent_trait_pred, + }) => { + code = &parent_code; + real_trait_pred = *parent_trait_pred; + } + _ => break, + }; + let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else { + continue; + }; - if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { - let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); - if let Some(steps) = autoderef.find_map(|(ty, steps)| { - // Re-add the `&` - let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty); - Some(steps).filter(|_| self.predicate_may_hold(&obligation)) - }) { - if steps > 0 { - if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { - // Don't care about `&mut` because `DerefMut` is used less - // often and user will not expect autoderef happens. - if src.starts_with('&') && !src.starts_with("&mut ") { - let derefs = "*".repeat(steps); - err.span_suggestion( - span, - "consider adding dereference here", - format!("&{}{}", derefs, &src[1..]), - Applicability::MachineApplicable, - ); - return true; + if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { + let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); + if let Some(steps) = autoderef.find_map(|(ty, steps)| { + // Re-add the `&` + let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty); + Some(steps).filter(|_| self.predicate_may_hold(&obligation)) + }) { + if steps > 0 { + if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect autoderef happens. + if src.starts_with('&') && !src.starts_with("&mut ") { + let derefs = "*".repeat(steps); + err.span_suggestion( + span, + "consider dereferencing here", + format!("&{}{}", derefs, &src[1..]), + Applicability::MachineApplicable, + ); + return true; + } } } + } else if real_trait_pred != trait_pred { + // This branch addresses #87437. + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, base_ty); + if self.predicate_may_hold(&obligation) { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider dereferencing here", + "*".to_string(), + Applicability::MachineApplicable, + ); + return true; + } } } } diff --git a/src/test/ui/traits/suggest-deferences/issue-39029.stderr b/src/test/ui/traits/suggest-deferences/issue-39029.stderr index 2c225f4311d..5c324cd38a3 100644 --- a/src/test/ui/traits/suggest-deferences/issue-39029.stderr +++ b/src/test/ui/traits/suggest-deferences/issue-39029.stderr @@ -5,7 +5,7 @@ LL | let _errors = TcpListener::bind(&bad); | ----------------- ^^^^ | | | | | the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs` - | | help: consider adding dereference here: `&*bad` + | | help: consider dereferencing here: `&*bad` | required by a bound introduced by this call | = note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs` diff --git a/src/test/ui/traits/suggest-deferences/issue-62530.stderr b/src/test/ui/traits/suggest-deferences/issue-62530.stderr index b77af7ddf47..d129328dae8 100644 --- a/src/test/ui/traits/suggest-deferences/issue-62530.stderr +++ b/src/test/ui/traits/suggest-deferences/issue-62530.stderr @@ -5,7 +5,7 @@ LL | takes_type_parameter(&string); // Error | -------------------- ^^^^^^^ | | | | | the trait `SomeTrait` is not implemented for `&String` - | | help: consider adding dereference here: `&*string` + | | help: consider dereferencing here: `&*string` | required by a bound introduced by this call | note: required by a bound in `takes_type_parameter` diff --git a/src/test/ui/traits/suggest-deferences/multiple-0.stderr b/src/test/ui/traits/suggest-deferences/multiple-0.stderr index bf9f85f1b45..efb3c7d123f 100644 --- a/src/test/ui/traits/suggest-deferences/multiple-0.stderr +++ b/src/test/ui/traits/suggest-deferences/multiple-0.stderr @@ -5,7 +5,7 @@ LL | foo(&baz); | --- ^^^^ | | | | | the trait `Happy` is not implemented for `&Baz` - | | help: consider adding dereference here: `&***baz` + | | help: consider dereferencing here: `&***baz` | required by a bound introduced by this call | note: required by a bound in `foo` diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.fixed b/src/test/ui/traits/suggest-deferences/root-obligation.fixed new file mode 100644 index 00000000000..9fd19240678 --- /dev/null +++ b/src/test/ui/traits/suggest-deferences/root-obligation.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +fn get_vowel_count(string: &str) -> usize { + string + .chars() + .filter(|c| "aeiou".contains(*c)) + //~^ ERROR expected a `Fn<(char,)>` closure, found `char` + .count() +} + +fn main() { + let _ = get_vowel_count("asdf"); +} + diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.rs b/src/test/ui/traits/suggest-deferences/root-obligation.rs new file mode 100644 index 00000000000..4dd0291b629 --- /dev/null +++ b/src/test/ui/traits/suggest-deferences/root-obligation.rs @@ -0,0 +1,14 @@ +// run-rustfix + +fn get_vowel_count(string: &str) -> usize { + string + .chars() + .filter(|c| "aeiou".contains(c)) + //~^ ERROR expected a `Fn<(char,)>` closure, found `char` + .count() +} + +fn main() { + let _ = get_vowel_count("asdf"); +} + diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.stderr b/src/test/ui/traits/suggest-deferences/root-obligation.stderr new file mode 100644 index 00000000000..16e03e79c75 --- /dev/null +++ b/src/test/ui/traits/suggest-deferences/root-obligation.stderr @@ -0,0 +1,24 @@ +error[E0277]: expected a `Fn<(char,)>` closure, found `char` + --> $DIR/root-obligation.rs:6:38 + | +LL | .filter(|c| "aeiou".contains(c)) + | -------- ^ expected an `Fn<(char,)>` closure, found `char` + | | + | required by a bound introduced by this call + | + = help: the trait `Fn<(char,)>` is not implemented for `char` + = note: required because of the requirements on the impl of `FnOnce<(char,)>` for `&char` + = note: required because of the requirements on the impl of `Pattern<'_>` for `&char` +note: required by a bound in `core::str::::contains` + --> $SRC_DIR/core/src/str/mod.rs:LL:COL + | +LL | pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + | ^^^^^^^^^^^ required by this bound in `core::str::::contains` +help: consider dereferencing here + | +LL | .filter(|c| "aeiou".contains(*c)) + | + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.