summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/method
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_typeck/src/method')
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs594
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs625
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs415
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs1926
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs2605
5 files changed, 6165 insertions, 0 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
new file mode 100644
index 000000000..be4ea9986
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -0,0 +1,594 @@
+use super::{probe, MethodCallee};
+
+use crate::{callee, FnCtxt};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::GenericArg;
+use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
+use rustc_infer::infer::{self, InferOk};
+use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
+use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::subst::{self, SubstsRef};
+use rustc_middle::ty::{self, GenericParamDefKind, Ty};
+use rustc_span::Span;
+use rustc_trait_selection::traits;
+
+use std::iter;
+use std::ops::Deref;
+
+struct ConfirmContext<'a, 'tcx> {
+ fcx: &'a FnCtxt<'a, 'tcx>,
+ span: Span,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+}
+
+impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> {
+ type Target = FnCtxt<'a, 'tcx>;
+ fn deref(&self) -> &Self::Target {
+ self.fcx
+ }
+}
+
+#[derive(Debug)]
+pub struct ConfirmResult<'tcx> {
+ pub callee: MethodCallee<'tcx>,
+ pub illegal_sized_bound: Option<Span>,
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub fn confirm_method(
+ &self,
+ span: Span,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ unadjusted_self_ty: Ty<'tcx>,
+ pick: probe::Pick<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ ) -> ConfirmResult<'tcx> {
+ debug!(
+ "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})",
+ unadjusted_self_ty, pick, segment.args,
+ );
+
+ let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
+ confirm_cx.confirm(unadjusted_self_ty, pick, segment)
+ }
+}
+
+impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
+ fn new(
+ fcx: &'a FnCtxt<'a, 'tcx>,
+ span: Span,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ ) -> ConfirmContext<'a, 'tcx> {
+ ConfirmContext { fcx, span, self_expr, call_expr }
+ }
+
+ fn confirm(
+ &mut self,
+ unadjusted_self_ty: Ty<'tcx>,
+ pick: probe::Pick<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ ) -> ConfirmResult<'tcx> {
+ // Adjust the self expression the user provided and obtain the adjusted type.
+ let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
+
+ // Create substitutions for the method's type parameters.
+ let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
+ let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs);
+
+ debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}");
+
+ // Create the final signature for the method, replacing late-bound regions.
+ let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
+
+ // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
+ // something which derefs to `Self` actually implements the trait and the caller
+ // wanted to make a static dispatch on it but forgot to import the trait.
+ // See test `src/test/ui/issue-35976.rs`.
+ //
+ // In that case, we'll error anyway, but we'll also re-run the search with all traits
+ // in scope, and if we find another method which can be used, we'll output an
+ // appropriate hint suggesting to import the trait.
+ let filler_substs = rcvr_substs
+ .extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
+ let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
+ &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
+ );
+
+ // Unify the (adjusted) self type with what the method expects.
+ //
+ // SUBTLE: if we want good error messages, because of "guessing" while matching
+ // traits, no trait system method can be called before this point because they
+ // could alter our Self-type, except for normalizing the receiver from the
+ // signature (which is also done during probing).
+ let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]);
+ debug!(
+ "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}",
+ self_ty, method_sig_rcvr, method_sig, method_predicates
+ );
+ self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs);
+
+ let (method_sig, method_predicates) =
+ self.normalize_associated_types_in(self.span, (method_sig, method_predicates));
+ let method_sig = ty::Binder::dummy(method_sig);
+
+ // Make sure nobody calls `drop()` explicitly.
+ self.enforce_illegal_method_limitations(&pick);
+
+ // Add any trait/regions obligations specified on the method's type parameters.
+ // We won't add these if we encountered an illegal sized bound, so that we can use
+ // a custom error in that case.
+ if illegal_sized_bound.is_none() {
+ self.add_obligations(
+ self.tcx.mk_fn_ptr(method_sig),
+ all_substs,
+ method_predicates,
+ pick.item.def_id,
+ );
+ }
+
+ // Create the final `MethodCallee`.
+ let callee = MethodCallee {
+ def_id: pick.item.def_id,
+ substs: all_substs,
+ sig: method_sig.skip_binder(),
+ };
+ ConfirmResult { callee, illegal_sized_bound }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // ADJUSTMENTS
+
+ fn adjust_self_ty(
+ &mut self,
+ unadjusted_self_ty: Ty<'tcx>,
+ pick: &probe::Pick<'tcx>,
+ ) -> Ty<'tcx> {
+ // Commit the autoderefs by calling `autoderef` again, but this
+ // time writing the results into the various typeck results.
+ let mut autoderef =
+ self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
+ let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
+ return self.tcx.ty_error_with_message(
+ rustc_span::DUMMY_SP,
+ &format!("failed autoderef {}", pick.autoderefs),
+ );
+ };
+ assert_eq!(n, pick.autoderefs);
+
+ let mut adjustments = self.adjust_steps(&autoderef);
+ let mut target = self.structurally_resolved_type(autoderef.span(), ty);
+
+ match pick.autoref_or_ptr_adjustment {
+ Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
+ let region = self.next_region_var(infer::Autoref(self.span));
+ // Type we're wrapping in a reference, used later for unsizing
+ let base_ty = target;
+
+ target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
+ let mutbl = match mutbl {
+ hir::Mutability::Not => AutoBorrowMutability::Not,
+ hir::Mutability::Mut => AutoBorrowMutability::Mut {
+ // Method call receivers are the primary use case
+ // for two-phase borrows.
+ allow_two_phase_borrow: AllowTwoPhase::Yes,
+ },
+ };
+ adjustments.push(Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+ target,
+ });
+
+ if unsize {
+ let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
+ self.tcx.mk_slice(*elem_ty)
+ } else {
+ bug!(
+ "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
+ base_ty
+ )
+ };
+ target = self
+ .tcx
+ .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
+ adjustments
+ .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
+ }
+ }
+ Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
+ target = match target.kind() {
+ &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
+ assert_eq!(mutbl, hir::Mutability::Mut);
+ self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
+ }
+ other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
+ };
+
+ adjustments.push(Adjustment {
+ kind: Adjust::Pointer(PointerCast::MutToConstPointer),
+ target,
+ });
+ }
+ None => {}
+ }
+
+ self.register_predicates(autoderef.into_obligations());
+
+ // Write out the final adjustments.
+ self.apply_adjustments(self.self_expr, adjustments);
+
+ target
+ }
+
+ /// Returns a set of substitutions for the method *receiver* where all type and region
+ /// parameters are instantiated with fresh variables. This substitution does not include any
+ /// parameters declared on the method itself.
+ ///
+ /// Note that this substitution may include late-bound regions from the impl level. If so,
+ /// these are instantiated later in the `instantiate_method_sig` routine.
+ fn fresh_receiver_substs(
+ &mut self,
+ self_ty: Ty<'tcx>,
+ pick: &probe::Pick<'tcx>,
+ ) -> SubstsRef<'tcx> {
+ match pick.kind {
+ probe::InherentImplPick => {
+ let impl_def_id = pick.item.container_id(self.tcx);
+ assert!(
+ self.tcx.impl_trait_ref(impl_def_id).is_none(),
+ "impl {:?} is not an inherent impl",
+ impl_def_id
+ );
+ self.fresh_substs_for_item(self.span, impl_def_id)
+ }
+
+ probe::ObjectPick => {
+ let trait_def_id = pick.item.container_id(self.tcx);
+ self.extract_existential_trait_ref(self_ty, |this, object_ty, principal| {
+ // The object data has no entry for the Self
+ // Type. For the purposes of this method call, we
+ // substitute the object type itself. This
+ // wouldn't be a sound substitution in all cases,
+ // since each instance of the object type is a
+ // different existential and hence could match
+ // distinct types (e.g., if `Self` appeared as an
+ // argument type), but those cases have already
+ // been ruled out when we deemed the trait to be
+ // "object safe".
+ let original_poly_trait_ref = principal.with_self_ty(this.tcx, object_ty);
+ let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id);
+ let upcast_trait_ref =
+ this.replace_bound_vars_with_fresh_vars(upcast_poly_trait_ref);
+ debug!(
+ "original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}",
+ original_poly_trait_ref, upcast_trait_ref, trait_def_id
+ );
+ upcast_trait_ref.substs
+ })
+ }
+
+ probe::TraitPick => {
+ let trait_def_id = pick.item.container_id(self.tcx);
+
+ // Make a trait reference `$0 : Trait<$1...$n>`
+ // consisting entirely of type variables. Later on in
+ // the process we will unify the transformed-self-type
+ // of the method with the actual type in order to
+ // unify some of these variables.
+ self.fresh_substs_for_item(self.span, trait_def_id)
+ }
+
+ probe::WhereClausePick(poly_trait_ref) => {
+ // Where clauses can have bound regions in them. We need to instantiate
+ // those to convert from a poly-trait-ref to a trait-ref.
+ self.replace_bound_vars_with_fresh_vars(poly_trait_ref).substs
+ }
+ }
+ }
+
+ fn extract_existential_trait_ref<R, F>(&mut self, self_ty: Ty<'tcx>, mut closure: F) -> R
+ where
+ F: FnMut(&mut ConfirmContext<'a, 'tcx>, Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>) -> R,
+ {
+ // If we specified that this is an object method, then the
+ // self-type ought to be something that can be dereferenced to
+ // yield an object-type (e.g., `&Object` or `Box<Object>`
+ // etc).
+
+ // FIXME: this feels, like, super dubious
+ self.fcx
+ .autoderef(self.span, self_ty)
+ .include_raw_pointers()
+ .find_map(|(ty, _)| match ty.kind() {
+ ty::Dynamic(data, ..) => Some(closure(
+ self,
+ ty,
+ data.principal().unwrap_or_else(|| {
+ span_bug!(self.span, "calling trait method on empty object?")
+ }),
+ )),
+ _ => None,
+ })
+ .unwrap_or_else(|| {
+ span_bug!(
+ self.span,
+ "self-type `{}` for ObjectPick never dereferenced to an object",
+ self_ty
+ )
+ })
+ }
+
+ fn instantiate_method_substs(
+ &mut self,
+ pick: &probe::Pick<'tcx>,
+ seg: &hir::PathSegment<'_>,
+ parent_substs: SubstsRef<'tcx>,
+ ) -> SubstsRef<'tcx> {
+ // Determine the values for the generic parameters of the method.
+ // If they were not explicitly supplied, just construct fresh
+ // variables.
+ let generics = self.tcx.generics_of(pick.item.def_id);
+
+ let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call(
+ self.tcx,
+ self.span,
+ pick.item.def_id,
+ generics,
+ seg,
+ IsMethodCall::Yes,
+ );
+
+ // Create subst for early-bound lifetime parameters, combining
+ // parameters from the type and those from the method.
+ assert_eq!(generics.parent_count, parent_substs.len());
+
+ struct MethodSubstsCtxt<'a, 'tcx> {
+ cfcx: &'a ConfirmContext<'a, 'tcx>,
+ pick: &'a probe::Pick<'tcx>,
+ seg: &'a hir::PathSegment<'a>,
+ }
+ impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> {
+ fn args_for_def_id(
+ &mut self,
+ def_id: DefId,
+ ) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
+ if def_id == self.pick.item.def_id {
+ if let Some(data) = self.seg.args {
+ return (Some(data), false);
+ }
+ }
+ (None, false)
+ }
+
+ fn provided_kind(
+ &mut self,
+ param: &ty::GenericParamDef,
+ arg: &GenericArg<'_>,
+ ) -> subst::GenericArg<'tcx> {
+ match (&param.kind, arg) {
+ (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+ <dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param))
+ .into()
+ }
+ (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
+ self.cfcx.to_ty(ty).into()
+ }
+ (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
+ self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
+ }
+ (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => {
+ self.cfcx.ty_infer(Some(param), inf.span).into()
+ }
+ (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => {
+ let tcx = self.cfcx.tcx();
+ self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into()
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn inferred_kind(
+ &mut self,
+ _substs: Option<&[subst::GenericArg<'tcx>]>,
+ param: &ty::GenericParamDef,
+ _infer_args: bool,
+ ) -> subst::GenericArg<'tcx> {
+ self.cfcx.var_for_def(self.cfcx.span, param)
+ }
+ }
+ <dyn AstConv<'_>>::create_substs_for_generic_args(
+ self.tcx,
+ pick.item.def_id,
+ parent_substs,
+ false,
+ None,
+ &arg_count_correct,
+ &mut MethodSubstsCtxt { cfcx: self, pick, seg },
+ )
+ }
+
+ fn unify_receivers(
+ &mut self,
+ self_ty: Ty<'tcx>,
+ method_self_ty: Ty<'tcx>,
+ pick: &probe::Pick<'tcx>,
+ substs: SubstsRef<'tcx>,
+ ) {
+ debug!(
+ "unify_receivers: self_ty={:?} method_self_ty={:?} span={:?} pick={:?}",
+ self_ty, method_self_ty, self.span, pick
+ );
+ let cause = self.cause(
+ self.span,
+ ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext {
+ assoc_item: pick.item,
+ param_env: self.param_env,
+ substs,
+ })),
+ );
+ match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) {
+ Ok(InferOk { obligations, value: () }) => {
+ self.register_predicates(obligations);
+ }
+ Err(_) => {
+ span_bug!(
+ self.span,
+ "{} was a subtype of {} but now is not?",
+ self_ty,
+ method_self_ty
+ );
+ }
+ }
+ }
+
+ // NOTE: this returns the *unnormalized* predicates and method sig. Because of
+ // inference guessing, the predicates and method signature can't be normalized
+ // until we unify the `Self` type.
+ fn instantiate_method_sig(
+ &mut self,
+ pick: &probe::Pick<'tcx>,
+ all_substs: SubstsRef<'tcx>,
+ ) -> (ty::FnSig<'tcx>, ty::InstantiatedPredicates<'tcx>) {
+ debug!("instantiate_method_sig(pick={:?}, all_substs={:?})", pick, all_substs);
+
+ // Instantiate the bounds on the method with the
+ // type/early-bound-regions substitutions performed. There can
+ // be no late-bound regions appearing here.
+ let def_id = pick.item.def_id;
+ let method_predicates = self.tcx.predicates_of(def_id).instantiate(self.tcx, all_substs);
+
+ debug!("method_predicates after subst = {:?}", method_predicates);
+
+ let sig = self.tcx.bound_fn_sig(def_id);
+
+ let sig = sig.subst(self.tcx, all_substs);
+ debug!("type scheme substituted, sig={:?}", sig);
+
+ let sig = self.replace_bound_vars_with_fresh_vars(sig);
+ debug!("late-bound lifetimes from method instantiated, sig={:?}", sig);
+
+ (sig, method_predicates)
+ }
+
+ fn add_obligations(
+ &mut self,
+ fty: Ty<'tcx>,
+ all_substs: SubstsRef<'tcx>,
+ method_predicates: ty::InstantiatedPredicates<'tcx>,
+ def_id: DefId,
+ ) {
+ debug!(
+ "add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}",
+ fty, all_substs, method_predicates, def_id
+ );
+
+ // FIXME: could replace with the following, but we already calculated `method_predicates`,
+ // so we just call `predicates_for_generics` directly to avoid redoing work.
+ // `self.add_required_obligations(self.span, def_id, &all_substs);`
+ for obligation in traits::predicates_for_generics(
+ |idx, span| {
+ let code = if span.is_dummy() {
+ ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx)
+ } else {
+ ObligationCauseCode::ExprBindingObligation(
+ def_id,
+ span,
+ self.call_expr.hir_id,
+ idx,
+ )
+ };
+ traits::ObligationCause::new(self.span, self.body_id, code)
+ },
+ self.param_env,
+ method_predicates,
+ ) {
+ self.register_predicate(obligation);
+ }
+
+ // this is a projection from a trait reference, so we have to
+ // make sure that the trait reference inputs are well-formed.
+ self.add_wf_bounds(all_substs, self.call_expr);
+
+ // the function type must also be well-formed (this is not
+ // implied by the substs being well-formed because of inherent
+ // impls and late-bound regions - see issue #28609).
+ self.register_wf_obligation(fty.into(), self.span, traits::WellFormed(None));
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // MISCELLANY
+
+ fn predicates_require_illegal_sized_bound(
+ &self,
+ predicates: &ty::InstantiatedPredicates<'tcx>,
+ ) -> Option<Span> {
+ let sized_def_id = self.tcx.lang_items().sized_trait()?;
+
+ traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied())
+ // We don't care about regions here.
+ .filter_map(|obligation| match obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => {
+ let span = iter::zip(&predicates.predicates, &predicates.spans)
+ .find_map(
+ |(p, span)| {
+ if *p == obligation.predicate { Some(*span) } else { None }
+ },
+ )
+ .unwrap_or(rustc_span::DUMMY_SP);
+ Some((trait_pred, span))
+ }
+ _ => None,
+ })
+ .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() {
+ ty::Dynamic(..) => Some(span),
+ _ => None,
+ })
+ }
+
+ fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
+ // Disallow calls to the method `drop` defined in the `Drop` trait.
+ if let Some(trait_def_id) = pick.item.trait_container(self.tcx) {
+ callee::check_legal_trait_for_method_call(
+ self.tcx,
+ self.span,
+ Some(self.self_expr.span),
+ self.call_expr.span,
+ trait_def_id,
+ )
+ }
+ }
+
+ fn upcast(
+ &mut self,
+ source_trait_ref: ty::PolyTraitRef<'tcx>,
+ target_trait_def_id: DefId,
+ ) -> ty::PolyTraitRef<'tcx> {
+ let upcast_trait_refs =
+ traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id);
+
+ // must be exactly one trait ref or we'd get an ambig error etc
+ if upcast_trait_refs.len() != 1 {
+ span_bug!(
+ self.span,
+ "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`",
+ source_trait_ref,
+ target_trait_def_id,
+ upcast_trait_refs
+ );
+ }
+
+ upcast_trait_refs.into_iter().next().unwrap()
+ }
+
+ fn replace_bound_vars_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T
+ where
+ T: TypeFoldable<'tcx> + Copy,
+ {
+ self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value)
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
new file mode 100644
index 000000000..a1278edef
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -0,0 +1,625 @@
+//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information.
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
+
+mod confirm;
+mod prelude2021;
+pub mod probe;
+mod suggest;
+
+pub use self::suggest::SelfSource;
+pub use self::MethodError::*;
+
+use crate::{Expectation, FnCtxt};
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Namespace};
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{self, InferOk};
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
+use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable};
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+use rustc_trait_selection::traits;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+
+use self::probe::{IsSuggestion, ProbeScope};
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ probe::provide(providers);
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct MethodCallee<'tcx> {
+ /// Impl method ID, for inherent methods, or trait method ID, otherwise.
+ pub def_id: DefId,
+ pub substs: SubstsRef<'tcx>,
+
+ /// Instantiated method signature, i.e., it has been
+ /// substituted, normalized, and has had late-bound
+ /// lifetimes replaced with inference variables.
+ pub sig: ty::FnSig<'tcx>,
+}
+
+#[derive(Debug)]
+pub enum MethodError<'tcx> {
+ // Did not find an applicable method, but we did find various near-misses that may work.
+ NoMatch(NoMatchData<'tcx>),
+
+ // Multiple methods might apply.
+ Ambiguity(Vec<CandidateSource>),
+
+ // Found an applicable method, but it is not visible. The third argument contains a list of
+ // not-in-scope traits which may work.
+ PrivateMatch(DefKind, DefId, Vec<DefId>),
+
+ // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
+ // forgotten to import a trait.
+ IllegalSizedBound(Vec<DefId>, bool, Span),
+
+ // Found a match, but the return type is wrong
+ BadReturnType,
+}
+
+// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
+// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
+#[derive(Debug)]
+pub struct NoMatchData<'tcx> {
+ pub static_candidates: Vec<CandidateSource>,
+ pub unsatisfied_predicates:
+ Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
+ pub out_of_scope_traits: Vec<DefId>,
+ pub lev_candidate: Option<ty::AssocItem>,
+ pub mode: probe::Mode,
+}
+
+// A pared down enum describing just the places from which a method
+// candidate can arise. Used for error reporting only.
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum CandidateSource {
+ Impl(DefId),
+ Trait(DefId /* trait id */),
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ /// Determines whether the type `self_ty` supports a method name `method_name` or not.
+ #[instrument(level = "debug", skip(self))]
+ pub fn method_exists(
+ &self,
+ method_name: Ident,
+ self_ty: Ty<'tcx>,
+ call_expr_id: hir::HirId,
+ allow_private: bool,
+ ) -> bool {
+ let mode = probe::Mode::MethodCall;
+ match self.probe_for_name(
+ method_name.span,
+ mode,
+ method_name,
+ IsSuggestion(false),
+ self_ty,
+ call_expr_id,
+ ProbeScope::TraitsInScope,
+ ) {
+ Ok(..) => true,
+ Err(NoMatch(..)) => false,
+ Err(Ambiguity(..)) => true,
+ Err(PrivateMatch(..)) => allow_private,
+ Err(IllegalSizedBound(..)) => true,
+ Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"),
+ }
+ }
+
+ /// Adds a suggestion to call the given method to the provided diagnostic.
+ #[instrument(level = "debug", skip(self, err, call_expr))]
+ pub(crate) fn suggest_method_call(
+ &self,
+ err: &mut Diagnostic,
+ msg: &str,
+ method_name: Ident,
+ self_ty: Ty<'tcx>,
+ call_expr: &hir::Expr<'_>,
+ span: Option<Span>,
+ ) {
+ let params = self
+ .probe_for_name(
+ method_name.span,
+ probe::Mode::MethodCall,
+ method_name,
+ IsSuggestion(false),
+ self_ty,
+ call_expr.hir_id,
+ ProbeScope::TraitsInScope,
+ )
+ .map(|pick| {
+ let sig = self.tcx.fn_sig(pick.item.def_id);
+ sig.inputs().skip_binder().len().saturating_sub(1)
+ })
+ .unwrap_or(0);
+
+ // Account for `foo.bar<T>`;
+ let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi();
+ let (suggestion, applicability) = (
+ format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")),
+ if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect },
+ );
+
+ err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability);
+ }
+
+ /// Performs method lookup. If lookup is successful, it will return the callee
+ /// and store an appropriate adjustment for the self-expr. In some cases it may
+ /// report an error (e.g., invoking the `drop` method).
+ ///
+ /// # Arguments
+ ///
+ /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`:
+ ///
+ /// * `self`: the surrounding `FnCtxt` (!)
+ /// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
+ /// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`)
+ /// * `span`: the span for the method call
+ /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`)
+ /// * `self_expr`: the self expression (`foo`)
+ /// * `args`: the expressions of the arguments (`a, b + 1, ...`)
+ #[instrument(level = "debug", skip(self))]
+ pub fn lookup_method(
+ &self,
+ self_ty: Ty<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ span: Span,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ args: &'tcx [hir::Expr<'tcx>],
+ ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
+ let pick =
+ self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
+
+ self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
+
+ for import_id in &pick.import_ids {
+ debug!("used_trait_import: {:?}", import_id);
+ Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
+ .unwrap()
+ .insert(*import_id);
+ }
+
+ self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None);
+
+ let result =
+ self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment);
+ debug!("result = {:?}", result);
+
+ if let Some(span) = result.illegal_sized_bound {
+ let mut needs_mut = false;
+ if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
+ let trait_type = self
+ .tcx
+ .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
+ // We probe again to see if there might be a borrow mutability discrepancy.
+ match self.lookup_probe(
+ span,
+ segment.ident,
+ trait_type,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ ) {
+ Ok(ref new_pick) if *new_pick != pick => {
+ needs_mut = true;
+ }
+ _ => {}
+ }
+ }
+
+ // We probe again, taking all traits into account (not only those in scope).
+ let mut candidates = match self.lookup_probe(
+ span,
+ segment.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ ) {
+ // If we find a different result the caller probably forgot to import a trait.
+ Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)],
+ Err(Ambiguity(ref sources)) => sources
+ .iter()
+ .filter_map(|source| {
+ match *source {
+ // Note: this cannot come from an inherent impl,
+ // because the first probing succeeded.
+ CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
+ CandidateSource::Trait(_) => None,
+ }
+ })
+ .collect(),
+ _ => Vec::new(),
+ };
+ candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
+
+ return Err(IllegalSizedBound(candidates, needs_mut, span));
+ }
+
+ Ok(result.callee)
+ }
+
+ #[instrument(level = "debug", skip(self, call_expr))]
+ pub fn lookup_probe(
+ &self,
+ span: Span,
+ method_name: Ident,
+ self_ty: Ty<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ scope: ProbeScope,
+ ) -> probe::PickResult<'tcx> {
+ let mode = probe::Mode::MethodCall;
+ let self_ty = self.resolve_vars_if_possible(self_ty);
+ self.probe_for_name(
+ span,
+ mode,
+ method_name,
+ IsSuggestion(false),
+ self_ty,
+ call_expr.hir_id,
+ scope,
+ )
+ }
+
+ pub(super) fn obligation_for_method(
+ &self,
+ span: Span,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ opt_input_types: Option<&[Ty<'tcx>]>,
+ ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
+ {
+ // Construct a trait-reference `self_ty : Trait<input_tys>`
+ let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
+ match param.kind {
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
+ GenericParamDefKind::Type { .. } => {
+ if param.index == 0 {
+ return self_ty.into();
+ } else if let Some(input_types) = opt_input_types {
+ return input_types[param.index as usize - 1].into();
+ }
+ }
+ }
+ self.var_for_def(span, param)
+ });
+
+ let trait_ref = ty::TraitRef::new(trait_def_id, substs);
+
+ // Construct an obligation
+ let poly_trait_ref = ty::Binder::dummy(trait_ref);
+ (
+ traits::Obligation::misc(
+ span,
+ self.body_id,
+ self.param_env,
+ poly_trait_ref.without_const().to_predicate(self.tcx),
+ ),
+ substs,
+ )
+ }
+
+ pub(super) fn obligation_for_op_method(
+ &self,
+ span: Span,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ opt_input_type: Option<Ty<'tcx>>,
+ opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+ expected: Expectation<'tcx>,
+ ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>)
+ {
+ // Construct a trait-reference `self_ty : Trait<input_tys>`
+ let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
+ match param.kind {
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {}
+ GenericParamDefKind::Type { .. } => {
+ if param.index == 0 {
+ return self_ty.into();
+ } else if let Some(input_type) = opt_input_type {
+ return input_type.into();
+ }
+ }
+ }
+ self.var_for_def(span, param)
+ });
+
+ let trait_ref = ty::TraitRef::new(trait_def_id, substs);
+
+ // Construct an obligation
+ let poly_trait_ref = ty::Binder::dummy(trait_ref);
+ let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty));
+
+ (
+ traits::Obligation::new(
+ traits::ObligationCause::new(
+ span,
+ self.body_id,
+ traits::BinOp {
+ rhs_span: opt_input_expr.map(|expr| expr.span),
+ is_lit: opt_input_expr
+ .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+ output_ty,
+ },
+ ),
+ self.param_env,
+ poly_trait_ref.without_const().to_predicate(self.tcx),
+ ),
+ substs,
+ )
+ }
+
+ /// `lookup_method_in_trait` is used for overloaded operators.
+ /// It does a very narrow slice of what the normal probe/confirm path does.
+ /// In particular, it doesn't really do any probing: it simply constructs
+ /// an obligation for a particular trait with the given self type and checks
+ /// whether that trait is implemented.
+ #[instrument(level = "debug", skip(self, span))]
+ pub(super) fn lookup_method_in_trait(
+ &self,
+ span: Span,
+ m_name: Ident,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ opt_input_types: Option<&[Ty<'tcx>]>,
+ ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+ let (obligation, substs) =
+ self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types);
+ self.construct_obligation_for_trait(
+ span,
+ m_name,
+ trait_def_id,
+ obligation,
+ substs,
+ None,
+ false,
+ )
+ }
+
+ pub(super) fn lookup_op_method_in_trait(
+ &self,
+ span: Span,
+ m_name: Ident,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ opt_input_type: Option<Ty<'tcx>>,
+ opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+ expected: Expectation<'tcx>,
+ ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+ let (obligation, substs) = self.obligation_for_op_method(
+ span,
+ trait_def_id,
+ self_ty,
+ opt_input_type,
+ opt_input_expr,
+ expected,
+ );
+ self.construct_obligation_for_trait(
+ span,
+ m_name,
+ trait_def_id,
+ obligation,
+ substs,
+ opt_input_expr,
+ true,
+ )
+ }
+
+ // FIXME(#18741): it seems likely that we can consolidate some of this
+ // code with the other method-lookup code. In particular, the second half
+ // of this method is basically the same as confirmation.
+ fn construct_obligation_for_trait(
+ &self,
+ span: Span,
+ m_name: Ident,
+ trait_def_id: DefId,
+ obligation: traits::PredicateObligation<'tcx>,
+ substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>,
+ opt_input_expr: Option<&'tcx hir::Expr<'tcx>>,
+ is_op: bool,
+ ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
+ debug!(?obligation);
+
+ // Now we want to know if this can be matched
+ if !self.predicate_may_hold(&obligation) {
+ debug!("--> Cannot match obligation");
+ // Cannot be matched, no such method resolution is possible.
+ return None;
+ }
+
+ // Trait must have a method named `m_name` and it should not have
+ // type parameters or early-bound regions.
+ let tcx = self.tcx;
+ let Some(method_item) = self.associated_value(trait_def_id, m_name) else {
+ tcx.sess.delay_span_bug(
+ span,
+ "operator trait does not have corresponding operator method",
+ );
+ return None;
+ };
+ let def_id = method_item.def_id;
+ let generics = tcx.generics_of(def_id);
+ assert_eq!(generics.params.len(), 0);
+
+ debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
+ let mut obligations = vec![];
+
+ // Instantiate late-bound regions and substitute the trait
+ // parameters into the method type to get the actual method type.
+ //
+ // N.B., instantiate late-bound regions first so that
+ // `instantiate_type_scheme` can normalize associated types that
+ // may reference those regions.
+ let fn_sig = tcx.bound_fn_sig(def_id);
+ let fn_sig = fn_sig.subst(self.tcx, substs);
+ let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
+
+ let InferOk { value, obligations: o } = if is_op {
+ self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr)
+ } else {
+ self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
+ };
+ let fn_sig = {
+ obligations.extend(o);
+ value
+ };
+
+ // Register obligations for the parameters. This will include the
+ // `Self` parameter, which in turn has a bound of the main trait,
+ // so this also effectively registers `obligation` as well. (We
+ // used to register `obligation` explicitly, but that resulted in
+ // double error messages being reported.)
+ //
+ // Note that as the method comes from a trait, it should not have
+ // any late-bound regions appearing in its bounds.
+ let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
+
+ let InferOk { value, obligations: o } = if is_op {
+ self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr)
+ } else {
+ self.normalize_associated_types_in_as_infer_ok(span, bounds)
+ };
+ let bounds = {
+ obligations.extend(o);
+ value
+ };
+
+ assert!(!bounds.has_escaping_bound_vars());
+
+ let cause = if is_op {
+ ObligationCause::new(
+ span,
+ self.body_id,
+ traits::BinOp {
+ rhs_span: opt_input_expr.map(|expr| expr.span),
+ is_lit: opt_input_expr
+ .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))),
+ output_ty: None,
+ },
+ )
+ } else {
+ traits::ObligationCause::misc(span, self.body_id)
+ };
+ let predicates_cause = cause.clone();
+ obligations.extend(traits::predicates_for_generics(
+ move |_, _| predicates_cause.clone(),
+ self.param_env,
+ bounds,
+ ));
+
+ // Also add an obligation for the method type being well-formed.
+ let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
+ debug!(
+ "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
+ method_ty, obligation
+ );
+ obligations.push(traits::Obligation::new(
+ cause,
+ self.param_env,
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
+ ));
+
+ let callee = MethodCallee { def_id, substs, sig: fn_sig };
+
+ debug!("callee = {:?}", callee);
+
+ Some(InferOk { obligations, value: callee })
+ }
+
+ /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If
+ /// lookup is successful, it will return the type of definition and the [`DefId`] of the found
+ /// function definition.
+ ///
+ /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
+ ///
+ /// # Arguments
+ ///
+ /// Given a function call like `Foo::bar::<T1,...Tn>(...)`:
+ ///
+ /// * `self`: the surrounding `FnCtxt` (!)
+ /// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`)
+ /// * `method_name`: the identifier of the function within the container type (`bar`)
+ /// * `self_ty`: the type to search within (`Foo`)
+ /// * `self_ty_span` the span for the type being searched within (span of `Foo`)
+ /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call
+ #[instrument(level = "debug", skip(self), ret)]
+ pub fn resolve_fully_qualified_call(
+ &self,
+ span: Span,
+ method_name: Ident,
+ self_ty: Ty<'tcx>,
+ self_ty_span: Span,
+ expr_id: hir::HirId,
+ ) -> Result<(DefKind, DefId), MethodError<'tcx>> {
+ let tcx = self.tcx;
+
+ // Check if we have an enum variant.
+ if let ty::Adt(adt_def, _) = self_ty.kind() {
+ if adt_def.is_enum() {
+ let variant_def = adt_def
+ .variants()
+ .iter()
+ .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did()));
+ if let Some(variant_def) = variant_def {
+ // Braced variants generate unusable names in value namespace (reserved for
+ // possible future use), so variants resolved as associated items may refer to
+ // them as well. It's ok to use the variant's id as a ctor id since an
+ // error will be reported on any use of such resolution anyway.
+ let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id);
+ tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span));
+ return Ok((
+ DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind),
+ ctor_def_id,
+ ));
+ }
+ }
+ }
+
+ let pick = self.probe_for_name(
+ span,
+ probe::Mode::Path,
+ method_name,
+ IsSuggestion(false),
+ self_ty,
+ expr_id,
+ ProbeScope::TraitsInScope,
+ )?;
+
+ self.lint_fully_qualified_call_from_2018(
+ span,
+ method_name,
+ self_ty,
+ self_ty_span,
+ expr_id,
+ &pick,
+ );
+
+ debug!(?pick);
+ {
+ let mut typeck_results = self.typeck_results.borrow_mut();
+ let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
+ for import_id in pick.import_ids {
+ debug!(used_trait_import=?import_id);
+ used_trait_imports.insert(import_id);
+ }
+ }
+
+ let def_kind = pick.item.kind.as_def_kind();
+ tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
+ Ok((def_kind, pick.item.def_id))
+ }
+
+ /// Finds item with name `item_name` defined in impl/trait `def_id`
+ /// and return it, or `None`, if no such item was defined there.
+ pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> {
+ self.tcx
+ .associated_items(def_id)
+ .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id)
+ .copied()
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
new file mode 100644
index 000000000..3c98a2aa3
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -0,0 +1,415 @@
+use crate::{
+ method::probe::{self, Pick},
+ FnCtxt,
+};
+use hir::def_id::DefId;
+use hir::HirId;
+use hir::ItemKind;
+use rustc_ast::Mutability;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_middle::ty::subst::InternalSubsts;
+use rustc_middle::ty::{Adt, Array, Ref, Ty};
+use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
+use rustc_span::symbol::kw::{Empty, Underscore};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+use rustc_trait_selection::infer::InferCtxtExt;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(super) fn lint_dot_call_from_2018(
+ &self,
+ self_ty: Ty<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ span: Span,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ pick: &Pick<'tcx>,
+ args: &'tcx [hir::Expr<'tcx>],
+ ) {
+ debug!(
+ "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
+ segment.ident, self_ty, call_expr, self_expr
+ );
+
+ // Rust 2021 and later is already using the new prelude
+ if span.rust_2021() {
+ return;
+ }
+
+ let prelude_or_array_lint = match segment.ident.name {
+ // `try_into` was added to the prelude in Rust 2021.
+ sym::try_into => RUST_2021_PRELUDE_COLLISIONS,
+ // `into_iter` wasn't added to the prelude,
+ // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter
+ // before Rust 2021, which results in the same problem.
+ // It is only a problem for arrays.
+ sym::into_iter if let Array(..) = self_ty.kind() => {
+ // In this case, it wasn't really a prelude addition that was the problem.
+ // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021.
+ rustc_lint::ARRAY_INTO_ITER
+ }
+ _ => return,
+ };
+
+ // No need to lint if method came from std/core, as that will now be in the prelude
+ if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
+ return;
+ }
+
+ if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) {
+ // avoid repeatedly adding unneeded `&*`s
+ if pick.autoderefs == 1
+ && matches!(
+ pick.autoref_or_ptr_adjustment,
+ Some(probe::AutorefOrPtrAdjustment::Autoref { .. })
+ )
+ && matches!(self_ty.kind(), Ref(..))
+ {
+ return;
+ }
+
+ // if it's an inherent `self` method (not `&self` or `&mut self`), it will take
+ // precedence over the `TryInto` impl, and thus won't break in 2021 edition
+ if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() {
+ return;
+ }
+
+ // Inherent impls only require not relying on autoref and autoderef in order to
+ // ensure that the trait implementation won't be used
+ self.tcx.struct_span_lint_hir(
+ prelude_or_array_lint,
+ self_expr.hir_id,
+ self_expr.span,
+ format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
+ |lint| {
+ let sp = self_expr.span;
+
+ let derefs = "*".repeat(pick.autoderefs);
+
+ let autoref = match pick.autoref_or_ptr_adjustment {
+ Some(probe::AutorefOrPtrAdjustment::Autoref {
+ mutbl: Mutability::Mut,
+ ..
+ }) => "&mut ",
+ Some(probe::AutorefOrPtrAdjustment::Autoref {
+ mutbl: Mutability::Not,
+ ..
+ }) => "&",
+ Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
+ };
+ if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
+ {
+ let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}{} as *const _", derefs, self_expr)
+ } else {
+ format!("{}{}{}", autoref, derefs, self_expr)
+ };
+
+ lint.span_suggestion(
+ sp,
+ "disambiguate the method call",
+ format!("({})", self_adjusted),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}(...) as *const _", derefs)
+ } else {
+ format!("{}{}...", autoref, derefs)
+ };
+ lint.span_help(
+ sp,
+ &format!("disambiguate the method call with `({})`", self_adjusted,),
+ );
+ }
+
+ lint
+ },
+ );
+ } else {
+ // trait implementations require full disambiguation to not clash with the new prelude
+ // additions (i.e. convert from dot-call to fully-qualified call)
+ self.tcx.struct_span_lint_hir(
+ prelude_or_array_lint,
+ call_expr.hir_id,
+ call_expr.span,
+ format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name),
+ |lint| {
+ let sp = call_expr.span;
+ let trait_name = self.trait_path_or_bare_name(
+ span,
+ call_expr.hir_id,
+ pick.item.container_id(self.tcx),
+ );
+
+ let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp);
+ if precise {
+ let args = args
+ .iter()
+ .map(|arg| {
+ let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
+ format!(
+ ", {}",
+ self.sess().source_map().span_to_snippet(span).unwrap()
+ )
+ })
+ .collect::<String>();
+
+ lint.span_suggestion(
+ sp,
+ "disambiguate the associated function",
+ format!(
+ "{}::{}{}({}{})",
+ trait_name,
+ segment.ident.name,
+ if let Some(args) = segment.args.as_ref().and_then(|args| self
+ .sess()
+ .source_map()
+ .span_to_snippet(args.span_ext)
+ .ok())
+ {
+ // Keep turbofish.
+ format!("::{}", args)
+ } else {
+ String::new()
+ },
+ self_adjusted,
+ args,
+ ),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ lint.span_help(
+ sp,
+ &format!(
+ "disambiguate the associated function with `{}::{}(...)`",
+ trait_name, segment.ident,
+ ),
+ );
+ }
+
+ lint
+ },
+ );
+ }
+ }
+
+ pub(super) fn lint_fully_qualified_call_from_2018(
+ &self,
+ span: Span,
+ method_name: Ident,
+ self_ty: Ty<'tcx>,
+ self_ty_span: Span,
+ expr_id: hir::HirId,
+ pick: &Pick<'tcx>,
+ ) {
+ // Rust 2021 and later is already using the new prelude
+ if span.rust_2021() {
+ return;
+ }
+
+ // These are the fully qualified methods added to prelude in Rust 2021
+ if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) {
+ return;
+ }
+
+ // No need to lint if method came from std/core, as that will now be in the prelude
+ if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
+ return;
+ }
+
+ // For from_iter, check if the type actually implements FromIterator.
+ // If we know it does not, we don't need to warn.
+ if method_name.name == sym::from_iter {
+ if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) {
+ if !self
+ .infcx
+ .type_implements_trait(
+ trait_def_id,
+ self_ty,
+ InternalSubsts::empty(),
+ self.param_env,
+ )
+ .may_apply()
+ {
+ return;
+ }
+ }
+ }
+
+ // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`,
+ // since such methods take precedence over trait methods.
+ if matches!(pick.kind, probe::PickKind::InherentImplPick) {
+ return;
+ }
+
+ self.tcx.struct_span_lint_hir(
+ RUST_2021_PRELUDE_COLLISIONS,
+ expr_id,
+ span,
+ format!(
+ "trait-associated function `{}` will become ambiguous in Rust 2021",
+ method_name.name
+ ),
+ |lint| {
+ // "type" refers to either a type or, more likely, a trait from which
+ // the associated function or method is from.
+ let container_id = pick.item.container_id(self.tcx);
+ let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id);
+ let trait_generics = self.tcx.generics_of(container_id);
+
+ let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize
+ {
+ trait_path
+ } else {
+ let counts = trait_generics.own_counts();
+ format!(
+ "{}<{}>",
+ trait_path,
+ std::iter::repeat("'_")
+ .take(counts.lifetimes)
+ .chain(std::iter::repeat("_").take(
+ counts.types + counts.consts - trait_generics.has_self as usize
+ ))
+ .collect::<Vec<_>>()
+ .join(", ")
+ )
+ };
+
+ let mut self_ty_name = self_ty_span
+ .find_ancestor_inside(span)
+ .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
+ .unwrap_or_else(|| self_ty.to_string());
+
+ // Get the number of generics the self type has (if an Adt) unless we can determine that
+ // the user has written the self type with generics already which we (naively) do by looking
+ // for a "<" in `self_ty_name`.
+ if !self_ty_name.contains('<') {
+ if let Adt(def, _) = self_ty.kind() {
+ let generics = self.tcx.generics_of(def.did());
+ if !generics.params.is_empty() {
+ let counts = generics.own_counts();
+ self_ty_name += &format!(
+ "<{}>",
+ std::iter::repeat("'_")
+ .take(counts.lifetimes)
+ .chain(
+ std::iter::repeat("_").take(counts.types + counts.consts)
+ )
+ .collect::<Vec<_>>()
+ .join(", ")
+ );
+ }
+ }
+ }
+ lint.span_suggestion(
+ span,
+ "disambiguate the associated function",
+ format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,),
+ Applicability::MachineApplicable,
+ );
+
+ lint
+ },
+ );
+ }
+
+ fn trait_path_or_bare_name(
+ &self,
+ span: Span,
+ expr_hir_id: HirId,
+ trait_def_id: DefId,
+ ) -> String {
+ self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| {
+ let key = self.tcx.def_key(trait_def_id);
+ format!("{}", key.disambiguated_data.data)
+ })
+ }
+
+ fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
+ let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
+ let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
+ if applicable_trait.import_ids.is_empty() {
+ // The trait was declared within the module, we only need to use its name.
+ return None;
+ }
+
+ let import_items: Vec<_> = applicable_trait
+ .import_ids
+ .iter()
+ .map(|&import_id| self.tcx.hir().expect_item(import_id))
+ .collect();
+
+ // Find an identifier with which this trait was imported (note that `_` doesn't count).
+ let any_id = import_items
+ .iter()
+ .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
+ .next();
+ if let Some(any_id) = any_id {
+ if any_id.name == Empty {
+ // Glob import, so just use its name.
+ return None;
+ } else {
+ return Some(format!("{}", any_id));
+ }
+ }
+
+ // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick,
+ // so just take the first one.
+ match import_items[0].kind {
+ ItemKind::Use(path, _) => Some(
+ path.segments
+ .iter()
+ .map(|segment| segment.ident.to_string())
+ .collect::<Vec<_>>()
+ .join("::"),
+ ),
+ _ => {
+ span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
+ }
+ }
+ }
+
+ /// Creates a string version of the `expr` that includes explicit adjustments.
+ /// Returns the string and also a bool indicating whether this is a *precise*
+ /// suggestion.
+ fn adjust_expr(
+ &self,
+ pick: &Pick<'tcx>,
+ expr: &hir::Expr<'tcx>,
+ outer: Span,
+ ) -> (String, bool) {
+ let derefs = "*".repeat(pick.autoderefs);
+
+ let autoref = match pick.autoref_or_ptr_adjustment {
+ Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ",
+ Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&",
+ Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
+ };
+
+ let (expr_text, precise) = if let Some(expr_text) = expr
+ .span
+ .find_ancestor_inside(outer)
+ .and_then(|span| self.sess().source_map().span_to_snippet(span).ok())
+ {
+ (expr_text, true)
+ } else {
+ ("(..)".to_string(), false)
+ };
+
+ let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) =
+ pick.autoref_or_ptr_adjustment
+ {
+ format!("{}{} as *const _", derefs, expr_text)
+ } else {
+ format!("{}{}{}", autoref, derefs, expr_text)
+ };
+
+ (adjusted_text, precise)
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
new file mode 100644
index 000000000..28aa2302f
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -0,0 +1,1926 @@
+use super::suggest;
+use super::CandidateSource;
+use super::MethodError;
+use super::NoMatchData;
+
+use crate::errors::MethodCallOnUnknownType;
+use crate::FnCtxt;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def::Namespace;
+use rustc_infer::infer::canonical::OriginalQueryValues;
+use rustc_infer::infer::canonical::{Canonical, QueryResponse};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::middle::stability;
+use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
+use rustc_middle::ty::GenericParamDefKind;
+use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_session::lint;
+use rustc_span::def_id::DefId;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::lev_distance::{
+ find_best_match_for_name_with_substrings, lev_distance_with_substrings,
+};
+use rustc_span::symbol::sym;
+use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
+use rustc_trait_selection::autoderef::{self, Autoderef};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
+use rustc_trait_selection::traits::query::method_autoderef::{
+ CandidateStep, MethodAutoderefStepsResult,
+};
+use rustc_trait_selection::traits::query::CanonicalTyGoal;
+use rustc_trait_selection::traits::{self, ObligationCause};
+use std::cmp::max;
+use std::iter;
+use std::mem;
+use std::ops::Deref;
+
+use smallvec::{smallvec, SmallVec};
+
+use self::CandidateKind::*;
+pub use self::PickKind::*;
+
+/// Boolean flag used to indicate if this search is for a suggestion
+/// or not. If true, we can allow ambiguity and so forth.
+#[derive(Clone, Copy, Debug)]
+pub struct IsSuggestion(pub bool);
+
+struct ProbeContext<'a, 'tcx> {
+ fcx: &'a FnCtxt<'a, 'tcx>,
+ span: Span,
+ mode: Mode,
+ method_name: Option<Ident>,
+ return_type: Option<Ty<'tcx>>,
+
+ /// This is the OriginalQueryValues for the steps queries
+ /// that are answered in steps.
+ orig_steps_var_values: OriginalQueryValues<'tcx>,
+ steps: &'tcx [CandidateStep<'tcx>],
+
+ inherent_candidates: Vec<Candidate<'tcx>>,
+ extension_candidates: Vec<Candidate<'tcx>>,
+ impl_dups: FxHashSet<DefId>,
+
+ /// Collects near misses when the candidate functions are missing a `self` keyword and is only
+ /// used for error reporting
+ static_candidates: Vec<CandidateSource>,
+
+ /// When probing for names, include names that are close to the
+ /// requested name (by Levensthein distance)
+ allow_similar_names: bool,
+
+ /// Some(candidate) if there is a private candidate
+ private_candidate: Option<(DefKind, DefId)>,
+
+ /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
+ /// for error reporting
+ unsatisfied_predicates:
+ Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
+
+ is_suggestion: IsSuggestion,
+
+ scope_expr_id: hir::HirId,
+}
+
+impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> {
+ type Target = FnCtxt<'a, 'tcx>;
+ fn deref(&self) -> &Self::Target {
+ self.fcx
+ }
+}
+
+#[derive(Debug, Clone)]
+struct Candidate<'tcx> {
+ // Candidates are (I'm not quite sure, but they are mostly) basically
+ // some metadata on top of a `ty::AssocItem` (without substs).
+ //
+ // However, method probing wants to be able to evaluate the predicates
+ // for a function with the substs applied - for example, if a function
+ // has `where Self: Sized`, we don't want to consider it unless `Self`
+ // is actually `Sized`, and similarly, return-type suggestions want
+ // to consider the "actual" return type.
+ //
+ // The way this is handled is through `xform_self_ty`. It contains
+ // the receiver type of this candidate, but `xform_self_ty`,
+ // `xform_ret_ty` and `kind` (which contains the predicates) have the
+ // generic parameters of this candidate substituted with the *same set*
+ // of inference variables, which acts as some weird sort of "query".
+ //
+ // When we check out a candidate, we require `xform_self_ty` to be
+ // a subtype of the passed-in self-type, and this equates the type
+ // variables in the rest of the fields.
+ //
+ // For example, if we have this candidate:
+ // ```
+ // trait Foo {
+ // fn foo(&self) where Self: Sized;
+ // }
+ // ```
+ //
+ // Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain
+ // the predicate `?X: Sized`, so if we are evaluating `Foo` for a
+ // the receiver `&T`, we'll do the subtyping which will make `?X`
+ // get the right value, then when we evaluate the predicate we'll check
+ // if `T: Sized`.
+ xform_self_ty: Ty<'tcx>,
+ xform_ret_ty: Option<Ty<'tcx>>,
+ item: ty::AssocItem,
+ kind: CandidateKind<'tcx>,
+ import_ids: SmallVec<[LocalDefId; 1]>,
+}
+
+#[derive(Debug, Clone)]
+enum CandidateKind<'tcx> {
+ InherentImplCandidate(
+ SubstsRef<'tcx>,
+ // Normalize obligations
+ Vec<traits::PredicateObligation<'tcx>>,
+ ),
+ ObjectCandidate,
+ TraitCandidate(ty::TraitRef<'tcx>),
+ WhereClauseCandidate(
+ // Trait
+ ty::PolyTraitRef<'tcx>,
+ ),
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+enum ProbeResult {
+ NoMatch,
+ BadReturnType,
+ Match,
+}
+
+/// When adjusting a receiver we often want to do one of
+///
+/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`)
+/// - If the receiver has type `*mut T`, convert it to `*const T`
+///
+/// This type tells us which one to do.
+///
+/// Note that in principle we could do both at the same time. For example, when the receiver has
+/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut
+/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do
+/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with
+/// `mut`), or it has type `*mut T` and we convert it to `*const T`.
+#[derive(Debug, PartialEq, Copy, Clone)]
+pub enum AutorefOrPtrAdjustment {
+ /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it.
+ /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing.
+ Autoref {
+ mutbl: hir::Mutability,
+
+ /// Indicates that the source expression should be "unsized" to a target type.
+ /// This is special-cased for just arrays unsizing to slices.
+ unsize: bool,
+ },
+ /// Receiver has type `*mut T`, convert to `*const T`
+ ToConstPtr,
+}
+
+impl AutorefOrPtrAdjustment {
+ fn get_unsize(&self) -> bool {
+ match self {
+ AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize,
+ AutorefOrPtrAdjustment::ToConstPtr => false,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct Pick<'tcx> {
+ pub item: ty::AssocItem,
+ pub kind: PickKind<'tcx>,
+ pub import_ids: SmallVec<[LocalDefId; 1]>,
+
+ /// Indicates that the source expression should be autoderef'd N times
+ /// ```ignore (not-rust)
+ /// A = expr | *expr | **expr | ...
+ /// ```
+ pub autoderefs: usize,
+
+ /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is
+ /// `*mut T`, convert it to `*const T`.
+ pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>,
+ pub self_ty: Ty<'tcx>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum PickKind<'tcx> {
+ InherentImplPick,
+ ObjectPick,
+ TraitPick,
+ WhereClausePick(
+ // Trait
+ ty::PolyTraitRef<'tcx>,
+ ),
+}
+
+pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>;
+
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+pub enum Mode {
+ // An expression of the form `receiver.method_name(...)`.
+ // Autoderefs are performed on `receiver`, lookup is done based on the
+ // `self` argument of the method, and static methods aren't considered.
+ MethodCall,
+ // An expression of the form `Type::item` or `<T>::item`.
+ // No autoderefs are performed, lookup is done based on the type each
+ // implementation is for, and static methods are included.
+ Path,
+}
+
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+pub enum ProbeScope {
+ // Assemble candidates coming only from traits in scope.
+ TraitsInScope,
+
+ // Assemble candidates coming from all traits.
+ AllTraits,
+}
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ /// This is used to offer suggestions to users. It returns methods
+ /// that could have been called which have the desired return
+ /// type. Some effort is made to rule out methods that, if called,
+ /// would result in an error (basically, the same criteria we
+ /// would use to decide if a method is a plausible fit for
+ /// ambiguity purposes).
+ #[instrument(level = "debug", skip(self, candidate_filter))]
+ pub fn probe_for_return_type(
+ &self,
+ span: Span,
+ mode: Mode,
+ return_type: Ty<'tcx>,
+ self_ty: Ty<'tcx>,
+ scope_expr_id: hir::HirId,
+ candidate_filter: impl Fn(&ty::AssocItem) -> bool,
+ ) -> Vec<ty::AssocItem> {
+ let method_names = self
+ .probe_op(
+ span,
+ mode,
+ None,
+ Some(return_type),
+ IsSuggestion(true),
+ self_ty,
+ scope_expr_id,
+ ProbeScope::AllTraits,
+ |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)),
+ )
+ .unwrap_or_default();
+ method_names
+ .iter()
+ .flat_map(|&method_name| {
+ self.probe_op(
+ span,
+ mode,
+ Some(method_name),
+ Some(return_type),
+ IsSuggestion(true),
+ self_ty,
+ scope_expr_id,
+ ProbeScope::AllTraits,
+ |probe_cx| probe_cx.pick(),
+ )
+ .ok()
+ .map(|pick| pick.item)
+ })
+ .collect()
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ pub fn probe_for_name(
+ &self,
+ span: Span,
+ mode: Mode,
+ item_name: Ident,
+ is_suggestion: IsSuggestion,
+ self_ty: Ty<'tcx>,
+ scope_expr_id: hir::HirId,
+ scope: ProbeScope,
+ ) -> PickResult<'tcx> {
+ self.probe_op(
+ span,
+ mode,
+ Some(item_name),
+ None,
+ is_suggestion,
+ self_ty,
+ scope_expr_id,
+ scope,
+ |probe_cx| probe_cx.pick(),
+ )
+ }
+
+ fn probe_op<OP, R>(
+ &'a self,
+ span: Span,
+ mode: Mode,
+ method_name: Option<Ident>,
+ return_type: Option<Ty<'tcx>>,
+ is_suggestion: IsSuggestion,
+ self_ty: Ty<'tcx>,
+ scope_expr_id: hir::HirId,
+ scope: ProbeScope,
+ op: OP,
+ ) -> Result<R, MethodError<'tcx>>
+ where
+ OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>,
+ {
+ let mut orig_values = OriginalQueryValues::default();
+ let param_env_and_self_ty = self.canonicalize_query(
+ ParamEnvAnd { param_env: self.param_env, value: self_ty },
+ &mut orig_values,
+ );
+
+ let steps = if mode == Mode::MethodCall {
+ self.tcx.method_autoderef_steps(param_env_and_self_ty)
+ } else {
+ self.probe(|_| {
+ // Mode::Path - the deref steps is "trivial". This turns
+ // our CanonicalQuery into a "trivial" QueryResponse. This
+ // is a bit inefficient, but I don't think that writing
+ // special handling for this "trivial case" is a good idea.
+
+ let infcx = &self.infcx;
+ let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) =
+ infcx.instantiate_canonical_with_fresh_inference_vars(
+ span,
+ &param_env_and_self_ty,
+ );
+ debug!(
+ "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}",
+ param_env_and_self_ty, self_ty
+ );
+ MethodAutoderefStepsResult {
+ steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
+ self_ty: self.make_query_response_ignoring_pending_obligations(
+ canonical_inference_vars,
+ self_ty,
+ ),
+ autoderefs: 0,
+ from_unsafe_deref: false,
+ unsize: false,
+ }]),
+ opt_bad_ty: None,
+ reached_recursion_limit: false,
+ }
+ })
+ };
+
+ // If our autoderef loop had reached the recursion limit,
+ // report an overflow error, but continue going on with
+ // the truncated autoderef list.
+ if steps.reached_recursion_limit {
+ self.probe(|_| {
+ let ty = &steps
+ .steps
+ .last()
+ .unwrap_or_else(|| span_bug!(span, "reached the recursion limit in 0 steps?"))
+ .self_ty;
+ let ty = self
+ .probe_instantiate_query_response(span, &orig_values, ty)
+ .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
+ autoderef::report_autoderef_recursion_limit_error(self.tcx, span, ty.value);
+ });
+ }
+
+ // If we encountered an `_` type or an error type during autoderef, this is
+ // ambiguous.
+ if let Some(bad_ty) = &steps.opt_bad_ty {
+ if is_suggestion.0 {
+ // Ambiguity was encountered during a suggestion. Just keep going.
+ debug!("ProbeContext: encountered ambiguity in suggestion");
+ } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types {
+ // this case used to be allowed by the compiler,
+ // so we do a future-compat lint here for the 2015 edition
+ // (see https://github.com/rust-lang/rust/issues/46906)
+ if self.tcx.sess.rust_2018() {
+ self.tcx.sess.emit_err(MethodCallOnUnknownType { span });
+ } else {
+ self.tcx.struct_span_lint_hir(
+ lint::builtin::TYVAR_BEHIND_RAW_POINTER,
+ scope_expr_id,
+ span,
+ "type annotations needed",
+ |lint| lint,
+ );
+ }
+ } else {
+ // Encountered a real ambiguity, so abort the lookup. If `ty` is not
+ // an `Err`, report the right "type annotations needed" error pointing
+ // to it.
+ let ty = &bad_ty.ty;
+ let ty = self
+ .probe_instantiate_query_response(span, &orig_values, ty)
+ .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
+ let ty = self.structurally_resolved_type(span, ty.value);
+ assert!(matches!(ty.kind(), ty::Error(_)));
+ return Err(MethodError::NoMatch(NoMatchData {
+ static_candidates: Vec::new(),
+ unsatisfied_predicates: Vec::new(),
+ out_of_scope_traits: Vec::new(),
+ lev_candidate: None,
+ mode,
+ }));
+ }
+ }
+
+ debug!("ProbeContext: steps for self_ty={:?} are {:?}", self_ty, steps);
+
+ // this creates one big transaction so that all type variables etc
+ // that we create during the probe process are removed later
+ self.probe(|_| {
+ let mut probe_cx = ProbeContext::new(
+ self,
+ span,
+ mode,
+ method_name,
+ return_type,
+ orig_values,
+ steps.steps,
+ is_suggestion,
+ scope_expr_id,
+ );
+
+ probe_cx.assemble_inherent_candidates();
+ match scope {
+ ProbeScope::TraitsInScope => {
+ probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)
+ }
+ ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
+ };
+ op(probe_cx)
+ })
+ }
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ providers.method_autoderef_steps = method_autoderef_steps;
+}
+
+fn method_autoderef_steps<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ goal: CanonicalTyGoal<'tcx>,
+) -> MethodAutoderefStepsResult<'tcx> {
+ debug!("method_autoderef_steps({:?})", goal);
+
+ let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
+ let ParamEnvAnd { param_env, value: self_ty } = goal;
+
+ let mut autoderef =
+ Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
+ .include_raw_pointers()
+ .silence_errors();
+ let mut reached_raw_pointer = false;
+ let mut steps: Vec<_> = autoderef
+ .by_ref()
+ .map(|(ty, d)| {
+ let step = CandidateStep {
+ self_ty: infcx
+ .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty),
+ autoderefs: d,
+ from_unsafe_deref: reached_raw_pointer,
+ unsize: false,
+ };
+ if let ty::RawPtr(_) = ty.kind() {
+ // all the subsequent steps will be from_unsafe_deref
+ reached_raw_pointer = true;
+ }
+ step
+ })
+ .collect();
+
+ let final_ty = autoderef.final_ty(true);
+ let opt_bad_ty = match final_ty.kind() {
+ ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
+ reached_raw_pointer,
+ ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+ }),
+ ty::Array(elem_ty, _) => {
+ let dereferences = steps.len() - 1;
+
+ steps.push(CandidateStep {
+ self_ty: infcx.make_query_response_ignoring_pending_obligations(
+ inference_vars,
+ infcx.tcx.mk_slice(*elem_ty),
+ ),
+ autoderefs: dereferences,
+ // this could be from an unsafe deref if we had
+ // a *mut/const [T; N]
+ from_unsafe_deref: reached_raw_pointer,
+ unsize: true,
+ });
+
+ None
+ }
+ _ => None,
+ };
+
+ debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+
+ MethodAutoderefStepsResult {
+ steps: tcx.arena.alloc_from_iter(steps),
+ opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
+ reached_recursion_limit: autoderef.reached_recursion_limit(),
+ }
+}
+
+impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
+ fn new(
+ fcx: &'a FnCtxt<'a, 'tcx>,
+ span: Span,
+ mode: Mode,
+ method_name: Option<Ident>,
+ return_type: Option<Ty<'tcx>>,
+ orig_steps_var_values: OriginalQueryValues<'tcx>,
+ steps: &'tcx [CandidateStep<'tcx>],
+ is_suggestion: IsSuggestion,
+ scope_expr_id: hir::HirId,
+ ) -> ProbeContext<'a, 'tcx> {
+ ProbeContext {
+ fcx,
+ span,
+ mode,
+ method_name,
+ return_type,
+ inherent_candidates: Vec::new(),
+ extension_candidates: Vec::new(),
+ impl_dups: FxHashSet::default(),
+ orig_steps_var_values,
+ steps,
+ static_candidates: Vec::new(),
+ allow_similar_names: false,
+ private_candidate: None,
+ unsatisfied_predicates: Vec::new(),
+ is_suggestion,
+ scope_expr_id,
+ }
+ }
+
+ fn reset(&mut self) {
+ self.inherent_candidates.clear();
+ self.extension_candidates.clear();
+ self.impl_dups.clear();
+ self.static_candidates.clear();
+ self.private_candidate = None;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // CANDIDATE ASSEMBLY
+
+ fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) {
+ let is_accessible = if let Some(name) = self.method_name {
+ let item = candidate.item;
+ let def_scope = self
+ .tcx
+ .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id)
+ .1;
+ item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx)
+ } else {
+ true
+ };
+ if is_accessible {
+ if is_inherent {
+ self.inherent_candidates.push(candidate);
+ } else {
+ self.extension_candidates.push(candidate);
+ }
+ } else if self.private_candidate.is_none() {
+ self.private_candidate =
+ Some((candidate.item.kind.as_def_kind(), candidate.item.def_id));
+ }
+ }
+
+ fn assemble_inherent_candidates(&mut self) {
+ for step in self.steps.iter() {
+ self.assemble_probe(&step.self_ty);
+ }
+ }
+
+ fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) {
+ debug!("assemble_probe: self_ty={:?}", self_ty);
+ let raw_self_ty = self_ty.value.value;
+ match *raw_self_ty.kind() {
+ ty::Dynamic(data, ..) if let Some(p) = data.principal() => {
+ // Subtle: we can't use `instantiate_query_response` here: using it will
+ // commit to all of the type equalities assumed by inference going through
+ // autoderef (see the `method-probe-no-guessing` test).
+ //
+ // However, in this code, it is OK if we end up with an object type that is
+ // "more general" than the object type that we are evaluating. For *every*
+ // object type `MY_OBJECT`, a function call that goes through a trait-ref
+ // of the form `<MY_OBJECT as SuperTraitOf(MY_OBJECT)>::func` is a valid
+ // `ObjectCandidate`, and it should be discoverable "exactly" through one
+ // of the iterations in the autoderef loop, so there is no problem with it
+ // being discoverable in another one of these iterations.
+ //
+ // Using `instantiate_canonical_with_fresh_inference_vars` on our
+ // `Canonical<QueryResponse<Ty<'tcx>>>` and then *throwing away* the
+ // `CanonicalVarValues` will exactly give us such a generalization - it
+ // will still match the original object type, but it won't pollute our
+ // type variables in any form, so just do that!
+ let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) =
+ self.fcx
+ .instantiate_canonical_with_fresh_inference_vars(self.span, self_ty);
+
+ self.assemble_inherent_candidates_from_object(generalized_self_ty);
+ self.assemble_inherent_impl_candidates_for_type(p.def_id());
+ if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) {
+ self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+ }
+ }
+ ty::Adt(def, _) => {
+ let def_id = def.did();
+ self.assemble_inherent_impl_candidates_for_type(def_id);
+ if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
+ self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+ }
+ }
+ ty::Foreign(did) => {
+ self.assemble_inherent_impl_candidates_for_type(did);
+ if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
+ self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+ }
+ }
+ ty::Param(p) => {
+ self.assemble_inherent_candidates_from_param(p);
+ }
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Str
+ | ty::Array(..)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(..)
+ | ty::Never
+ | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty),
+ _ => {}
+ }
+ }
+
+ fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) {
+ let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else {
+ bug!("unexpected incoherent type: {:?}", self_ty)
+ };
+ for &impl_def_id in self.tcx.incoherent_impls(simp) {
+ self.assemble_inherent_impl_probe(impl_def_id);
+ }
+ }
+
+ fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) {
+ let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id);
+ for &impl_def_id in impl_def_ids.iter() {
+ self.assemble_inherent_impl_probe(impl_def_id);
+ }
+ }
+
+ fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) {
+ if !self.impl_dups.insert(impl_def_id) {
+ return; // already visited
+ }
+
+ debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
+
+ for item in self.impl_or_trait_item(impl_def_id) {
+ if !self.has_applicable_self(&item) {
+ // No receiver declared. Not a candidate.
+ self.record_static_candidate(CandidateSource::Impl(impl_def_id));
+ continue;
+ }
+
+ let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
+ let impl_ty = impl_ty.subst(self.tcx, impl_substs);
+
+ debug!("impl_ty: {:?}", impl_ty);
+
+ // Determine the receiver type that the method itself expects.
+ let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(&item, impl_ty, impl_substs);
+ debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty);
+
+ // We can't use normalize_associated_types_in as it will pollute the
+ // fcx's fulfillment context after this probe is over.
+ // Note: we only normalize `xform_self_ty` here since the normalization
+ // of the return type can lead to inference results that prohibit
+ // valid candidates from being found, see issue #85671
+ // FIXME Postponing the normalization of the return type likely only hides a deeper bug,
+ // which might be caused by the `param_env` itself. The clauses of the `param_env`
+ // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized,
+ // see issue #89650
+ let cause = traits::ObligationCause::misc(self.span, self.body_id);
+ let selcx = &mut traits::SelectionContext::new(self.fcx);
+ let traits::Normalized { value: xform_self_ty, obligations } =
+ traits::normalize(selcx, self.param_env, cause, xform_self_ty);
+ debug!(
+ "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}",
+ xform_self_ty, xform_ret_ty
+ );
+
+ self.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ kind: InherentImplCandidate(impl_substs, obligations),
+ import_ids: smallvec![],
+ },
+ true,
+ );
+ }
+ }
+
+ fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) {
+ debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty);
+
+ let principal = match self_ty.kind() {
+ ty::Dynamic(ref data, ..) => Some(data),
+ _ => None,
+ }
+ .and_then(|data| data.principal())
+ .unwrap_or_else(|| {
+ span_bug!(
+ self.span,
+ "non-object {:?} in assemble_inherent_candidates_from_object",
+ self_ty
+ )
+ });
+
+ // It is illegal to invoke a method on a trait instance that refers to
+ // the `Self` type. An [`ObjectSafetyViolation::SupertraitSelf`] error
+ // will be reported by `object_safety.rs` if the method refers to the
+ // `Self` type anywhere other than the receiver. Here, we use a
+ // substitution that replaces `Self` with the object type itself. Hence,
+ // a `&self` method will wind up with an argument type like `&dyn Trait`.
+ let trait_ref = principal.with_self_ty(self.tcx, self_ty);
+ self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| {
+ let new_trait_ref = this.erase_late_bound_regions(new_trait_ref);
+
+ let (xform_self_ty, xform_ret_ty) =
+ this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs);
+ this.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ kind: ObjectCandidate,
+ import_ids: smallvec![],
+ },
+ true,
+ );
+ });
+ }
+
+ fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
+ // FIXME: do we want to commit to this behavior for param bounds?
+ debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty);
+
+ let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| {
+ let bound_predicate = predicate.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Trait(trait_predicate) => {
+ match *trait_predicate.trait_ref.self_ty().kind() {
+ ty::Param(p) if p == param_ty => {
+ Some(bound_predicate.rebind(trait_predicate.trait_ref))
+ }
+ _ => None,
+ }
+ }
+ ty::PredicateKind::Subtype(..)
+ | ty::PredicateKind::Coerce(..)
+ | ty::PredicateKind::Projection(..)
+ | ty::PredicateKind::RegionOutlives(..)
+ | ty::PredicateKind::WellFormed(..)
+ | ty::PredicateKind::ObjectSafe(..)
+ | ty::PredicateKind::ClosureKind(..)
+ | ty::PredicateKind::TypeOutlives(..)
+ | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ }
+ });
+
+ self.elaborate_bounds(bounds, |this, poly_trait_ref, item| {
+ let trait_ref = this.erase_late_bound_regions(poly_trait_ref);
+
+ let (xform_self_ty, xform_ret_ty) =
+ this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs);
+
+ // Because this trait derives from a where-clause, it
+ // should not contain any inference variables or other
+ // artifacts. This means it is safe to put into the
+ // `WhereClauseCandidate` and (eventually) into the
+ // `WhereClausePick`.
+ assert!(!trait_ref.substs.needs_infer());
+
+ this.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ kind: WhereClauseCandidate(poly_trait_ref),
+ import_ids: smallvec![],
+ },
+ true,
+ );
+ });
+ }
+
+ // Do a search through a list of bounds, using a callback to actually
+ // create the candidates.
+ fn elaborate_bounds<F>(
+ &mut self,
+ bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
+ mut mk_cand: F,
+ ) where
+ F: for<'b> FnMut(&mut ProbeContext<'b, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssocItem),
+ {
+ let tcx = self.tcx;
+ for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
+ debug!("elaborate_bounds(bound_trait_ref={:?})", bound_trait_ref);
+ for item in self.impl_or_trait_item(bound_trait_ref.def_id()) {
+ if !self.has_applicable_self(&item) {
+ self.record_static_candidate(CandidateSource::Trait(bound_trait_ref.def_id()));
+ } else {
+ mk_cand(self, bound_trait_ref, item);
+ }
+ }
+ }
+ }
+
+ fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) {
+ let mut duplicates = FxHashSet::default();
+ let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id);
+ if let Some(applicable_traits) = opt_applicable_traits {
+ for trait_candidate in applicable_traits.iter() {
+ let trait_did = trait_candidate.def_id;
+ if duplicates.insert(trait_did) {
+ self.assemble_extension_candidates_for_trait(
+ &trait_candidate.import_ids,
+ trait_did,
+ );
+ }
+ }
+ }
+ }
+
+ fn assemble_extension_candidates_for_all_traits(&mut self) {
+ let mut duplicates = FxHashSet::default();
+ for trait_info in suggest::all_traits(self.tcx) {
+ if duplicates.insert(trait_info.def_id) {
+ self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id);
+ }
+ }
+ }
+
+ pub fn matches_return_type(
+ &self,
+ method: &ty::AssocItem,
+ self_ty: Option<Ty<'tcx>>,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ match method.kind {
+ ty::AssocKind::Fn => {
+ let fty = self.tcx.bound_fn_sig(method.def_id);
+ self.probe(|_| {
+ let substs = self.fresh_substs_for_item(self.span, method.def_id);
+ let fty = fty.subst(self.tcx, substs);
+ let fty =
+ self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty);
+
+ if let Some(self_ty) = self_ty {
+ if self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .sup(fty.inputs()[0], self_ty)
+ .is_err()
+ {
+ return false;
+ }
+ }
+ self.can_sub(self.param_env, fty.output(), expected).is_ok()
+ })
+ }
+ _ => false,
+ }
+ }
+
+ fn assemble_extension_candidates_for_trait(
+ &mut self,
+ import_ids: &SmallVec<[LocalDefId; 1]>,
+ trait_def_id: DefId,
+ ) {
+ debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id);
+ let trait_substs = self.fresh_item_substs(trait_def_id);
+ let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs);
+
+ if self.tcx.is_trait_alias(trait_def_id) {
+ // For trait aliases, assume all supertraits are relevant.
+ let bounds = iter::once(ty::Binder::dummy(trait_ref));
+ self.elaborate_bounds(bounds, |this, new_trait_ref, item| {
+ let new_trait_ref = this.erase_late_bound_regions(new_trait_ref);
+
+ let (xform_self_ty, xform_ret_ty) =
+ this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs);
+ this.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ import_ids: import_ids.clone(),
+ kind: TraitCandidate(new_trait_ref),
+ },
+ false,
+ );
+ });
+ } else {
+ debug_assert!(self.tcx.is_trait(trait_def_id));
+ for item in self.impl_or_trait_item(trait_def_id) {
+ // Check whether `trait_def_id` defines a method with suitable name.
+ if !self.has_applicable_self(&item) {
+ debug!("method has inapplicable self");
+ self.record_static_candidate(CandidateSource::Trait(trait_def_id));
+ continue;
+ }
+
+ let (xform_self_ty, xform_ret_ty) =
+ self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs);
+ self.push_candidate(
+ Candidate {
+ xform_self_ty,
+ xform_ret_ty,
+ item,
+ import_ids: import_ids.clone(),
+ kind: TraitCandidate(trait_ref),
+ },
+ false,
+ );
+ }
+ }
+ }
+
+ fn candidate_method_names(
+ &self,
+ candidate_filter: impl Fn(&ty::AssocItem) -> bool,
+ ) -> Vec<Ident> {
+ let mut set = FxHashSet::default();
+ let mut names: Vec<_> = self
+ .inherent_candidates
+ .iter()
+ .chain(&self.extension_candidates)
+ .filter(|candidate| candidate_filter(&candidate.item))
+ .filter(|candidate| {
+ if let Some(return_ty) = self.return_type {
+ self.matches_return_type(&candidate.item, None, return_ty)
+ } else {
+ true
+ }
+ })
+ .map(|candidate| candidate.item.ident(self.tcx))
+ .filter(|&name| set.insert(name))
+ .collect();
+
+ // Sort them by the name so we have a stable result.
+ names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap());
+ names
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // THE ACTUAL SEARCH
+
+ fn pick(mut self) -> PickResult<'tcx> {
+ assert!(self.method_name.is_some());
+
+ if let Some(r) = self.pick_core() {
+ return r;
+ }
+
+ debug!("pick: actual search failed, assemble diagnostics");
+
+ let static_candidates = mem::take(&mut self.static_candidates);
+ let private_candidate = self.private_candidate.take();
+ let unsatisfied_predicates = mem::take(&mut self.unsatisfied_predicates);
+
+ // things failed, so lets look at all traits, for diagnostic purposes now:
+ self.reset();
+
+ let span = self.span;
+ let tcx = self.tcx;
+
+ self.assemble_extension_candidates_for_all_traits();
+
+ let out_of_scope_traits = match self.pick_core() {
+ Some(Ok(p)) => vec![p.item.container_id(self.tcx)],
+ //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(),
+ Some(Err(MethodError::Ambiguity(v))) => v
+ .into_iter()
+ .map(|source| match source {
+ CandidateSource::Trait(id) => id,
+ CandidateSource::Impl(impl_id) => match tcx.trait_id_of_impl(impl_id) {
+ Some(id) => id,
+ None => span_bug!(span, "found inherent method when looking at traits"),
+ },
+ })
+ .collect(),
+ Some(Err(MethodError::NoMatch(NoMatchData {
+ out_of_scope_traits: others, ..
+ }))) => {
+ assert!(others.is_empty());
+ vec![]
+ }
+ _ => vec![],
+ };
+
+ if let Some((kind, def_id)) = private_candidate {
+ return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits));
+ }
+ let lev_candidate = self.probe_for_lev_candidate()?;
+
+ Err(MethodError::NoMatch(NoMatchData {
+ static_candidates,
+ unsatisfied_predicates,
+ out_of_scope_traits,
+ lev_candidate,
+ mode: self.mode,
+ }))
+ }
+
+ fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
+ let mut unstable_candidates = Vec::new();
+ let pick = self.pick_all_method(Some(&mut unstable_candidates));
+
+ // In this case unstable picking is done by `pick_method`.
+ if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable {
+ return pick;
+ }
+
+ match pick {
+ // Emit a lint if there are unstable candidates alongside the stable ones.
+ //
+ // We suppress warning if we're picking the method only because it is a
+ // suggestion.
+ Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => {
+ self.emit_unstable_name_collision_hint(p, &unstable_candidates);
+ pick
+ }
+ Some(_) => pick,
+ None => self.pick_all_method(None),
+ }
+ }
+
+ fn pick_all_method(
+ &mut self,
+ mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>> {
+ let steps = self.steps.clone();
+ steps
+ .iter()
+ .filter(|step| {
+ debug!("pick_all_method: step={:?}", step);
+ // skip types that are from a type error or that would require dereferencing
+ // a raw pointer
+ !step.self_ty.references_error() && !step.from_unsafe_deref
+ })
+ .flat_map(|step| {
+ let InferOk { value: self_ty, obligations: _ } = self
+ .fcx
+ .probe_instantiate_query_response(
+ self.span,
+ &self.orig_steps_var_values,
+ &step.self_ty,
+ )
+ .unwrap_or_else(|_| {
+ span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty)
+ });
+ self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut())
+ .or_else(|| {
+ self.pick_autorefd_method(
+ step,
+ self_ty,
+ hir::Mutability::Not,
+ unstable_candidates.as_deref_mut(),
+ )
+ .or_else(|| {
+ self.pick_autorefd_method(
+ step,
+ self_ty,
+ hir::Mutability::Mut,
+ unstable_candidates.as_deref_mut(),
+ )
+ })
+ .or_else(|| {
+ self.pick_const_ptr_method(
+ step,
+ self_ty,
+ unstable_candidates.as_deref_mut(),
+ )
+ })
+ })
+ })
+ .next()
+ }
+
+ /// For each type `T` in the step list, this attempts to find a method where
+ /// the (transformed) self type is exactly `T`. We do however do one
+ /// transformation on the adjustment: if we are passing a region pointer in,
+ /// we will potentially *reborrow* it to a shorter lifetime. This allows us
+ /// to transparently pass `&mut` pointers, in particular, without consuming
+ /// them for their entire lifetime.
+ fn pick_by_value_method(
+ &mut self,
+ step: &CandidateStep<'tcx>,
+ self_ty: Ty<'tcx>,
+ unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>> {
+ if step.unsize {
+ return None;
+ }
+
+ self.pick_method(self_ty, unstable_candidates).map(|r| {
+ r.map(|mut pick| {
+ pick.autoderefs = step.autoderefs;
+
+ // Insert a `&*` or `&mut *` if this is a reference type:
+ if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() {
+ pick.autoderefs += 1;
+ pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref {
+ mutbl,
+ unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()),
+ })
+ }
+
+ pick
+ })
+ })
+ }
+
+ fn pick_autorefd_method(
+ &mut self,
+ step: &CandidateStep<'tcx>,
+ self_ty: Ty<'tcx>,
+ mutbl: hir::Mutability,
+ unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>> {
+ let tcx = self.tcx;
+
+ // In general, during probing we erase regions.
+ let region = tcx.lifetimes.re_erased;
+
+ let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl });
+ self.pick_method(autoref_ty, unstable_candidates).map(|r| {
+ r.map(|mut pick| {
+ pick.autoderefs = step.autoderefs;
+ pick.autoref_or_ptr_adjustment =
+ Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize });
+ pick
+ })
+ })
+ }
+
+ /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
+ /// special case for this is because going from `*mut T` to `*const T` with autoderefs and
+ /// autorefs would require dereferencing the pointer, which is not safe.
+ fn pick_const_ptr_method(
+ &mut self,
+ step: &CandidateStep<'tcx>,
+ self_ty: Ty<'tcx>,
+ unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>> {
+ // Don't convert an unsized reference to ptr
+ if step.unsize {
+ return None;
+ }
+
+ let &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) = self_ty.kind() else {
+ return None;
+ };
+
+ let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not };
+ let const_ptr_ty = self.tcx.mk_ptr(const_self_ty);
+ self.pick_method(const_ptr_ty, unstable_candidates).map(|r| {
+ r.map(|mut pick| {
+ pick.autoderefs = step.autoderefs;
+ pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
+ pick
+ })
+ })
+ }
+
+ fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
+ debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty));
+
+ let mut possibly_unsatisfied_predicates = Vec::new();
+ let mut unstable_candidates = Vec::new();
+
+ for (kind, candidates) in
+ &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
+ {
+ debug!("searching {} candidates", kind);
+ let res = self.consider_candidates(
+ self_ty,
+ candidates.iter(),
+ &mut possibly_unsatisfied_predicates,
+ Some(&mut unstable_candidates),
+ );
+ if let Some(pick) = res {
+ if !self.is_suggestion.0 && !unstable_candidates.is_empty() {
+ if let Ok(p) = &pick {
+ // Emit a lint if there are unstable candidates alongside the stable ones.
+ //
+ // We suppress warning if we're picking the method only because it is a
+ // suggestion.
+ self.emit_unstable_name_collision_hint(p, &unstable_candidates);
+ }
+ }
+ return Some(pick);
+ }
+ }
+
+ debug!("searching unstable candidates");
+ let res = self.consider_candidates(
+ self_ty,
+ unstable_candidates.iter().map(|(c, _)| c),
+ &mut possibly_unsatisfied_predicates,
+ None,
+ );
+ if res.is_none() {
+ self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ }
+ res
+ }
+
+ fn pick_method(
+ &mut self,
+ self_ty: Ty<'tcx>,
+ mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>> {
+ if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable {
+ return self.pick_method_with_unstable(self_ty);
+ }
+
+ debug!("pick_method(self_ty={})", self.ty_to_string(self_ty));
+
+ let mut possibly_unsatisfied_predicates = Vec::new();
+
+ for (kind, candidates) in
+ &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
+ {
+ debug!("searching {} candidates", kind);
+ let res = self.consider_candidates(
+ self_ty,
+ candidates.iter(),
+ &mut possibly_unsatisfied_predicates,
+ unstable_candidates.as_deref_mut(),
+ );
+ if let Some(pick) = res {
+ return Some(pick);
+ }
+ }
+
+ // `pick_method` may be called twice for the same self_ty if no stable methods
+ // match. Only extend once.
+ if unstable_candidates.is_some() {
+ self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
+ }
+ None
+ }
+
+ fn consider_candidates<'b, ProbesIter>(
+ &self,
+ self_ty: Ty<'tcx>,
+ probes: ProbesIter,
+ possibly_unsatisfied_predicates: &mut Vec<(
+ ty::Predicate<'tcx>,
+ Option<ty::Predicate<'tcx>>,
+ Option<ObligationCause<'tcx>>,
+ )>,
+ unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ ) -> Option<PickResult<'tcx>>
+ where
+ ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
+ 'tcx: 'b,
+ {
+ let mut applicable_candidates: Vec<_> = probes
+ .clone()
+ .map(|probe| {
+ (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates))
+ })
+ .filter(|&(_, status)| status != ProbeResult::NoMatch)
+ .collect();
+
+ debug!("applicable_candidates: {:?}", applicable_candidates);
+
+ if applicable_candidates.len() > 1 {
+ if let Some(pick) =
+ self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates)
+ {
+ return Some(Ok(pick));
+ }
+ }
+
+ if let Some(uc) = unstable_candidates {
+ applicable_candidates.retain(|&(p, _)| {
+ if let stability::EvalResult::Deny { feature, .. } =
+ self.tcx.eval_stability(p.item.def_id, None, self.span, None)
+ {
+ uc.push((p.clone(), feature));
+ return false;
+ }
+ true
+ });
+ }
+
+ if applicable_candidates.len() > 1 {
+ let sources = probes.map(|p| self.candidate_source(p, self_ty)).collect();
+ return Some(Err(MethodError::Ambiguity(sources)));
+ }
+
+ applicable_candidates.pop().map(|(probe, status)| {
+ if status == ProbeResult::Match {
+ Ok(probe.to_unadjusted_pick(self_ty))
+ } else {
+ Err(MethodError::BadReturnType)
+ }
+ })
+ }
+
+ fn emit_unstable_name_collision_hint(
+ &self,
+ stable_pick: &Pick<'_>,
+ unstable_candidates: &[(Candidate<'tcx>, Symbol)],
+ ) {
+ let def_kind = stable_pick.item.kind.as_def_kind();
+ self.tcx.struct_span_lint_hir(
+ lint::builtin::UNSTABLE_NAME_COLLISIONS,
+ self.scope_expr_id,
+ self.span,
+ format!(
+ "{} {} with this name may be added to the standard library in the future",
+ def_kind.article(),
+ def_kind.descr(stable_pick.item.def_id),
+ ),
+ |lint| {
+ match (stable_pick.item.kind, stable_pick.item.container) {
+ (ty::AssocKind::Fn, _) => {
+ // FIXME: This should be a `span_suggestion` instead of `help`
+ // However `self.span` only
+ // highlights the method name, so we can't use it. Also consider reusing
+ // the code from `report_method_error()`.
+ lint.help(&format!(
+ "call with fully qualified syntax `{}(...)` to keep using the current \
+ method",
+ self.tcx.def_path_str(stable_pick.item.def_id),
+ ));
+ }
+ (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => {
+ let def_id = stable_pick.item.container_id(self.tcx);
+ lint.span_suggestion(
+ self.span,
+ "use the fully qualified path to the associated const",
+ format!(
+ "<{} as {}>::{}",
+ stable_pick.self_ty,
+ self.tcx.def_path_str(def_id),
+ stable_pick.item.name
+ ),
+ Applicability::MachineApplicable,
+ );
+ }
+ _ => {}
+ }
+ if self.tcx.sess.is_nightly_build() {
+ for (candidate, feature) in unstable_candidates {
+ lint.help(&format!(
+ "add `#![feature({})]` to the crate attributes to enable `{}`",
+ feature,
+ self.tcx.def_path_str(candidate.item.def_id),
+ ));
+ }
+ }
+
+ lint
+ },
+ );
+ }
+
+ fn select_trait_candidate(
+ &self,
+ trait_ref: ty::TraitRef<'tcx>,
+ ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> {
+ let cause = traits::ObligationCause::misc(self.span, self.body_id);
+ let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate();
+ let obligation = traits::Obligation::new(cause, self.param_env, predicate);
+ traits::SelectionContext::new(self).select(&obligation)
+ }
+
+ fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource {
+ match candidate.kind {
+ InherentImplCandidate(..) => {
+ CandidateSource::Impl(candidate.item.container_id(self.tcx))
+ }
+ ObjectCandidate | WhereClauseCandidate(_) => {
+ CandidateSource::Trait(candidate.item.container_id(self.tcx))
+ }
+ TraitCandidate(trait_ref) => self.probe(|_| {
+ let _ = self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .define_opaque_types(false)
+ .sup(candidate.xform_self_ty, self_ty);
+ match self.select_trait_candidate(trait_ref) {
+ Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => {
+ // If only a single impl matches, make the error message point
+ // to that impl.
+ CandidateSource::Impl(impl_data.impl_def_id)
+ }
+ _ => CandidateSource::Trait(candidate.item.container_id(self.tcx)),
+ }
+ }),
+ }
+ }
+
+ fn consider_probe(
+ &self,
+ self_ty: Ty<'tcx>,
+ probe: &Candidate<'tcx>,
+ possibly_unsatisfied_predicates: &mut Vec<(
+ ty::Predicate<'tcx>,
+ Option<ty::Predicate<'tcx>>,
+ Option<ObligationCause<'tcx>>,
+ )>,
+ ) -> ProbeResult {
+ debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);
+
+ self.probe(|_| {
+ // First check that the self type can be related.
+ let sub_obligations = match self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .define_opaque_types(false)
+ .sup(probe.xform_self_ty, self_ty)
+ {
+ Ok(InferOk { obligations, value: () }) => obligations,
+ Err(err) => {
+ debug!("--> cannot relate self-types {:?}", err);
+ return ProbeResult::NoMatch;
+ }
+ };
+
+ let mut result = ProbeResult::Match;
+ let mut xform_ret_ty = probe.xform_ret_ty;
+ debug!(?xform_ret_ty);
+
+ let selcx = &mut traits::SelectionContext::new(self);
+ let cause = traits::ObligationCause::misc(self.span, self.body_id);
+
+ let mut parent_pred = None;
+
+ // If so, impls may carry other conditions (e.g., where
+ // clauses) that must be considered. Make sure that those
+ // match as well (or at least may match, sometimes we
+ // don't have enough information to fully evaluate).
+ match probe.kind {
+ InherentImplCandidate(ref substs, ref ref_obligations) => {
+ // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`,
+ // see the reasons mentioned in the comments in `assemble_inherent_impl_probe`
+ // for why this is necessary
+ let traits::Normalized {
+ value: normalized_xform_ret_ty,
+ obligations: normalization_obligations,
+ } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty);
+ xform_ret_ty = normalized_xform_ret_ty;
+ debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
+
+ // Check whether the impl imposes obligations we have to worry about.
+ let impl_def_id = probe.item.container_id(self.tcx);
+ let impl_bounds = self.tcx.predicates_of(impl_def_id);
+ let impl_bounds = impl_bounds.instantiate(self.tcx, substs);
+ let traits::Normalized { value: impl_bounds, obligations: norm_obligations } =
+ traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds);
+
+ // Convert the bounds into obligations.
+ let impl_obligations = traits::predicates_for_generics(
+ move |_, _| cause.clone(),
+ self.param_env,
+ impl_bounds,
+ );
+
+ let candidate_obligations = impl_obligations
+ .chain(norm_obligations.into_iter())
+ .chain(ref_obligations.iter().cloned())
+ .chain(normalization_obligations.into_iter());
+
+ // Evaluate those obligations to see if they might possibly hold.
+ for o in candidate_obligations {
+ let o = self.resolve_vars_if_possible(o);
+ if !self.predicate_may_hold(&o) {
+ result = ProbeResult::NoMatch;
+ possibly_unsatisfied_predicates.push((
+ o.predicate,
+ None,
+ Some(o.cause),
+ ));
+ }
+ }
+ }
+
+ ObjectCandidate | WhereClauseCandidate(..) => {
+ // These have no additional conditions to check.
+ }
+
+ TraitCandidate(trait_ref) => {
+ if let Some(method_name) = self.method_name {
+ // Some trait methods are excluded for arrays before 2021.
+ // (`array.into_iter()` wants a slice iterator for compatibility.)
+ if self_ty.is_array() && !method_name.span.rust_2021() {
+ let trait_def = self.tcx.trait_def(trait_ref.def_id);
+ if trait_def.skip_array_during_method_dispatch {
+ return ProbeResult::NoMatch;
+ }
+ }
+ }
+ let predicate =
+ ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx);
+ parent_pred = Some(predicate);
+ let obligation = traits::Obligation::new(cause, self.param_env, predicate);
+ if !self.predicate_may_hold(&obligation) {
+ result = ProbeResult::NoMatch;
+ if self.probe(|_| {
+ match self.select_trait_candidate(trait_ref) {
+ Err(_) => return true,
+ Ok(Some(impl_source))
+ if !impl_source.borrow_nested_obligations().is_empty() =>
+ {
+ for obligation in impl_source.borrow_nested_obligations() {
+ // Determine exactly which obligation wasn't met, so
+ // that we can give more context in the error.
+ if !self.predicate_may_hold(obligation) {
+ let nested_predicate =
+ self.resolve_vars_if_possible(obligation.predicate);
+ let predicate =
+ self.resolve_vars_if_possible(predicate);
+ let p = if predicate == nested_predicate {
+ // Avoid "`MyStruct: Foo` which is required by
+ // `MyStruct: Foo`" in E0599.
+ None
+ } else {
+ Some(predicate)
+ };
+ possibly_unsatisfied_predicates.push((
+ nested_predicate,
+ p,
+ Some(obligation.cause.clone()),
+ ));
+ }
+ }
+ }
+ _ => {
+ // Some nested subobligation of this predicate
+ // failed.
+ let predicate = self.resolve_vars_if_possible(predicate);
+ possibly_unsatisfied_predicates.push((predicate, None, None));
+ }
+ }
+ false
+ }) {
+ // This candidate's primary obligation doesn't even
+ // select - don't bother registering anything in
+ // `potentially_unsatisfied_predicates`.
+ return ProbeResult::NoMatch;
+ }
+ }
+ }
+ }
+
+ // Evaluate those obligations to see if they might possibly hold.
+ for o in sub_obligations {
+ let o = self.resolve_vars_if_possible(o);
+ if !self.predicate_may_hold(&o) {
+ result = ProbeResult::NoMatch;
+ possibly_unsatisfied_predicates.push((o.predicate, parent_pred, Some(o.cause)));
+ }
+ }
+
+ if let ProbeResult::Match = result {
+ if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) {
+ let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty);
+ debug!(
+ "comparing return_ty {:?} with xform ret ty {:?}",
+ return_ty, probe.xform_ret_ty
+ );
+ if self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .define_opaque_types(false)
+ .sup(return_ty, xform_ret_ty)
+ .is_err()
+ {
+ return ProbeResult::BadReturnType;
+ }
+ }
+ }
+
+ result
+ })
+ }
+
+ /// Sometimes we get in a situation where we have multiple probes that are all impls of the
+ /// same trait, but we don't know which impl to use. In this case, since in all cases the
+ /// external interface of the method can be determined from the trait, it's ok not to decide.
+ /// We can basically just collapse all of the probes for various impls into one where-clause
+ /// probe. This will result in a pending obligation so when more type-info is available we can
+ /// make the final decision.
+ ///
+ /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`):
+ ///
+ /// ```ignore (illustrative)
+ /// trait Foo { ... }
+ /// impl Foo for Vec<i32> { ... }
+ /// impl Foo for Vec<usize> { ... }
+ /// ```
+ ///
+ /// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we
+ /// use, so it's ok to just commit to "using the method from the trait Foo".
+ fn collapse_candidates_to_trait_pick(
+ &self,
+ self_ty: Ty<'tcx>,
+ probes: &[(&Candidate<'tcx>, ProbeResult)],
+ ) -> Option<Pick<'tcx>> {
+ // Do all probes correspond to the same trait?
+ let container = probes[0].0.item.trait_container(self.tcx)?;
+ for (p, _) in &probes[1..] {
+ let p_container = p.item.trait_container(self.tcx)?;
+ if p_container != container {
+ return None;
+ }
+ }
+
+ // FIXME: check the return type here somehow.
+ // If so, just use this trait and call it a day.
+ Some(Pick {
+ item: probes[0].0.item,
+ kind: TraitPick,
+ import_ids: probes[0].0.import_ids.clone(),
+ autoderefs: 0,
+ autoref_or_ptr_adjustment: None,
+ self_ty,
+ })
+ }
+
+ /// Similarly to `probe_for_return_type`, this method attempts to find the best matching
+ /// candidate method where the method name may have been misspelled. Similarly to other
+ /// Levenshtein based suggestions, we provide at most one such suggestion.
+ fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
+ debug!("probing for method names similar to {:?}", self.method_name);
+
+ let steps = self.steps.clone();
+ self.probe(|_| {
+ let mut pcx = ProbeContext::new(
+ self.fcx,
+ self.span,
+ self.mode,
+ self.method_name,
+ self.return_type,
+ self.orig_steps_var_values.clone(),
+ steps,
+ IsSuggestion(true),
+ self.scope_expr_id,
+ );
+ pcx.allow_similar_names = true;
+ pcx.assemble_inherent_candidates();
+
+ let method_names = pcx.candidate_method_names(|_| true);
+ pcx.allow_similar_names = false;
+ let applicable_close_candidates: Vec<ty::AssocItem> = method_names
+ .iter()
+ .filter_map(|&method_name| {
+ pcx.reset();
+ pcx.method_name = Some(method_name);
+ pcx.assemble_inherent_candidates();
+ pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item)
+ })
+ .collect();
+
+ if applicable_close_candidates.is_empty() {
+ Ok(None)
+ } else {
+ let best_name = {
+ let names = applicable_close_candidates
+ .iter()
+ .map(|cand| cand.name)
+ .collect::<Vec<Symbol>>();
+ find_best_match_for_name_with_substrings(
+ &names,
+ self.method_name.unwrap().name,
+ None,
+ )
+ }
+ .unwrap();
+ Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name))
+ }
+ })
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // MISCELLANY
+ fn has_applicable_self(&self, item: &ty::AssocItem) -> bool {
+ // "Fast track" -- check for usage of sugar when in method call
+ // mode.
+ //
+ // In Path mode (i.e., resolving a value like `T::next`), consider any
+ // associated value (i.e., methods, constants) but not types.
+ match self.mode {
+ Mode::MethodCall => item.fn_has_self_parameter,
+ Mode::Path => match item.kind {
+ ty::AssocKind::Type => false,
+ ty::AssocKind::Fn | ty::AssocKind::Const => true,
+ },
+ }
+ // FIXME -- check for types that deref to `Self`,
+ // like `Rc<Self>` and so on.
+ //
+ // Note also that the current code will break if this type
+ // includes any of the type parameters defined on the method
+ // -- but this could be overcome.
+ }
+
+ fn record_static_candidate(&mut self, source: CandidateSource) {
+ self.static_candidates.push(source);
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn xform_self_ty(
+ &self,
+ item: &ty::AssocItem,
+ impl_ty: Ty<'tcx>,
+ substs: SubstsRef<'tcx>,
+ ) -> (Ty<'tcx>, Option<Ty<'tcx>>) {
+ if item.kind == ty::AssocKind::Fn && self.mode == Mode::MethodCall {
+ let sig = self.xform_method_sig(item.def_id, substs);
+ (sig.inputs()[0], Some(sig.output()))
+ } else {
+ (impl_ty, None)
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> {
+ let fn_sig = self.tcx.bound_fn_sig(method);
+ debug!(?fn_sig);
+
+ assert!(!substs.has_escaping_bound_vars());
+
+ // It is possible for type parameters or early-bound lifetimes
+ // to appear in the signature of `self`. The substitutions we
+ // are given do not include type/lifetime parameters for the
+ // method yet. So create fresh variables here for those too,
+ // if there are any.
+ let generics = self.tcx.generics_of(method);
+ assert_eq!(substs.len(), generics.parent_count as usize);
+
+ let xform_fn_sig = if generics.params.is_empty() {
+ fn_sig.subst(self.tcx, substs)
+ } else {
+ let substs = InternalSubsts::for_item(self.tcx, method, |param, _| {
+ let i = param.index as usize;
+ if i < substs.len() {
+ substs[i]
+ } else {
+ match param.kind {
+ GenericParamDefKind::Lifetime => {
+ // In general, during probe we erase regions.
+ self.tcx.lifetimes.re_erased.into()
+ }
+ GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+ self.var_for_def(self.span, param)
+ }
+ }
+ }
+ });
+ fn_sig.subst(self.tcx, substs)
+ };
+
+ self.erase_late_bound_regions(xform_fn_sig)
+ }
+
+ /// Gets the type of an impl and generate substitutions with inference vars.
+ fn impl_ty_and_substs(
+ &self,
+ impl_def_id: DefId,
+ ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) {
+ (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
+ }
+
+ fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
+ InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
+ GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
+ GenericParamDefKind::Type { .. } => self
+ .next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::SubstitutionPlaceholder,
+ span: self.tcx.def_span(def_id),
+ })
+ .into(),
+ GenericParamDefKind::Const { .. } => {
+ let span = self.tcx.def_span(def_id);
+ let origin = ConstVariableOrigin {
+ kind: ConstVariableOriginKind::SubstitutionPlaceholder,
+ span,
+ };
+ self.next_const_var(self.tcx.type_of(param.def_id), origin).into()
+ }
+ })
+ }
+
+ /// Replaces late-bound-regions bound by `value` with `'static` using
+ /// `ty::erase_late_bound_regions`.
+ ///
+ /// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of
+ /// method matching. It is reasonable during the probe phase because we don't consider region
+ /// relationships at all. Therefore, we can just replace all the region variables with 'static
+ /// rather than creating fresh region variables. This is nice for two reasons:
+ ///
+ /// 1. Because the numbers of the region variables would otherwise be fairly unique to this
+ /// particular method call, it winds up creating fewer types overall, which helps for memory
+ /// usage. (Admittedly, this is a rather small effect, though measurable.)
+ ///
+ /// 2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any
+ /// late-bound regions with 'static. Otherwise, if we were going to replace late-bound
+ /// regions with actual region variables as is proper, we'd have to ensure that the same
+ /// region got replaced with the same variable, which requires a bit more coordination
+ /// and/or tracking the substitution and
+ /// so forth.
+ fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ self.tcx.erase_late_bound_regions(value)
+ }
+
+ /// Finds the method with the appropriate name (or return type, as the case may be). If
+ /// `allow_similar_names` is set, find methods with close-matching names.
+ // The length of the returned iterator is nearly always 0 or 1 and this
+ // method is fairly hot.
+ fn impl_or_trait_item(&self, def_id: DefId) -> SmallVec<[ty::AssocItem; 1]> {
+ if let Some(name) = self.method_name {
+ if self.allow_similar_names {
+ let max_dist = max(name.as_str().len(), 3) / 3;
+ self.tcx
+ .associated_items(def_id)
+ .in_definition_order()
+ .filter(|x| {
+ if x.kind.namespace() != Namespace::ValueNS {
+ return false;
+ }
+ match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
+ {
+ Some(d) => d > 0,
+ None => false,
+ }
+ })
+ .copied()
+ .collect()
+ } else {
+ self.fcx
+ .associated_value(def_id, name)
+ .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x]))
+ }
+ } else {
+ self.tcx.associated_items(def_id).in_definition_order().copied().collect()
+ }
+ }
+}
+
+impl<'tcx> Candidate<'tcx> {
+ fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> {
+ Pick {
+ item: self.item,
+ kind: match self.kind {
+ InherentImplCandidate(..) => InherentImplPick,
+ ObjectCandidate => ObjectPick,
+ TraitCandidate(_) => TraitPick,
+ WhereClauseCandidate(ref trait_ref) => {
+ // Only trait derived from where-clauses should
+ // appear here, so they should not contain any
+ // inference variables or other artifacts. This
+ // means they are safe to put into the
+ // `WhereClausePick`.
+ assert!(
+ !trait_ref.skip_binder().substs.needs_infer()
+ && !trait_ref.skip_binder().substs.has_placeholders()
+ );
+
+ WhereClausePick(*trait_ref)
+ }
+ },
+ import_ids: self.import_ids.clone(),
+ autoderefs: 0,
+ autoref_or_ptr_adjustment: None,
+ self_ty,
+ }
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
new file mode 100644
index 000000000..6c21ed902
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -0,0 +1,2605 @@
+//! Give useful errors and suggestions to users when an item can't be
+//! found or is otherwise invalid.
+
+use crate::errors;
+use crate::FnCtxt;
+use rustc_ast::ast::Mutability;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{
+ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+ MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ExprKind, Node, QPath};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+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::{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};
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+use rustc_trait_selection::traits::{
+ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
+};
+
+use std::cmp::Ordering;
+use std::iter;
+
+use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
+use super::{CandidateSource, MethodError, NoMatchData};
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
+ let tcx = self.tcx;
+ match ty.kind() {
+ // Not all of these (e.g., unsafe fns) implement `FnOnce`,
+ // so we look for these beforehand.
+ ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
+ // If it's not a simple function, look for things which implement `FnOnce`.
+ _ => {
+ let Some(fn_once) = tcx.lang_items().fn_once_trait() else {
+ return false;
+ };
+
+ // This conditional prevents us from asking to call errors and unresolved types.
+ // It might seem that we can use `predicate_must_hold_modulo_regions`,
+ // but since a Dummy binder is used to fill in the FnOnce trait's arguments,
+ // type resolution always gives a "maybe" here.
+ if self.autoderef(span, ty).any(|(ty, _)| {
+ info!("check deref {:?} error", ty);
+ matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
+ }) {
+ return false;
+ }
+
+ self.autoderef(span, ty).any(|(ty, _)| {
+ info!("check deref {:?} impl FnOnce", ty);
+ self.probe(|_| {
+ let fn_once_substs = tcx.mk_substs_trait(
+ ty,
+ &[self
+ .next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span,
+ })
+ .into()],
+ );
+ let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs);
+ let poly_trait_ref = ty::Binder::dummy(trait_ref);
+ let obligation = Obligation::misc(
+ span,
+ self.body_id,
+ self.param_env,
+ poly_trait_ref.without_const().to_predicate(tcx),
+ );
+ self.predicate_may_hold(&obligation)
+ })
+ })
+ }
+ }
+ }
+
+ fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
+ self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
+ }
+
+ pub fn report_method_error(
+ &self,
+ mut span: Span,
+ rcvr_ty: Ty<'tcx>,
+ item_name: Ident,
+ source: SelfSource<'tcx>,
+ error: MethodError<'tcx>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+ ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
+ // Avoid suggestions when we don't know what's going on.
+ if rcvr_ty.references_error() {
+ return None;
+ }
+
+ let report_candidates = |span: Span,
+ err: &mut Diagnostic,
+ sources: &mut Vec<CandidateSource>,
+ sugg_span: Span| {
+ sources.sort();
+ sources.dedup();
+ // Dynamic limit to avoid hiding just one candidate, which is silly.
+ let limit = if sources.len() == 5 { 5 } else { 4 };
+
+ for (idx, source) in sources.iter().take(limit).enumerate() {
+ match *source {
+ CandidateSource::Impl(impl_did) => {
+ // Provide the best span we can. Use the item, if local to crate, else
+ // the impl, if local to crate (item may be defaulted), else nothing.
+ let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
+ let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
+ self.associated_value(impl_trait_ref.def_id, item_name)
+ }) else {
+ continue;
+ };
+
+ let note_span = if item.def_id.is_local() {
+ Some(self.tcx.def_span(item.def_id))
+ } else if impl_did.is_local() {
+ Some(self.tcx.def_span(impl_did))
+ } else {
+ None
+ };
+
+ let impl_ty = self.tcx.at(span).type_of(impl_did);
+
+ let insertion = match self.tcx.impl_trait_ref(impl_did) {
+ None => String::new(),
+ Some(trait_ref) => format!(
+ " of the trait `{}`",
+ self.tcx.def_path_str(trait_ref.def_id)
+ ),
+ };
+
+ let (note_str, idx) = if sources.len() > 1 {
+ (
+ format!(
+ "candidate #{} is defined in an impl{} for the type `{}`",
+ idx + 1,
+ insertion,
+ impl_ty,
+ ),
+ Some(idx + 1),
+ )
+ } else {
+ (
+ format!(
+ "the candidate is defined in an impl{} for the type `{}`",
+ insertion, impl_ty,
+ ),
+ None,
+ )
+ };
+ if let Some(note_span) = note_span {
+ // We have a span pointing to the method. Show note with snippet.
+ err.span_note(note_span, &note_str);
+ } else {
+ err.note(&note_str);
+ }
+ if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
+ let path = self.tcx.def_path_str(trait_ref.def_id);
+
+ let ty = match item.kind {
+ ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
+ ty::AssocKind::Fn => self
+ .tcx
+ .fn_sig(item.def_id)
+ .inputs()
+ .skip_binder()
+ .get(0)
+ .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr())
+ .copied()
+ .unwrap_or(rcvr_ty),
+ };
+ print_disambiguation_help(
+ item_name,
+ args,
+ err,
+ path,
+ ty,
+ item.kind,
+ item.def_id,
+ sugg_span,
+ idx,
+ self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
+ );
+ }
+ }
+ CandidateSource::Trait(trait_did) => {
+ let Some(item) = self.associated_value(trait_did, item_name) else { continue };
+ let item_span = self.tcx.def_span(item.def_id);
+ let idx = if sources.len() > 1 {
+ let msg = &format!(
+ "candidate #{} is defined in the trait `{}`",
+ idx + 1,
+ self.tcx.def_path_str(trait_did)
+ );
+ err.span_note(item_span, msg);
+ Some(idx + 1)
+ } else {
+ let msg = &format!(
+ "the candidate is defined in the trait `{}`",
+ self.tcx.def_path_str(trait_did)
+ );
+ err.span_note(item_span, msg);
+ None
+ };
+ let path = self.tcx.def_path_str(trait_did);
+ print_disambiguation_help(
+ item_name,
+ args,
+ err,
+ path,
+ rcvr_ty,
+ item.kind,
+ item.def_id,
+ sugg_span,
+ idx,
+ self.tcx.sess.source_map(),
+ item.fn_has_self_parameter,
+ );
+ }
+ }
+ }
+ if sources.len() > limit {
+ err.note(&format!("and {} others", sources.len() - limit));
+ }
+ };
+
+ let sugg_span = if let SelfSource::MethodCall(expr) = source {
+ // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing.
+ self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span
+ } else {
+ span
+ };
+
+ match error {
+ MethodError::NoMatch(NoMatchData {
+ static_candidates: mut static_sources,
+ unsatisfied_predicates,
+ out_of_scope_traits,
+ lev_candidate,
+ mode,
+ }) => {
+ let tcx = self.tcx;
+
+ let actual = self.resolve_vars_if_possible(rcvr_ty);
+ let ty_str = self.ty_to_string(actual);
+ let is_method = mode == Mode::MethodCall;
+ let item_kind = if is_method {
+ "method"
+ } else if actual.is_enum() {
+ "variant or associated item"
+ } else {
+ match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
+ (Some(name), false) if name.is_lowercase() => "function or associated item",
+ (Some(_), false) => "associated item",
+ (Some(_), true) | (None, false) => "variant or associated item",
+ (None, true) => "variant",
+ }
+ };
+
+ if self.suggest_wrapping_range_with_parens(
+ tcx, actual, source, span, item_name, &ty_str,
+ ) || self.suggest_constraining_numerical_ty(
+ tcx, actual, source, span, item_kind, item_name, &ty_str,
+ ) {
+ return None;
+ }
+
+ span = item_name.span;
+
+ // Don't show generic arguments when the method can't be found in any implementation (#81576).
+ let mut ty_str_reported = ty_str.clone();
+ if let ty::Adt(_, generics) = actual.kind() {
+ if generics.len() > 0 {
+ let mut autoderef = self.autoderef(span, actual);
+ let candidate_found = autoderef.any(|(ty, _)| {
+ if let ty::Adt(adt_deref, _) = ty.kind() {
+ self.tcx
+ .inherent_impls(adt_deref.did())
+ .iter()
+ .filter_map(|def_id| self.associated_value(*def_id, item_name))
+ .count()
+ >= 1
+ } else {
+ false
+ }
+ });
+ let has_deref = autoderef.step_count() > 0;
+ if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
+ if let Some((path_string, _)) = ty_str.split_once('<') {
+ ty_str_reported = path_string.to_string();
+ }
+ }
+ }
+ }
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0599,
+ "no {} named `{}` found for {} `{}` in the current scope",
+ item_kind,
+ item_name,
+ actual.prefix_string(self.tcx),
+ ty_str_reported,
+ );
+ if actual.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
+
+ if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+ self.suggest_await_before_method(
+ &mut err, item_name, actual, cal, span,
+ );
+ }
+ if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
+ err.span_suggestion(
+ span.shrink_to_lo(),
+ "you are looking for the module in `std`, not the primitive type",
+ "std::",
+ Applicability::MachineApplicable,
+ );
+ }
+ if let ty::RawPtr(_) = &actual.kind() {
+ err.note(
+ "try using `<*const T>::as_ref()` to get a reference to the \
+ type behind the pointer: https://doc.rust-lang.org/std/\
+ primitive.pointer.html#method.as_ref",
+ );
+ err.note(
+ "using `<*const T>::as_ref()` on a pointer which is unaligned or points \
+ to invalid or uninitialized memory is undefined behavior",
+ );
+ }
+
+ let ty_span = match actual.kind() {
+ ty::Param(param_type) => {
+ let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let type_param = generics.type_param(param_type, self.tcx);
+ Some(self.tcx.def_span(type_param.def_id))
+ }
+ ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())),
+ _ => None,
+ };
+
+ if let Some(span) = ty_span {
+ err.span_label(
+ span,
+ format!(
+ "{item_kind} `{item_name}` not found for this {}",
+ actual.prefix_string(self.tcx)
+ ),
+ );
+ }
+
+ if let SelfSource::MethodCall(rcvr_expr) = source {
+ self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
+ let call_expr = self
+ .tcx
+ .hir()
+ .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
+ let probe = self.lookup_probe(
+ span,
+ item_name,
+ output_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ );
+ probe.is_ok()
+ });
+ }
+
+ let mut custom_span_label = false;
+
+ if !static_sources.is_empty() {
+ err.note(
+ "found the following associated functions; to be used as methods, \
+ functions must have a `self` parameter",
+ );
+ err.span_label(span, "this is an associated function, not a method");
+ custom_span_label = true;
+ }
+ if static_sources.len() == 1 {
+ let ty_str =
+ if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) {
+ // When the "method" is resolved through dereferencing, we really want the
+ // original type that has the associated function for accurate suggestions.
+ // (#61411)
+ let ty = tcx.at(span).type_of(*impl_did);
+ match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
+ (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
+ // Use `actual` as it will have more `substs` filled in.
+ self.ty_to_value_string(actual.peel_refs())
+ }
+ _ => self.ty_to_value_string(ty.peel_refs()),
+ }
+ } else {
+ self.ty_to_value_string(actual.peel_refs())
+ };
+ if let SelfSource::MethodCall(expr) = source {
+ err.span_suggestion(
+ expr.span.to(span),
+ "use associated function syntax instead",
+ format!("{}::{}", ty_str, item_name),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.help(&format!("try with `{}::{}`", ty_str, item_name,));
+ }
+
+ report_candidates(span, &mut err, &mut static_sources, sugg_span);
+ } else if static_sources.len() > 1 {
+ report_candidates(span, &mut err, &mut static_sources, sugg_span);
+ }
+
+ let mut bound_spans = vec![];
+ let mut restrict_type_params = false;
+ let mut unsatisfied_bounds = false;
+ if item_name.name == sym::count && self.is_slice_ty(actual, span) {
+ let msg = "consider using `len` instead";
+ if let SelfSource::MethodCall(_expr) = source {
+ err.span_suggestion_short(
+ span,
+ msg,
+ "len",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(span, msg);
+ }
+ if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
+ let iterator_trait = self.tcx.def_path_str(iterator_trait);
+ err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
+ }
+ } else if !unsatisfied_predicates.is_empty() {
+ let mut type_params = FxHashMap::default();
+
+ // Pick out the list of unimplemented traits on the receiver.
+ // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute.
+ let mut unimplemented_traits = FxHashMap::default();
+ let mut unimplemented_traits_only = true;
+ for (predicate, _parent_pred, cause) in &unsatisfied_predicates {
+ if let (ty::PredicateKind::Trait(p), Some(cause)) =
+ (predicate.kind().skip_binder(), cause.as_ref())
+ {
+ if p.trait_ref.self_ty() != rcvr_ty {
+ // This is necessary, not just to keep the errors clean, but also
+ // because our derived obligations can wind up with a trait ref that
+ // requires a different param_env to be correctly compared.
+ continue;
+ }
+ unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
+ predicate.kind().rebind(p.trait_ref),
+ Obligation {
+ cause: cause.clone(),
+ param_env: self.param_env,
+ predicate: *predicate,
+ recursion_depth: 0,
+ },
+ ));
+ }
+ }
+
+ // Make sure that, if any traits other than the found ones were involved,
+ // we don't don't report an unimplemented trait.
+ // We don't want to say that `iter::Cloned` is not an iterator, just
+ // because of some non-Clone item being iterated over.
+ for (predicate, _parent_pred, _cause) in &unsatisfied_predicates {
+ match predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(p)
+ if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
+ _ => {
+ unimplemented_traits_only = false;
+ break;
+ }
+ }
+ }
+
+ let mut collect_type_param_suggestions =
+ |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
+ // We don't care about regions here, so it's fine to skip the binder here.
+ if let (ty::Param(_), ty::PredicateKind::Trait(p)) =
+ (self_ty.kind(), parent_pred.kind().skip_binder())
+ {
+ let node = match p.trait_ref.self_ty().kind() {
+ ty::Param(_) => {
+ // Account for `fn` items like in `issue-35677.rs` to
+ // suggest restricting its type params.
+ let did = self.tcx.hir().body_owner_def_id(hir::BodyId {
+ hir_id: self.body_id,
+ });
+ Some(
+ self.tcx
+ .hir()
+ .get(self.tcx.hir().local_def_id_to_hir_id(did)),
+ )
+ }
+ ty::Adt(def, _) => def.did().as_local().map(|def_id| {
+ self.tcx
+ .hir()
+ .get(self.tcx.hir().local_def_id_to_hir_id(def_id))
+ }),
+ _ => None,
+ };
+ if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
+ if let Some(g) = kind.generics() {
+ let key = (
+ g.tail_span_for_predicate_suggestion(),
+ g.add_where_or_trailing_comma(),
+ );
+ type_params
+ .entry(key)
+ .or_insert_with(FxHashSet::default)
+ .insert(obligation.to_owned());
+ }
+ }
+ }
+ };
+ let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
+ let msg = format!(
+ "doesn't satisfy `{}`",
+ if obligation.len() > 50 { quiet } else { obligation }
+ );
+ match &self_ty.kind() {
+ // Point at the type that couldn't satisfy the bound.
+ ty::Adt(def, _) => {
+ bound_spans.push((self.tcx.def_span(def.did()), msg))
+ }
+ // Point at the trait object that couldn't satisfy the bound.
+ ty::Dynamic(preds, _, _) => {
+ for pred in preds.iter() {
+ match pred.skip_binder() {
+ ty::ExistentialPredicate::Trait(tr) => bound_spans
+ .push((self.tcx.def_span(tr.def_id), msg.clone())),
+ ty::ExistentialPredicate::Projection(_)
+ | ty::ExistentialPredicate::AutoTrait(_) => {}
+ }
+ }
+ }
+ // Point at the closure that couldn't satisfy the bound.
+ ty::Closure(def_id, _) => bound_spans.push((
+ tcx.def_span(*def_id),
+ format!("doesn't satisfy `{}`", quiet),
+ )),
+ _ => {}
+ }
+ };
+ let mut format_pred = |pred: ty::Predicate<'tcx>| {
+ let bound_predicate = pred.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Projection(pred) => {
+ let pred = bound_predicate.rebind(pred);
+ // `<Foo as Iterator>::Item = String`.
+ let projection_ty = pred.skip_binder().projection_ty;
+
+ let substs_with_infer_self = tcx.mk_substs(
+ iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+ .chain(projection_ty.substs.iter().skip(1)),
+ );
+
+ let quiet_projection_ty = ty::ProjectionTy {
+ substs: substs_with_infer_self,
+ item_def_id: projection_ty.item_def_id,
+ };
+
+ let term = pred.skip_binder().term;
+
+ let obligation = format!("{} = {}", projection_ty, term);
+ let quiet = format!("{} = {}", quiet_projection_ty, term);
+
+ bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
+ Some((obligation, projection_ty.self_ty()))
+ }
+ ty::PredicateKind::Trait(poly_trait_ref) => {
+ let p = poly_trait_ref.trait_ref;
+ let self_ty = p.self_ty();
+ let path = p.print_only_trait_path();
+ let obligation = format!("{}: {}", self_ty, path);
+ let quiet = format!("_: {}", path);
+ bound_span_label(self_ty, &obligation, &quiet);
+ Some((obligation, self_ty))
+ }
+ _ => None,
+ }
+ };
+
+ // Find all the requirements that come from a local `impl` block.
+ let mut skip_list: FxHashSet<_> = Default::default();
+ let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default();
+ for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates
+ .iter()
+ .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c)))
+ .filter_map(|(p, parent, c)| match c.code() {
+ ObligationCauseCode::ImplDerivedObligation(ref data) => {
+ Some((&data.derived, p, parent, data.impl_def_id, data))
+ }
+ _ => None,
+ })
+ {
+ let parent_trait_ref = data.parent_trait_pred;
+ let path = parent_trait_ref.print_modifiers_and_trait_path();
+ let tr_self_ty = parent_trait_ref.skip_binder().self_ty();
+ let unsatisfied_msg = "unsatisfied trait bound introduced here";
+ let derive_msg =
+ "unsatisfied trait bound introduced in this `derive` macro";
+ match self.tcx.hir().get_if_local(impl_def_id) {
+ // Unmet obligation comes from a `derive` macro, point at it once to
+ // avoid multiple span labels pointing at the same place.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(..),
+ ident,
+ ..
+ })) if matches!(
+ ident.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) =>
+ {
+ let span = ident.span.ctxt().outer_expn_data().call_site;
+ let mut spans: MultiSpan = span.into();
+ spans.push_span_label(span, derive_msg);
+ let entry = spanned_predicates.entry(spans);
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
+ ..
+ })) if matches!(
+ self_ty.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) || matches!(
+ of_trait.as_ref().map(|t| t
+ .path
+ .span
+ .ctxt()
+ .outer_expn_data()
+ .kind),
+ Some(ExpnKind::Macro(MacroKind::Derive, _))
+ ) =>
+ {
+ let span = self_ty.span.ctxt().outer_expn_data().call_site;
+ let mut spans: MultiSpan = span.into();
+ spans.push_span_label(span, derive_msg);
+ let entry = spanned_predicates.entry(spans);
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ // Unmet obligation coming from a `trait`.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(..),
+ ident,
+ span: item_span,
+ ..
+ })) if !matches!(
+ ident.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) =>
+ {
+ if let Some(pred) = parent_p {
+ // Done to add the "doesn't satisfy" `span_label`.
+ let _ = format_pred(*pred);
+ }
+ skip_list.insert(p);
+ let mut spans = if cause.span != *item_span {
+ let mut spans: MultiSpan = cause.span.into();
+ spans.push_span_label(cause.span, unsatisfied_msg);
+ spans
+ } else {
+ ident.span.into()
+ };
+ spans.push_span_label(ident.span, "in this trait");
+ let entry = spanned_predicates.entry(spans);
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ // Unmet obligation coming from an `impl`.
+ Some(Node::Item(hir::Item {
+ kind:
+ hir::ItemKind::Impl(hir::Impl {
+ of_trait, self_ty, generics, ..
+ }),
+ span: item_span,
+ ..
+ })) if !matches!(
+ self_ty.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) && !matches!(
+ of_trait.as_ref().map(|t| t
+ .path
+ .span
+ .ctxt()
+ .outer_expn_data()
+ .kind),
+ Some(ExpnKind::Macro(MacroKind::Derive, _))
+ ) =>
+ {
+ let sized_pred =
+ unsatisfied_predicates.iter().any(|(pred, _, _)| {
+ match pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => {
+ Some(pred.def_id())
+ == self.tcx.lang_items().sized_trait()
+ && pred.polarity == ty::ImplPolarity::Positive
+ }
+ _ => false,
+ }
+ });
+ for param in generics.params {
+ if param.span == cause.span && sized_pred {
+ let (sp, sugg) = match param.colon_span {
+ Some(sp) => (sp.shrink_to_hi(), " ?Sized +"),
+ None => (param.span.shrink_to_hi(), ": ?Sized"),
+ };
+ err.span_suggestion_verbose(
+ sp,
+ "consider relaxing the type parameter's implicit \
+ `Sized` bound",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ if let Some(pred) = parent_p {
+ // Done to add the "doesn't satisfy" `span_label`.
+ let _ = format_pred(*pred);
+ }
+ skip_list.insert(p);
+ let mut spans = if cause.span != *item_span {
+ let mut spans: MultiSpan = cause.span.into();
+ spans.push_span_label(cause.span, unsatisfied_msg);
+ spans
+ } else {
+ let mut spans = Vec::with_capacity(2);
+ if let Some(trait_ref) = of_trait {
+ spans.push(trait_ref.path.span);
+ }
+ spans.push(self_ty.span);
+ spans.into()
+ };
+ if let Some(trait_ref) = of_trait {
+ spans.push_span_label(trait_ref.path.span, "");
+ }
+ spans.push_span_label(self_ty.span, "");
+
+ let entry = spanned_predicates.entry(spans);
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+ _ => {}
+ }
+ }
+ let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
+ spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span());
+ for (span, (_path, _self_ty, preds)) in spanned_predicates {
+ let mut preds: Vec<_> = preds
+ .into_iter()
+ .filter_map(|pred| format_pred(*pred))
+ .map(|(p, _)| format!("`{}`", p))
+ .collect();
+ preds.sort();
+ preds.dedup();
+ let msg = if let [pred] = &preds[..] {
+ format!("trait bound {} was not satisfied", pred)
+ } else {
+ format!(
+ "the following trait bounds were not satisfied:\n{}",
+ preds.join("\n"),
+ )
+ };
+ err.span_note(span, &msg);
+ unsatisfied_bounds = true;
+ }
+
+ // The requirements that didn't have an `impl` span to show.
+ let mut bound_list = unsatisfied_predicates
+ .iter()
+ .filter_map(|(pred, parent_pred, _cause)| {
+ format_pred(*pred).map(|(p, self_ty)| {
+ collect_type_param_suggestions(self_ty, *pred, &p);
+ (
+ match parent_pred {
+ None => format!("`{}`", &p),
+ Some(parent_pred) => match format_pred(*parent_pred) {
+ None => format!("`{}`", &p),
+ Some((parent_p, _)) => {
+ collect_type_param_suggestions(
+ self_ty,
+ *parent_pred,
+ &p,
+ );
+ format!(
+ "`{}`\nwhich is required by `{}`",
+ p, parent_p
+ )
+ }
+ },
+ },
+ *pred,
+ )
+ })
+ })
+ .filter(|(_, pred)| !skip_list.contains(&pred))
+ .map(|(t, _)| t)
+ .enumerate()
+ .collect::<Vec<(usize, String)>>();
+
+ for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
+ restrict_type_params = true;
+ // #74886: Sort here so that the output is always the same.
+ let mut obligations = obligations.into_iter().collect::<Vec<_>>();
+ obligations.sort();
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "consider restricting the type parameter{s} to satisfy the \
+ trait bound{s}",
+ s = pluralize!(obligations.len())
+ ),
+ format!("{} {}", add_where_or_comma, obligations.join(", ")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
+ bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677
+ bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order.
+
+ if !bound_list.is_empty() || !skip_list.is_empty() {
+ let bound_list = bound_list
+ .into_iter()
+ .map(|(_, path)| path)
+ .collect::<Vec<_>>()
+ .join("\n");
+ let actual_prefix = actual.prefix_string(self.tcx);
+ info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
+ let (primary_message, label) =
+ if unimplemented_traits.len() == 1 && unimplemented_traits_only {
+ unimplemented_traits
+ .into_iter()
+ .next()
+ .map(|(_, (trait_ref, obligation))| {
+ if trait_ref.self_ty().references_error()
+ || actual.references_error()
+ {
+ // Avoid crashing.
+ return (None, None);
+ }
+ let OnUnimplementedNote { message, label, .. } = self
+ .err_ctxt()
+ .on_unimplemented_note(trait_ref, &obligation);
+ (message, label)
+ })
+ .unwrap_or((None, None))
+ } else {
+ (None, None)
+ };
+ let primary_message = primary_message.unwrap_or_else(|| format!(
+ "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
+ ));
+ err.set_primary_message(&primary_message);
+ if let Some(label) = label {
+ custom_span_label = true;
+ err.span_label(span, label);
+ }
+ if !bound_list.is_empty() {
+ err.note(&format!(
+ "the following trait bounds were not satisfied:\n{bound_list}"
+ ));
+ }
+ self.suggest_derive(&mut err, &unsatisfied_predicates);
+
+ unsatisfied_bounds = true;
+ }
+ }
+
+ let label_span_not_found = |err: &mut Diagnostic| {
+ if unsatisfied_predicates.is_empty() {
+ err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
+ let is_string_or_ref_str = match actual.kind() {
+ ty::Ref(_, ty, _) => {
+ ty.is_str()
+ || matches!(
+ ty.kind(),
+ ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did())
+ )
+ }
+ ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()),
+ _ => false,
+ };
+ if is_string_or_ref_str && item_name.name == sym::iter {
+ err.span_suggestion_verbose(
+ item_name.span,
+ "because of the in-memory representation of `&str`, to obtain \
+ an `Iterator` over each of its codepoint use method `chars`",
+ "chars",
+ Applicability::MachineApplicable,
+ );
+ }
+ if let ty::Adt(adt, _) = rcvr_ty.kind() {
+ let mut inherent_impls_candidate = self
+ .tcx
+ .inherent_impls(adt.did())
+ .iter()
+ .copied()
+ .filter(|def_id| {
+ if let Some(assoc) = self.associated_value(*def_id, item_name) {
+ // Check for both mode is the same so we avoid suggesting
+ // incorrect associated item.
+ match (mode, assoc.fn_has_self_parameter, source) {
+ (Mode::MethodCall, true, SelfSource::MethodCall(_)) => {
+ // We check that the suggest type is actually
+ // different from the received one
+ // So we avoid suggestion method with Box<Self>
+ // for instance
+ self.tcx.at(span).type_of(*def_id) != actual
+ && self.tcx.at(span).type_of(*def_id) != rcvr_ty
+ }
+ (Mode::Path, false, _) => true,
+ _ => false,
+ }
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>();
+ if !inherent_impls_candidate.is_empty() {
+ inherent_impls_candidate.sort();
+ inherent_impls_candidate.dedup();
+
+ // number of type to shows at most.
+ let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
+ let type_candidates = inherent_impls_candidate
+ .iter()
+ .take(limit)
+ .map(|impl_item| {
+ format!("- `{}`", self.tcx.at(span).type_of(*impl_item))
+ })
+ .collect::<Vec<_>>()
+ .join("\n");
+ let additional_types = if inherent_impls_candidate.len() > limit {
+ format!(
+ "\nand {} more types",
+ inherent_impls_candidate.len() - limit
+ )
+ } else {
+ "".to_string()
+ };
+ err.note(&format!(
+ "the {item_kind} was found for\n{}{}",
+ type_candidates, additional_types
+ ));
+ }
+ }
+ } else {
+ err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
+ }
+ };
+
+ // If the method name is the name of a field with a function or closure type,
+ // give a helping note that it has to be called as `(x.f)(...)`.
+ if let SelfSource::MethodCall(expr) = source {
+ if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err)
+ && lev_candidate.is_none()
+ && !custom_span_label
+ {
+ label_span_not_found(&mut err);
+ }
+ } else if !custom_span_label {
+ label_span_not_found(&mut err);
+ }
+
+ // Don't suggest (for example) `expr.field.method()` if `expr.method()`
+ // doesn't exist due to unsatisfied predicates.
+ if unsatisfied_predicates.is_empty() {
+ self.check_for_field_method(&mut err, source, span, actual, item_name);
+ }
+
+ self.check_for_inner_self(&mut err, source, span, actual, item_name);
+
+ bound_spans.sort();
+ bound_spans.dedup();
+ for (span, msg) in bound_spans.into_iter() {
+ err.span_label(span, &msg);
+ }
+
+ if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
+ } else {
+ self.suggest_traits_to_import(
+ &mut err,
+ span,
+ rcvr_ty,
+ item_name,
+ args.map(|(_, args)| args.len() + 1),
+ source,
+ out_of_scope_traits,
+ &unsatisfied_predicates,
+ &static_sources,
+ unsatisfied_bounds,
+ );
+ }
+
+ // Don't emit a suggestion if we found an actual method
+ // that had unsatisfied trait bounds
+ if unsatisfied_predicates.is_empty() && actual.is_enum() {
+ let adt_def = actual.ty_adt_def().expect("enum is not an ADT");
+ if let Some(suggestion) = lev_distance::find_best_match_for_name(
+ &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
+ item_name.name,
+ None,
+ ) {
+ err.span_suggestion(
+ span,
+ "there is a variant with a similar name",
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
+ if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+ let msg = "remove this method call";
+ let mut fallback_span = true;
+ if let SelfSource::MethodCall(expr) = source {
+ let call_expr =
+ self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+ if let Some(span) = call_expr.span.trim_start(expr.span) {
+ err.span_suggestion(span, msg, "", Applicability::MachineApplicable);
+ fallback_span = false;
+ }
+ }
+ if fallback_span {
+ err.span_label(span, msg);
+ }
+ } else if let Some(lev_candidate) = lev_candidate {
+ // Don't emit a suggestion if we found an actual method
+ // that had unsatisfied trait bounds
+ if unsatisfied_predicates.is_empty() {
+ let def_kind = lev_candidate.kind.as_def_kind();
+ // Methods are defined within the context of a struct and their first parameter is always self,
+ // which represents the instance of the struct the method is being called on
+ // Associated functions don’t take self as a parameter and
+ // they are not methods because they don’t have an instance of the struct to work with.
+ if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+ err.span_suggestion(
+ span,
+ &format!("there is a method with a similar name",),
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion(
+ span,
+ &format!(
+ "there is {} {} with a similar name",
+ def_kind.article(),
+ def_kind.descr(lev_candidate.def_id),
+ ),
+ lev_candidate.name,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ self.check_for_deref_method(&mut err, source, rcvr_ty, item_name);
+
+ return Some(err);
+ }
+
+ MethodError::Ambiguity(mut sources) => {
+ let mut err = struct_span_err!(
+ self.sess(),
+ item_name.span,
+ E0034,
+ "multiple applicable items in scope"
+ );
+ err.span_label(item_name.span, format!("multiple `{}` found", item_name));
+
+ report_candidates(span, &mut err, &mut sources, sugg_span);
+ err.emit();
+ }
+
+ MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
+ let kind = kind.descr(def_id);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ item_name.span,
+ E0624,
+ "{} `{}` is private",
+ kind,
+ item_name
+ );
+ err.span_label(item_name.span, &format!("private {}", kind));
+ let sp = self
+ .tcx
+ .hir()
+ .span_if_local(def_id)
+ .unwrap_or_else(|| self.tcx.def_span(def_id));
+ err.span_label(sp, &format!("private {} defined here", kind));
+ self.suggest_valid_traits(&mut err, out_of_scope_traits);
+ err.emit();
+ }
+
+ MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => {
+ let msg = format!("the `{}` method cannot be invoked on a trait object", item_name);
+ let mut err = self.sess().struct_span_err(span, &msg);
+ err.span_label(bound_span, "this has a `Sized` requirement");
+ if !candidates.is_empty() {
+ let help = format!(
+ "{an}other candidate{s} {were} found in the following trait{s}, perhaps \
+ add a `use` for {one_of_them}:",
+ an = if candidates.len() == 1 { "an" } else { "" },
+ s = pluralize!(candidates.len()),
+ were = pluralize!("was", candidates.len()),
+ one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
+ );
+ self.suggest_use_candidates(&mut err, help, candidates);
+ }
+ if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
+ if needs_mut {
+ let trait_type = self.tcx.mk_ref(
+ *region,
+ ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
+ );
+ err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty));
+ }
+ }
+ err.emit();
+ }
+
+ MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
+ }
+ None
+ }
+
+ fn suggest_field_call(
+ &self,
+ span: Span,
+ rcvr_ty: Ty<'tcx>,
+ expr: &hir::Expr<'_>,
+ item_name: Ident,
+ err: &mut Diagnostic,
+ ) -> bool {
+ let tcx = self.tcx;
+ let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
+ ty::Adt(def, substs) if !def.is_enum() => {
+ let variant = &def.non_enum_variant();
+ tcx.find_field_index(item_name, variant).map(|index| {
+ let field = &variant.fields[index];
+ let field_ty = field.ty(tcx, substs);
+ (field, field_ty)
+ })
+ }
+ _ => None,
+ });
+ if let Some((field, field_ty)) = field_receiver {
+ let scope = tcx.parent_module(self.body_id);
+ let is_accessible = field.vis.is_accessible_from(scope, tcx);
+
+ if is_accessible {
+ if self.is_fn_ty(field_ty, span) {
+ let expr_span = expr.span.to(item_name.span);
+ err.multipart_suggestion(
+ &format!(
+ "to call the function stored in `{}`, \
+ surround the field access with parentheses",
+ item_name,
+ ),
+ vec![
+ (expr_span.shrink_to_lo(), '('.to_string()),
+ (expr_span.shrink_to_hi(), ')'.to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+
+ if let Some(span) = call_expr.span.trim_start(item_name.span) {
+ err.span_suggestion(
+ span,
+ "remove the arguments",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ let field_kind = if is_accessible { "field" } else { "private field" };
+ err.span_label(item_name.span, format!("{}, not a method", field_kind));
+ return true;
+ }
+ false
+ }
+
+ /// Suggest possible range with adding parentheses, for example:
+ /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
+ fn suggest_wrapping_range_with_parens(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ actual: Ty<'tcx>,
+ source: SelfSource<'tcx>,
+ span: Span,
+ item_name: Ident,
+ ty_str: &str,
+ ) -> bool {
+ if let SelfSource::MethodCall(expr) = source {
+ for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
+ if let Node::Expr(parent_expr) = parent {
+ let lang_item = match parent_expr.kind {
+ ExprKind::Struct(ref qpath, _, _) => match **qpath {
+ QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
+ QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
+ QPath::LangItem(LangItem::RangeToInclusive, ..) => {
+ Some(LangItem::RangeToInclusive)
+ }
+ _ => None,
+ },
+ ExprKind::Call(ref func, _) => match func.kind {
+ // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+ ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => {
+ Some(LangItem::RangeInclusiveStruct)
+ }
+ _ => None,
+ },
+ _ => None,
+ };
+
+ if lang_item.is_none() {
+ continue;
+ }
+
+ let span_included = match parent_expr.kind {
+ hir::ExprKind::Struct(_, eps, _) => {
+ eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span))
+ }
+ // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+ hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
+ _ => false,
+ };
+
+ if !span_included {
+ continue;
+ }
+
+ let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None);
+ let range_ty =
+ self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
+
+ let pick = self.probe_for_name(
+ span,
+ Mode::MethodCall,
+ item_name,
+ IsSuggestion(true),
+ range_ty,
+ expr.hir_id,
+ ProbeScope::AllTraits,
+ );
+ if pick.is_ok() {
+ let range_span = parent_expr.span.with_hi(expr.span.hi());
+ tcx.sess.emit_err(errors::MissingParentheseInRange {
+ span,
+ ty_str: ty_str.to_string(),
+ method_name: item_name.as_str().to_string(),
+ add_missing_parentheses: Some(errors::AddMissingParenthesesInRange {
+ func_name: item_name.name.as_str().to_string(),
+ left: range_span.shrink_to_lo(),
+ right: range_span.shrink_to_hi(),
+ }),
+ });
+ return true;
+ }
+ }
+ }
+ }
+ false
+ }
+
+ fn suggest_constraining_numerical_ty(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ actual: Ty<'tcx>,
+ source: SelfSource<'_>,
+ span: Span,
+ item_kind: &str,
+ item_name: Ident,
+ ty_str: &str,
+ ) -> bool {
+ let found_candidate = all_traits(self.tcx)
+ .into_iter()
+ .any(|info| self.associated_value(info.def_id, item_name).is_some());
+ let found_assoc = |ty: Ty<'tcx>| {
+ simplify_type(tcx, ty, TreatParams::AsInfer)
+ .and_then(|simp| {
+ tcx.incoherent_impls(simp)
+ .iter()
+ .find_map(|&id| self.associated_value(id, item_name))
+ })
+ .is_some()
+ };
+ let found_candidate = found_candidate
+ || found_assoc(tcx.types.i8)
+ || found_assoc(tcx.types.i16)
+ || found_assoc(tcx.types.i32)
+ || found_assoc(tcx.types.i64)
+ || found_assoc(tcx.types.i128)
+ || found_assoc(tcx.types.u8)
+ || found_assoc(tcx.types.u16)
+ || found_assoc(tcx.types.u32)
+ || found_assoc(tcx.types.u64)
+ || found_assoc(tcx.types.u128)
+ || found_assoc(tcx.types.f32)
+ || found_assoc(tcx.types.f32);
+ if found_candidate
+ && actual.is_numeric()
+ && !actual.has_concrete_skeleton()
+ && let SelfSource::MethodCall(expr) = source
+ {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0689,
+ "can't call {} `{}` on ambiguous numeric type `{}`",
+ item_kind,
+ item_name,
+ ty_str
+ );
+ let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
+ match expr.kind {
+ ExprKind::Lit(ref lit) => {
+ // numeric literal
+ let snippet = tcx
+ .sess
+ .source_map()
+ .span_to_snippet(lit.span)
+ .unwrap_or_else(|_| "<numeric literal>".to_owned());
+
+ // If this is a floating point literal that ends with '.',
+ // get rid of it to stop this from becoming a member access.
+ let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
+ err.span_suggestion(
+ lit.span,
+ &format!(
+ "you must specify a concrete type for this numeric value, \
+ like `{}`",
+ concrete_type
+ ),
+ format!("{snippet}_{concrete_type}"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ExprKind::Path(QPath::Resolved(_, path)) => {
+ // local binding
+ if let hir::def::Res::Local(hir_id) = path.res {
+ let span = tcx.hir().span(hir_id);
+ let filename = tcx.sess.source_map().span_to_filename(span);
+
+ let parent_node =
+ self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+ let msg = format!(
+ "you must specify a type for this binding, like `{}`",
+ concrete_type,
+ );
+
+ match (filename, parent_node) {
+ (
+ FileName::Real(_),
+ Node::Local(hir::Local {
+ source: hir::LocalSource::Normal,
+ ty,
+ ..
+ }),
+ ) => {
+ let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi());
+ err.span_suggestion(
+ // account for `let x: _ = 42;`
+ // ^^^
+ type_span,
+ &msg,
+ format!(": {concrete_type}"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ err.span_label(span, msg);
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ err.emit();
+ return true;
+ }
+ false
+ }
+
+ fn check_for_field_method(
+ &self,
+ err: &mut Diagnostic,
+ source: SelfSource<'tcx>,
+ span: Span,
+ actual: Ty<'tcx>,
+ item_name: Ident,
+ ) {
+ if let SelfSource::MethodCall(expr) = source
+ && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
+ && let Some((fields, substs)) =
+ self.get_field_candidates_considering_privacy(span, actual, mod_id)
+ {
+ let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+
+ let lang_items = self.tcx.lang_items();
+ let never_mention_traits = [
+ lang_items.clone_trait(),
+ lang_items.deref_trait(),
+ lang_items.deref_mut_trait(),
+ self.tcx.get_diagnostic_item(sym::AsRef),
+ self.tcx.get_diagnostic_item(sym::AsMut),
+ self.tcx.get_diagnostic_item(sym::Borrow),
+ self.tcx.get_diagnostic_item(sym::BorrowMut),
+ ];
+ let candidate_fields: Vec<_> = fields
+ .filter_map(|candidate_field| {
+ self.check_for_nested_field_satisfying(
+ span,
+ &|_, field_ty| {
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ )
+ .map_or(false, |pick| {
+ !never_mention_traits
+ .iter()
+ .flatten()
+ .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
+ })
+ },
+ candidate_field,
+ substs,
+ vec![],
+ mod_id,
+ )
+ })
+ .map(|field_path| {
+ field_path
+ .iter()
+ .map(|id| id.name.to_ident_string())
+ .collect::<Vec<String>>()
+ .join(".")
+ })
+ .collect();
+
+ let len = candidate_fields.len();
+ if len > 0 {
+ err.span_suggestions(
+ item_name.span.shrink_to_lo(),
+ format!(
+ "{} of the expressions' fields {} a method of the same name",
+ if len > 1 { "some" } else { "one" },
+ if len > 1 { "have" } else { "has" },
+ ),
+ candidate_fields.iter().map(|path| format!("{path}.")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ fn check_for_inner_self(
+ &self,
+ err: &mut Diagnostic,
+ source: SelfSource<'tcx>,
+ span: Span,
+ actual: Ty<'tcx>,
+ item_name: Ident,
+ ) {
+ let tcx = self.tcx;
+ let SelfSource::MethodCall(expr) = source else { return; };
+ let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+
+ let ty::Adt(kind, substs) = actual.kind() else { return; };
+ match kind.adt_kind() {
+ ty::AdtKind::Enum => {
+ let matching_variants: Vec<_> = kind
+ .variants()
+ .iter()
+ .flat_map(|variant| {
+ let [field] = &variant.fields[..] else { return None; };
+ let field_ty = field.ty(tcx, substs);
+
+ // Skip `_`, since that'll just lead to ambiguity.
+ if self.resolve_vars_if_possible(field_ty).is_ty_var() {
+ return None;
+ }
+
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ )
+ .ok()
+ .map(|pick| (variant, field, pick))
+ })
+ .collect();
+
+ let ret_ty_matches = |diagnostic_item| {
+ if let Some(ret_ty) = self
+ .ret_coercion
+ .as_ref()
+ .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
+ && let ty::Adt(kind, _) = ret_ty.kind()
+ && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
+ {
+ true
+ } else {
+ false
+ }
+ };
+
+ match &matching_variants[..] {
+ [(_, field, pick)] => {
+ let self_ty = field.ty(tcx, substs);
+ err.span_note(
+ tcx.def_span(pick.item.def_id),
+ &format!("the method `{item_name}` exists on the type `{self_ty}`"),
+ );
+ let (article, kind, variant, question) =
+ if tcx.is_diagnostic_item(sym::Result, kind.did()) {
+ ("a", "Result", "Err", ret_ty_matches(sym::Result))
+ } else if tcx.is_diagnostic_item(sym::Option, kind.did()) {
+ ("an", "Option", "None", ret_ty_matches(sym::Option))
+ } else {
+ return;
+ };
+ if question {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use the `?` operator to extract the `{self_ty}` value, propagating \
+ {article} `{kind}::{variant}` value to the caller"
+ ),
+ "?",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
+ panicking if the value is {article} `{kind}::{variant}`"
+ ),
+ ".expect(\"REASON\")",
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ // FIXME(compiler-errors): Support suggestions for other matching enum variants
+ _ => {}
+ }
+ }
+ // Target wrapper types - types that wrap or pretend to wrap another type,
+ // perhaps this inner type is meant to be called?
+ ty::AdtKind::Struct | ty::AdtKind::Union => {
+ let [first] = ***substs else { return; };
+ let ty::GenericArgKind::Type(ty) = first.unpack() else { return; };
+ let Ok(pick) = self.lookup_probe(
+ span,
+ item_name,
+ ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ ) else { return; };
+
+ let name = self.ty_to_value_string(actual);
+ let inner_id = kind.did();
+ let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) =
+ pick.autoref_or_ptr_adjustment
+ {
+ Some(mutbl)
+ } else {
+ None
+ };
+
+ if tcx.is_diagnostic_item(sym::LocalKey, inner_id) {
+ err.help("use `with` or `try_with` to access thread local storage");
+ } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() {
+ err.help(format!(
+ "if this `{name}` has been initialized, \
+ use one of the `assume_init` methods to access the inner value"
+ ));
+ } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) {
+ let (suggestion, borrow_kind, panic_if) = match mutable {
+ Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"),
+ Some(Mutability::Mut) => {
+ (".borrow_mut()", "mutably borrow", "any borrows exist")
+ }
+ None => return,
+ };
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `{suggestion}` to {borrow_kind} the `{ty}`, \
+ panicking if {panic_if}"
+ ),
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `.lock().unwrap()` to borrow the `{ty}`, \
+ blocking the current thread until it can be acquired"
+ ),
+ ".lock().unwrap()",
+ Applicability::MaybeIncorrect,
+ );
+ } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) {
+ let (suggestion, borrow_kind) = match mutable {
+ Some(Mutability::Not) => (".read().unwrap()", "borrow"),
+ Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"),
+ None => return,
+ };
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `{suggestion}` to {borrow_kind} the `{ty}`, \
+ blocking the current thread until it can be acquired"
+ ),
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ return;
+ };
+
+ err.span_note(
+ tcx.def_span(pick.item.def_id),
+ &format!("the method `{item_name}` exists on the type `{ty}`"),
+ );
+ }
+ }
+ }
+
+ pub(crate) fn note_unmet_impls_on_type(
+ &self,
+ err: &mut Diagnostic,
+ errors: Vec<FulfillmentError<'tcx>>,
+ ) {
+ let all_local_types_needing_impls =
+ errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() {
+ ty::Adt(def, _) => def.did().is_local(),
+ _ => false,
+ },
+ _ => false,
+ });
+ let mut preds: Vec<_> = errors
+ .iter()
+ .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => Some(pred),
+ _ => None,
+ })
+ .collect();
+ preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty()));
+ let def_ids = preds
+ .iter()
+ .filter_map(|pred| match pred.self_ty().kind() {
+ ty::Adt(def, _) => Some(def.did()),
+ _ => None,
+ })
+ .collect::<FxHashSet<_>>();
+ let mut spans: MultiSpan = def_ids
+ .iter()
+ .filter_map(|def_id| {
+ let span = self.tcx.def_span(*def_id);
+ if span.is_dummy() { None } else { Some(span) }
+ })
+ .collect::<Vec<_>>()
+ .into();
+
+ for pred in &preds {
+ match pred.self_ty().kind() {
+ ty::Adt(def, _) if def.did().is_local() => {
+ spans.push_span_label(
+ self.tcx.def_span(def.did()),
+ format!("must implement `{}`", pred.trait_ref.print_only_trait_path()),
+ );
+ }
+ _ => {}
+ }
+ }
+
+ if all_local_types_needing_impls && spans.primary_span().is_some() {
+ let msg = if preds.len() == 1 {
+ format!(
+ "an implementation of `{}` might be missing for `{}`",
+ preds[0].trait_ref.print_only_trait_path(),
+ preds[0].self_ty()
+ )
+ } else {
+ format!(
+ "the following type{} would have to `impl` {} required trait{} for this \
+ operation to be valid",
+ pluralize!(def_ids.len()),
+ if def_ids.len() == 1 { "its" } else { "their" },
+ pluralize!(preds.len()),
+ )
+ };
+ err.span_note(spans, &msg);
+ }
+
+ let preds: Vec<_> = errors
+ .iter()
+ .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone())))
+ .collect();
+ self.suggest_derive(err, &preds);
+ }
+
+ fn suggest_derive(
+ &self,
+ err: &mut Diagnostic,
+ unsatisfied_predicates: &[(
+ ty::Predicate<'tcx>,
+ Option<ty::Predicate<'tcx>>,
+ Option<ObligationCause<'tcx>>,
+ )],
+ ) {
+ let mut derives = Vec::<(String, Span, Symbol)>::new();
+ let mut traits = Vec::<Span>::new();
+ for (pred, _, _) in unsatisfied_predicates {
+ let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
+ let adt = match trait_pred.self_ty().ty_adt_def() {
+ Some(adt) if adt.did().is_local() => adt,
+ _ => continue,
+ };
+ if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
+ let can_derive = match diagnostic_name {
+ sym::Default => !adt.is_enum(),
+ sym::Eq
+ | sym::PartialEq
+ | sym::Ord
+ | sym::PartialOrd
+ | sym::Clone
+ | sym::Copy
+ | sym::Hash
+ | sym::Debug => true,
+ _ => false,
+ };
+ if can_derive {
+ let self_name = trait_pred.self_ty().to_string();
+ let self_span = self.tcx.def_span(adt.did());
+ if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() {
+ for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref())
+ {
+ if let Some(parent_diagnostic_name) =
+ self.tcx.get_diagnostic_name(super_trait.def_id())
+ {
+ derives.push((
+ self_name.clone(),
+ self_span,
+ parent_diagnostic_name,
+ ));
+ }
+ }
+ }
+ derives.push((self_name, self_span, diagnostic_name));
+ } else {
+ traits.push(self.tcx.def_span(trait_pred.def_id()));
+ }
+ } else {
+ traits.push(self.tcx.def_span(trait_pred.def_id()));
+ }
+ }
+ traits.sort();
+ traits.dedup();
+
+ derives.sort();
+ derives.dedup();
+
+ let mut derives_grouped = Vec::<(String, Span, String)>::new();
+ for (self_name, self_span, trait_name) in derives.into_iter() {
+ if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut()
+ {
+ if last_self_name == &self_name {
+ last_trait_names.push_str(format!(", {}", trait_name).as_str());
+ continue;
+ }
+ }
+ derives_grouped.push((self_name, self_span, trait_name.to_string()));
+ }
+
+ let len = traits.len();
+ if len > 0 {
+ let span: MultiSpan = traits.into();
+ err.span_note(
+ span,
+ &format!("the following trait{} must be implemented", pluralize!(len),),
+ );
+ }
+
+ for (self_name, self_span, traits) in &derives_grouped {
+ err.span_suggestion_verbose(
+ self_span.shrink_to_lo(),
+ &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits),
+ format!("#[derive({})]\n", traits),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
+ 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() {
+ ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)),
+ _ => self.ty_to_string(ty),
+ }
+ }
+
+ fn suggest_await_before_method(
+ &self,
+ err: &mut Diagnostic,
+ item_name: Ident,
+ ty: Ty<'tcx>,
+ call: &hir::Expr<'_>,
+ span: Span,
+ ) {
+ let output_ty = match self.get_impl_future_output_ty(ty) {
+ Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(),
+ _ => return,
+ };
+ let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
+ debug!("suggest_await_before_method: is_method_exist={}", method_exists);
+ if method_exists {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ "consider `await`ing on the `Future` and calling the method on its `Output`",
+ "await.",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
+ fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) {
+ let parent_map = self.tcx.visible_parent_map(());
+
+ // Separate out candidates that must be imported with a glob, because they are named `_`
+ // and cannot be referred with their identifier.
+ let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
+ if let Some(parent_did) = parent_map.get(trait_did) {
+ // If the item is re-exported as `_`, we should suggest a glob-import instead.
+ if *parent_did != self.tcx.parent(*trait_did)
+ && self
+ .tcx
+ .module_children(*parent_did)
+ .iter()
+ .filter(|child| child.res.opt_def_id() == Some(*trait_did))
+ .all(|child| child.ident.name == kw::Underscore)
+ {
+ return false;
+ }
+ }
+
+ true
+ });
+
+ let module_did = self.tcx.parent_module(self.body_id);
+ let (module, _, _) = self.tcx.hir().get_module(module_did);
+ let span = module.spans.inject_use_span;
+
+ let path_strings = candidates.iter().map(|trait_did| {
+ format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
+ });
+
+ let glob_path_strings = globs.iter().map(|trait_did| {
+ let parent_did = parent_map.get(trait_did).unwrap();
+ format!(
+ "use {}::*; // trait {}\n",
+ with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
+ self.tcx.item_name(*trait_did),
+ )
+ });
+
+ err.span_suggestions(
+ span,
+ &msg,
+ path_strings.chain(glob_path_strings),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ fn suggest_valid_traits(
+ &self,
+ err: &mut Diagnostic,
+ valid_out_of_scope_traits: Vec<DefId>,
+ ) -> bool {
+ if !valid_out_of_scope_traits.is_empty() {
+ let mut candidates = valid_out_of_scope_traits;
+ candidates.sort();
+ candidates.dedup();
+
+ // `TryFrom` and `FromIterator` have no methods
+ let edition_fix = candidates
+ .iter()
+ .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did))
+ .copied();
+
+ err.help("items from traits can only be used if the trait is in scope");
+ let msg = format!(
+ "the following {traits_are} implemented but not in scope; \
+ perhaps add a `use` for {one_of_them}:",
+ traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" },
+ one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
+ );
+
+ self.suggest_use_candidates(err, msg, candidates);
+ if let Some(did) = edition_fix {
+ err.note(&format!(
+ "'{}' is included in the prelude starting in Edition 2021",
+ with_crate_prefix!(self.tcx.def_path_str(did))
+ ));
+ }
+
+ true
+ } else {
+ false
+ }
+ }
+
+ fn suggest_traits_to_import(
+ &self,
+ err: &mut Diagnostic,
+ span: Span,
+ rcvr_ty: Ty<'tcx>,
+ item_name: Ident,
+ inputs_len: Option<usize>,
+ source: SelfSource<'tcx>,
+ valid_out_of_scope_traits: Vec<DefId>,
+ unsatisfied_predicates: &[(
+ ty::Predicate<'tcx>,
+ Option<ty::Predicate<'tcx>>,
+ Option<ObligationCause<'tcx>>,
+ )],
+ static_candidates: &[CandidateSource],
+ unsatisfied_bounds: bool,
+ ) {
+ let mut alt_rcvr_sugg = false;
+ if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
+ debug!(?span, ?item_name, ?rcvr_ty, ?rcvr);
+ let skippable = [
+ self.tcx.lang_items().clone_trait(),
+ self.tcx.lang_items().deref_trait(),
+ self.tcx.lang_items().deref_mut_trait(),
+ self.tcx.lang_items().drop_trait(),
+ self.tcx.get_diagnostic_item(sym::AsRef),
+ ];
+ // Try alternative arbitrary self types that could fulfill this call.
+ // FIXME: probe for all types that *could* be arbitrary self-types, not
+ // just this list.
+ for (rcvr_ty, post) in &[
+ (rcvr_ty, ""),
+ (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
+ (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
+ ] {
+ match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
+ Ok(pick) => {
+ // If the method is defined for the receiver we have, it likely wasn't `use`d.
+ // We point at the method, but we just skip the rest of the check for arbitrary
+ // self types and rely on the suggestion to `use` the trait from
+ // `suggest_valid_traits`.
+ let did = Some(pick.item.container_id(self.tcx));
+ let skip = skippable.contains(&did);
+ if pick.autoderefs == 0 && !skip {
+ err.span_label(
+ pick.item.ident(self.tcx).span,
+ &format!("the method is available for `{}` here", rcvr_ty),
+ );
+ }
+ break;
+ }
+ Err(MethodError::Ambiguity(_)) => {
+ // If the method is defined (but ambiguous) for the receiver we have, it is also
+ // likely we haven't `use`d it. It may be possible that if we `Box`/`Pin`/etc.
+ // the receiver, then it might disambiguate this method, but I think these
+ // suggestions are generally misleading (see #94218).
+ break;
+ }
+ _ => {}
+ }
+
+ for (rcvr_ty, pre) in &[
+ (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"),
+ (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"),
+ (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
+ (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
+ ] {
+ if let Some(new_rcvr_t) = *rcvr_ty
+ && let Ok(pick) = self.lookup_probe(
+ span,
+ item_name,
+ new_rcvr_t,
+ rcvr,
+ ProbeScope::AllTraits,
+ )
+ {
+ debug!("try_alt_rcvr: pick candidate {:?}", pick);
+ let did = Some(pick.item.container_id(self.tcx));
+ // We don't want to suggest a container type when the missing
+ // method is `.clone()` or `.deref()` otherwise we'd suggest
+ // `Arc::new(foo).clone()`, which is far from what the user wants.
+ // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not
+ // implement the `AsRef` trait.
+ let skip = skippable.contains(&did)
+ || (("Pin::new" == *pre) && (sym::as_ref == item_name.name))
+ || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len);
+ // Make sure the method is defined for the *actual* receiver: we don't
+ // want to treat `Box<Self>` as a receiver if it only works because of
+ // an autoderef to `&self`
+ if pick.autoderefs == 0 && !skip {
+ err.span_label(
+ pick.item.ident(self.tcx).span,
+ &format!("the method is available for `{}` here", new_rcvr_t),
+ );
+ err.multipart_suggestion(
+ "consider wrapping the receiver expression with the \
+ appropriate type",
+ vec![
+ (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
+ (rcvr.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ // We don't care about the other suggestions.
+ alt_rcvr_sugg = true;
+ }
+ }
+ }
+ }
+ }
+ if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
+ return;
+ }
+
+ let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source);
+
+ let mut arbitrary_rcvr = vec![];
+ // There are no traits implemented, so lets suggest some traits to
+ // implement, by finding ones that have the item name, and are
+ // legal to implement.
+ let mut candidates = all_traits(self.tcx)
+ .into_iter()
+ // Don't issue suggestions for unstable traits since they're
+ // unlikely to be implementable anyway
+ .filter(|info| match self.tcx.lookup_stability(info.def_id) {
+ Some(attr) => attr.level.is_stable(),
+ None => true,
+ })
+ .filter(|info| {
+ // Static candidates are already implemented, and known not to work
+ // Do not suggest them again
+ static_candidates.iter().all(|sc| match *sc {
+ CandidateSource::Trait(def_id) => def_id != info.def_id,
+ CandidateSource::Impl(def_id) => {
+ self.tcx.trait_id_of_impl(def_id) != Some(info.def_id)
+ }
+ })
+ })
+ .filter(|info| {
+ // We approximate the coherence rules to only suggest
+ // traits that are legal to implement by requiring that
+ // either the type or trait is local. Multi-dispatch means
+ // this isn't perfect (that is, there are cases when
+ // implementing a trait would be legal but is rejected
+ // here).
+ unsatisfied_predicates.iter().all(|(p, _, _)| {
+ match p.kind().skip_binder() {
+ // Hide traits if they are present in predicates as they can be fixed without
+ // having to implement them.
+ ty::PredicateKind::Trait(t) => t.def_id() == info.def_id,
+ ty::PredicateKind::Projection(p) => {
+ p.projection_ty.item_def_id == info.def_id
+ }
+ _ => false,
+ }
+ }) && (type_is_local || info.def_id.is_local())
+ && self
+ .associated_value(info.def_id, item_name)
+ .filter(|item| {
+ if let ty::AssocKind::Fn = item.kind {
+ let id = item
+ .def_id
+ .as_local()
+ .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id));
+ if let Some(hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(fn_sig, method),
+ ..
+ })) = id.map(|id| self.tcx.hir().get(id))
+ {
+ let self_first_arg = match method {
+ hir::TraitFn::Required([ident, ..]) => {
+ ident.name == kw::SelfLower
+ }
+ hir::TraitFn::Provided(body_id) => {
+ self.tcx.hir().body(*body_id).params.first().map_or(
+ false,
+ |param| {
+ matches!(
+ param.pat.kind,
+ hir::PatKind::Binding(_, _, ident, _)
+ if ident.name == kw::SelfLower
+ )
+ },
+ )
+ }
+ _ => false,
+ };
+
+ if !fn_sig.decl.implicit_self.has_implicit_self()
+ && self_first_arg
+ {
+ if let Some(ty) = fn_sig.decl.inputs.get(0) {
+ arbitrary_rcvr.push(ty.span);
+ }
+ return false;
+ }
+ }
+ }
+ // We only want to suggest public or local traits (#45781).
+ item.visibility(self.tcx).is_public() || info.def_id.is_local()
+ })
+ .is_some()
+ })
+ .collect::<Vec<_>>();
+ for span in &arbitrary_rcvr {
+ err.span_label(
+ *span,
+ "the method might not be found because of this arbitrary self type",
+ );
+ }
+ if alt_rcvr_sugg {
+ return;
+ }
+
+ if !candidates.is_empty() {
+ // Sort from most relevant to least relevant.
+ candidates.sort_by(|a, b| a.cmp(b).reverse());
+ candidates.dedup();
+
+ let param_type = match rcvr_ty.kind() {
+ ty::Param(param) => Some(param),
+ ty::Ref(_, ty, _) => match ty.kind() {
+ ty::Param(param) => Some(param),
+ _ => None,
+ },
+ _ => None,
+ };
+ err.help(if param_type.is_some() {
+ "items from traits can only be used if the type parameter is bounded by the trait"
+ } else {
+ "items from traits can only be used if the trait is implemented and in scope"
+ });
+ let candidates_len = candidates.len();
+ let message = |action| {
+ format!(
+ "the following {traits_define} an item `{name}`, perhaps you need to {action} \
+ {one_of_them}:",
+ traits_define =
+ if candidates_len == 1 { "trait defines" } else { "traits define" },
+ action = action,
+ one_of_them = if candidates_len == 1 { "it" } else { "one of them" },
+ name = item_name,
+ )
+ };
+ // Obtain the span for `param` and use it for a structured suggestion.
+ if let Some(param) = param_type {
+ let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let type_param = generics.type_param(param, self.tcx);
+ let hir = self.tcx.hir();
+ if let Some(def_id) = type_param.def_id.as_local() {
+ let id = hir.local_def_id_to_hir_id(def_id);
+ // Get the `hir::Param` to verify whether it already has any bounds.
+ // We do this to avoid suggesting code that ends up as `T: FooBar`,
+ // instead we suggest `T: Foo + Bar` in that case.
+ match hir.get(id) {
+ Node::GenericParam(param) => {
+ enum Introducer {
+ Plus,
+ Colon,
+ Nothing,
+ }
+ let ast_generics = hir.get_generics(id.owner.def_id).unwrap();
+ let (sp, mut introducer) = if let Some(span) =
+ ast_generics.bounds_span_for_suggestions(def_id)
+ {
+ (span, Introducer::Plus)
+ } else if let Some(colon_span) = param.colon_span {
+ (colon_span.shrink_to_hi(), Introducer::Nothing)
+ } else {
+ (param.span.shrink_to_hi(), Introducer::Colon)
+ };
+ if matches!(
+ param.kind,
+ hir::GenericParamKind::Type { synthetic: true, .. },
+ ) {
+ introducer = Introducer::Plus
+ }
+ let trait_def_ids: FxHashSet<DefId> = ast_generics
+ .bounds_for_param(def_id)
+ .flat_map(|bp| bp.bounds.iter())
+ .filter_map(|bound| bound.trait_ref()?.trait_def_id())
+ .collect();
+ if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
+ err.span_suggestions(
+ sp,
+ &message(format!(
+ "restrict type parameter `{}` with",
+ param.name.ident(),
+ )),
+ candidates.iter().map(|t| {
+ format!(
+ "{} {}",
+ match introducer {
+ Introducer::Plus => " +",
+ Introducer::Colon => ":",
+ Introducer::Nothing => "",
+ },
+ self.tcx.def_path_str(t.def_id),
+ )
+ }),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ return;
+ }
+ Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(.., bounds, _),
+ ident,
+ ..
+ }) => {
+ let (sp, sep, article) = if bounds.is_empty() {
+ (ident.span.shrink_to_hi(), ":", "a")
+ } else {
+ (bounds.last().unwrap().span().shrink_to_hi(), " +", "another")
+ };
+ err.span_suggestions(
+ sp,
+ &message(format!("add {} supertrait for", article)),
+ candidates.iter().map(|t| {
+ format!("{} {}", sep, self.tcx.def_path_str(t.def_id),)
+ }),
+ Applicability::MaybeIncorrect,
+ );
+ return;
+ }
+ _ => {}
+ }
+ }
+ }
+
+ let (potential_candidates, explicitly_negative) = if param_type.is_some() {
+ // FIXME: Even though negative bounds are not implemented, we could maybe handle
+ // cases where a positive bound implies a negative impl.
+ (candidates, Vec::new())
+ } else if let Some(simp_rcvr_ty) =
+ simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder)
+ {
+ let mut potential_candidates = Vec::new();
+ let mut explicitly_negative = Vec::new();
+ for candidate in candidates {
+ // Check if there's a negative impl of `candidate` for `rcvr_ty`
+ if self
+ .tcx
+ .all_impls(candidate.def_id)
+ .filter(|imp_did| {
+ self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
+ })
+ .any(|imp_did| {
+ let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
+ let imp_simp =
+ simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder);
+ imp_simp.map_or(false, |s| s == simp_rcvr_ty)
+ })
+ {
+ explicitly_negative.push(candidate);
+ } else {
+ potential_candidates.push(candidate);
+ }
+ }
+ (potential_candidates, explicitly_negative)
+ } else {
+ // We don't know enough about `recv_ty` to make proper suggestions.
+ (candidates, Vec::new())
+ };
+
+ let action = if let Some(param) = param_type {
+ format!("restrict type parameter `{}` with", param)
+ } else {
+ // FIXME: it might only need to be imported into scope, not implemented.
+ "implement".to_string()
+ };
+ match &potential_candidates[..] {
+ [] => {}
+ [trait_info] if trait_info.def_id.is_local() => {
+ err.span_note(
+ self.tcx.def_span(trait_info.def_id),
+ &format!(
+ "`{}` defines an item `{}`, perhaps you need to {} it",
+ self.tcx.def_path_str(trait_info.def_id),
+ item_name,
+ action
+ ),
+ );
+ }
+ trait_infos => {
+ let mut msg = message(action);
+ for (i, trait_info) in trait_infos.iter().enumerate() {
+ msg.push_str(&format!(
+ "\ncandidate #{}: `{}`",
+ i + 1,
+ self.tcx.def_path_str(trait_info.def_id),
+ ));
+ }
+ err.note(&msg);
+ }
+ }
+ match &explicitly_negative[..] {
+ [] => {}
+ [trait_info] => {
+ let msg = format!(
+ "the trait `{}` defines an item `{}`, but is explicitly unimplemented",
+ self.tcx.def_path_str(trait_info.def_id),
+ item_name
+ );
+ err.note(&msg);
+ }
+ trait_infos => {
+ let mut msg = format!(
+ "the following traits define an item `{}`, but are explicitly unimplemented:",
+ item_name
+ );
+ for trait_info in trait_infos {
+ msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id)));
+ }
+ err.note(&msg);
+ }
+ }
+ }
+ }
+
+ /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else`
+ /// FIXME: currently not working for suggesting `map_or_else`, see #102408
+ pub(crate) fn suggest_else_fn_with_closure(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ found: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
+ else { return false; };
+
+ if !self.can_coerce(output, expected) {
+ return false;
+ }
+
+ let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
+ let hir::ExprKind::MethodCall(
+ hir::PathSegment { ident: method_name, .. },
+ self_expr,
+ args,
+ ..,
+ ) = call_expr.kind &&
+ let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) {
+ let new_name = Ident {
+ name: Symbol::intern(&format!("{}_else", method_name.as_str())),
+ span: method_name.span,
+ };
+ let probe = self.lookup_probe(
+ expr.span,
+ new_name,
+ self_ty,
+ self_expr,
+ ProbeScope::TraitsInScope,
+ );
+
+ // check the method arguments number
+ if let Ok(pick) = probe &&
+ let fn_sig = self.tcx.fn_sig(pick.item.def_id) &&
+ let fn_args = fn_sig.skip_binder().inputs() &&
+ fn_args.len() == args.len() + 1 {
+ err.span_suggestion_verbose(
+ method_name.span.shrink_to_hi(),
+ &format!("try calling `{}` instead", new_name.name.as_str()),
+ "_else",
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ }
+ false
+ }
+
+ /// Checks whether there is a local type somewhere in the chain of
+ /// autoderefs of `rcvr_ty`.
+ fn type_derefs_to_local(
+ &self,
+ span: Span,
+ rcvr_ty: Ty<'tcx>,
+ source: SelfSource<'tcx>,
+ ) -> bool {
+ fn is_local(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ ty::Adt(def, _) => def.did().is_local(),
+ ty::Foreign(did) => did.is_local(),
+ ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()),
+ ty::Param(_) => true,
+
+ // Everything else (primitive types, etc.) is effectively
+ // non-local (there are "edge" cases, e.g., `(LocalType,)`, but
+ // the noise from these sort of types is usually just really
+ // annoying, rather than any sort of help).
+ _ => false,
+ }
+ }
+
+ // This occurs for UFCS desugaring of `T::method`, where there is no
+ // receiver expression for the method call, and thus no autoderef.
+ if let SelfSource::QPath(_) = source {
+ return is_local(self.resolve_vars_with_obligations(rcvr_ty));
+ }
+
+ self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty))
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum SelfSource<'a> {
+ QPath(&'a hir::Ty<'a>),
+ MethodCall(&'a hir::Expr<'a> /* rcvr */),
+}
+
+#[derive(Copy, Clone)]
+pub struct TraitInfo {
+ pub def_id: DefId,
+}
+
+impl PartialEq for TraitInfo {
+ fn eq(&self, other: &TraitInfo) -> bool {
+ self.cmp(other) == Ordering::Equal
+ }
+}
+impl Eq for TraitInfo {}
+impl PartialOrd for TraitInfo {
+ fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+impl Ord for TraitInfo {
+ fn cmp(&self, other: &TraitInfo) -> Ordering {
+ // Local crates are more important than remote ones (local:
+ // `cnum == 0`), and otherwise we throw in the defid for totality.
+
+ let lhs = (other.def_id.krate, other.def_id);
+ let rhs = (self.def_id.krate, self.def_id);
+ lhs.cmp(&rhs)
+ }
+}
+
+/// Retrieves all traits in this crate and any dependent crates,
+/// and wraps them into `TraitInfo` for custom sorting.
+pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
+ tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect()
+}
+
+fn print_disambiguation_help<'tcx>(
+ item_name: Ident,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
+ err: &mut Diagnostic,
+ trait_name: String,
+ rcvr_ty: Ty<'_>,
+ kind: ty::AssocKind,
+ def_id: DefId,
+ span: Span,
+ candidate: Option<usize>,
+ source_map: &source_map::SourceMap,
+ fn_has_self_parameter: bool,
+) {
+ let mut applicability = Applicability::MachineApplicable;
+ let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
+ let args = format!(
+ "({}{})",
+ if rcvr_ty.is_region_ptr() {
+ if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" }
+ } else {
+ ""
+ },
+ std::iter::once(receiver)
+ .chain(args.iter())
+ .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {
+ applicability = Applicability::HasPlaceholders;
+ "_".to_owned()
+ }))
+ .collect::<Vec<_>>()
+ .join(", "),
+ );
+ let trait_name = if !fn_has_self_parameter {
+ format!("<{} as {}>", rcvr_ty, trait_name)
+ } else {
+ trait_name
+ };
+ (span, format!("{}::{}{}", trait_name, item_name, args))
+ } else {
+ (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name))
+ };
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "disambiguate the {} for {}",
+ kind.as_def_kind().descr(def_id),
+ if let Some(candidate) = candidate {
+ format!("candidate #{}", candidate)
+ } else {
+ "the candidate".to_string()
+ },
+ ),
+ sugg,
+ applicability,
+ );
+}