diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index e99782fdc65..998405bcbe1 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -16,8 +16,8 @@ use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; @@ -30,7 +30,7 @@ use std::cmp::Ordering; use std::iter; -use super::probe::{Mode, ProbeScope}; +use super::probe::{IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -1069,6 +1069,8 @@ trait bound{s}", } } + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + return Some(err); } @@ -1651,6 +1653,62 @@ fn suggest_derive( } } + fn check_for_deref_method( + &self, + err: &mut Diagnostic, + self_source: SelfSource<'tcx>, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + ) { + let SelfSource::QPath(ty) = self_source else { return; }; + for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { + if let Ok(pick) = self.probe_for_name( + ty.span, + Mode::Path, + item_name, + IsSuggestion(true), + deref_ty, + ty.hir_id, + ProbeScope::TraitsInScope, + ) { + if deref_ty.is_suggestable(self.tcx, true) + // If this method receives `&self`, then the provided + // argument _should_ coerce, so it's valid to suggest + // just changing the path. + && pick.item.fn_has_self_parameter + && let Some(self_ty) = + self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) + && self_ty.is_ref() + { + let suggested_path = match deref_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Str + | ty::Projection(_) + | ty::Param(_) => format!("{deref_ty}"), + _ => format!("<{deref_ty}>"), + }; + err.span_suggestion_verbose( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + suggested_path, + Applicability::MaybeIncorrect, + ); + } else { + err.span_note( + ty.span, + format!("the function `{item_name}` is implemented on `{deref_ty}`"), + ); + } + return; + } + } + } + /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { diff --git a/src/test/ui/suggestions/deref-path-method.rs b/src/test/ui/suggestions/deref-path-method.rs new file mode 100644 index 00000000000..0281cdb6b37 --- /dev/null +++ b/src/test/ui/suggestions/deref-path-method.rs @@ -0,0 +1,6 @@ +fn main() { + let vec = Vec::new(); + Vec::contains(&vec, &0); + //~^ ERROR no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope + //~| HELP the function `contains` is implemented on `[_]` +} diff --git a/src/test/ui/suggestions/deref-path-method.stderr b/src/test/ui/suggestions/deref-path-method.stderr new file mode 100644 index 00000000000..1cc37d61151 --- /dev/null +++ b/src/test/ui/suggestions/deref-path-method.stderr @@ -0,0 +1,14 @@ +error[E0599]: no function or associated item named `contains` found for struct `Vec<_, _>` in the current scope + --> $DIR/deref-path-method.rs:3:10 + | +LL | Vec::contains(&vec, &0); + | ^^^^^^^^ function or associated item not found in `Vec<_, _>` + | +help: the function `contains` is implemented on `[_]` + | +LL | <[_]>::contains(&vec, &0); + | ~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`.