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.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs169
-rw-r--r--compiler/rustc_hir_typeck/src/method/prelude2021.rs28
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs210
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs555
5 files changed, 565 insertions, 423 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index be4ea9986..03d0e7926 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -106,7 +106,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// 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]);
+ let method_sig_rcvr = self.normalize(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
@@ -114,7 +114,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
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));
+ self.normalize(self.span, (method_sig, method_predicates));
let method_sig = ty::Binder::dummy(method_sig);
// Make sure nobody calls `drop()` explicitly.
@@ -151,8 +151,7 @@ impl<'a, 'tcx> ConfirmContext<'a, '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 mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty);
let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
return self.tcx.ty_error_with_message(
rustc_span::DUMMY_SP,
@@ -171,14 +170,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
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,
- },
- };
+
+ // Method call receivers are the primary use case
+ // for two-phase borrows.
+ let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::Yes);
+
adjustments.push(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
target,
@@ -203,7 +199,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
target = match target.kind() {
&ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
- assert_eq!(mutbl, hir::Mutability::Mut);
+ assert!(mutbl.is_mut());
self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
}
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
@@ -532,7 +528,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
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 => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
+ if trait_pred.def_id() == sized_def_id =>
+ {
let span = iter::zip(&predicates.predicates, &predicates.spans)
.find_map(
|(p, span)| {
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index a1278edef..a2ca5c3b7 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -10,6 +10,7 @@ mod suggest;
pub use self::suggest::SelfSource;
pub use self::MethodError::*;
+use crate::errors::OpMethodGenericParams;
use crate::{Expectation, FnCtxt};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diagnostic};
@@ -19,11 +20,11 @@ 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_middle::ty::{self, GenericParamDefKind, 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 rustc_trait_selection::traits::{self, NormalizeExt};
use self::probe::{IsSuggestion, ProbeScope};
@@ -55,8 +56,7 @@ pub enum MethodError<'tcx> {
// 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.
+ // Found a `Self: Sized` bound where `Self` is a trait object.
IllegalSizedBound(Vec<DefId>, bool, Span),
// Found a match, but the return type is wrong
@@ -93,17 +93,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr_id: hir::HirId,
allow_private: bool,
) -> bool {
- let mode = probe::Mode::MethodCall;
match self.probe_for_name(
- method_name.span,
- mode,
+ probe::Mode::MethodCall,
method_name,
IsSuggestion(false),
self_ty,
call_expr_id,
ProbeScope::TraitsInScope,
) {
- Ok(..) => true,
+ Ok(pick) => {
+ pick.maybe_emit_unstable_name_collision_hint(
+ self.tcx,
+ method_name.span,
+ call_expr_id,
+ );
+ true
+ }
Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true,
Err(PrivateMatch(..)) => allow_private,
@@ -125,10 +130,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let params = self
.probe_for_name(
- method_name.span,
probe::Mode::MethodCall,
method_name,
- IsSuggestion(false),
+ IsSuggestion(true),
self_ty,
call_expr.hir_id,
ProbeScope::TraitsInScope,
@@ -175,7 +179,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
@@ -200,13 +204,12 @@ impl<'a, 'tcx> FnCtxt<'a, '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 => {
+ Ok(ref new_pick) if pick.differs_from(new_pick) => {
needs_mut = true;
}
_ => {}
@@ -214,29 +217,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// 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));
+ let candidates =
+ match self.lookup_probe(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 pick.differs_from(new_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(),
+ };
return Err(IllegalSizedBound(candidates, needs_mut, span));
}
@@ -247,23 +246,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[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,
+ let pick = self.probe_for_name(
+ probe::Mode::MethodCall,
method_name,
IsSuggestion(false),
self_ty,
call_expr.hir_id,
scope,
- )
+ )?;
+ pick.maybe_emit_unstable_name_collision_hint(self.tcx, method_name.span, call_expr.hir_id);
+ Ok(pick)
}
pub(super) fn obligation_for_method(
@@ -295,10 +292,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let poly_trait_ref = ty::Binder::dummy(trait_ref);
(
traits::Obligation::misc(
+ self.tcx,
span,
self.body_id,
self.param_env,
- poly_trait_ref.without_const().to_predicate(self.tcx),
+ poly_trait_ref.without_const(),
),
substs,
)
@@ -337,6 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(
traits::Obligation::new(
+ self.tcx,
traits::ObligationCause::new(
span,
self.body_id,
@@ -348,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
),
self.param_env,
- poly_trait_ref.without_const().to_predicate(self.tcx),
+ poly_trait_ref,
),
substs,
)
@@ -444,7 +443,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let def_id = method_item.def_id;
let generics = tcx.generics_of(def_id);
- assert_eq!(generics.params.len(), 0);
+
+ if generics.params.len() != 0 {
+ tcx.sess.emit_fatal(OpMethodGenericParams {
+ span: tcx.def_span(method_item.def_id),
+ method_name: m_name.to_string(),
+ });
+ }
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
let mut obligations = vec![];
@@ -459,11 +464,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)
+ 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 {
- self.normalize_associated_types_in_as_infer_ok(span, fn_sig)
+ traits::ObligationCause::misc(span, self.body_id)
};
+
+ let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig);
let fn_sig = {
obligations.extend(o);
value
@@ -479,11 +495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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 InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds);
let bounds = {
obligations.extend(o);
value
@@ -491,20 +503,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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(),
@@ -519,9 +517,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
method_ty, obligation
);
obligations.push(traits::Obligation::new(
+ tcx,
cause,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx),
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())),
));
let callee = MethodCallee { def_id, substs, sig: fn_sig };
@@ -559,6 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
// Check if we have an enum variant.
+ let mut struct_variant = None;
if let ty::Adt(adt_def, _) = self_ty.kind() {
if adt_def.is_enum() {
let variant_def = adt_def
@@ -566,29 +566,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.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,
- ));
+ if let Some((ctor_kind, ctor_def_id)) = variant_def.ctor {
+ tcx.check_stability(
+ ctor_def_id,
+ Some(expr_id),
+ span,
+ Some(method_name.span),
+ );
+ return Ok((DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id));
+ } else {
+ struct_variant = Some((DefKind::Variant, variant_def.def_id));
+ }
}
}
}
let pick = self.probe_for_name(
- span,
probe::Mode::Path,
method_name,
IsSuggestion(false),
self_ty,
expr_id,
ProbeScope::TraitsInScope,
- )?;
+ );
+ let pick = match (pick, struct_variant) {
+ // Fall back to a resolution that will produce an error later.
+ (Err(_), Some(res)) => return Ok(res),
+ (pick, _) => pick?,
+ };
+
+ pick.maybe_emit_unstable_name_collision_hint(self.tcx, span, expr_id);
self.lint_fully_qualified_call_from_2018(
span,
diff --git a/compiler/rustc_hir_typeck/src/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
index 3c98a2aa3..dea14dd93 100644
--- a/compiler/rustc_hir_typeck/src/method/prelude2021.rs
+++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs
@@ -5,10 +5,9 @@ use crate::{
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_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::{Adt, Array, Ref, Ty};
use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS;
use rustc_span::symbol::kw::{Empty, Underscore};
@@ -88,14 +87,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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::Autoref { mutbl, .. }) => {
+ mutbl.ref_prefix_str()
+ }
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
};
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span)
@@ -227,14 +221,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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) {
+ let any_type = self.infcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span,
+ });
if !self
.infcx
- .type_implements_trait(
- trait_def_id,
- self_ty,
- InternalSubsts::empty(),
- self.param_env,
- )
+ .type_implements_trait(trait_def_id, [self_ty, any_type], self.param_env)
.may_apply()
{
return;
@@ -387,8 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(),
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
};
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 28aa2302f..ae299cc9d 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -9,7 +9,6 @@ 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};
@@ -17,8 +16,10 @@ 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::AssocItem;
use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_middle::ty::ToPredicate;
+use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFoldable, TypeVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_session::lint;
use rustc_span::def_id::DefId;
@@ -35,6 +36,7 @@ use rustc_trait_selection::traits::query::method_autoderef::{
CandidateStep, MethodAutoderefStepsResult,
};
use rustc_trait_selection::traits::query::CanonicalTyGoal;
+use rustc_trait_selection::traits::NormalizeExt;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::cmp::max;
use std::iter;
@@ -83,8 +85,6 @@ struct ProbeContext<'a, 'tcx> {
unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
- is_suggestion: IsSuggestion,
-
scope_expr_id: hir::HirId,
}
@@ -193,7 +193,7 @@ impl AutorefOrPtrAdjustment {
}
}
-#[derive(Debug, PartialEq, Clone)]
+#[derive(Debug, Clone)]
pub struct Pick<'tcx> {
pub item: ty::AssocItem,
pub kind: PickKind<'tcx>,
@@ -209,6 +209,9 @@ pub struct Pick<'tcx> {
/// `*mut T`, convert it to `*const T`.
pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>,
pub self_ty: Ty<'tcx>,
+
+ /// Unstable candidates alongside the stable ones.
+ unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -298,7 +301,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
pub fn probe_for_name(
&self,
- span: Span,
mode: Mode,
item_name: Ident,
is_suggestion: IsSuggestion,
@@ -307,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
scope: ProbeScope,
) -> PickResult<'tcx> {
self.probe_op(
- span,
+ item_name.span,
mode,
Some(item_name),
None,
@@ -340,10 +342,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&mut orig_values,
);
- let steps = if mode == Mode::MethodCall {
- self.tcx.method_autoderef_steps(param_env_and_self_ty)
- } else {
- self.probe(|_| {
+ let steps = match mode {
+ Mode::MethodCall => self.tcx.method_autoderef_steps(param_env_and_self_ty),
+ Mode::Path => 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
@@ -372,7 +373,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_bad_ty: None,
reached_recursion_limit: false,
}
- })
+ }),
};
// If our autoderef loop had reached the recursion limit,
@@ -446,7 +447,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return_type,
orig_values,
steps.steps,
- is_suggestion,
scope_expr_id,
);
@@ -475,10 +475,9 @@ fn method_autoderef_steps<'tcx>(
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 autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty)
+ .include_raw_pointers()
+ .silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef
.by_ref()
@@ -542,7 +541,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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 {
@@ -560,7 +558,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
allow_similar_names: false,
private_candidate: None,
unsatisfied_predicates: Vec::new(),
- is_suggestion,
scope_expr_id,
}
}
@@ -718,9 +715,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// 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);
+ let InferOk { value: xform_self_ty, obligations } =
+ self.fcx.at(&cause, self.param_env).normalize(xform_self_ty);
+
debug!(
"assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}",
xform_self_ty, xform_ret_ty
@@ -787,7 +784,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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) => {
+ ty::PredicateKind::Clause(ty::Clause::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))
@@ -797,14 +794,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Projection(..)
- | ty::PredicateKind::RegionOutlives(..)
+ | ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::WellFormed(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::TypeOutlives(..)
+ | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
+ | ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
}
});
@@ -882,7 +880,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- pub fn matches_return_type(
+ fn matches_return_type(
&self,
method: &ty::AssocItem,
self_ty: Option<Ty<'tcx>>,
@@ -1019,7 +1017,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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 {
@@ -1054,26 +1051,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
- let mut unstable_candidates = Vec::new();
- let pick = self.pick_all_method(Some(&mut unstable_candidates));
+ let pick = self.pick_all_method(Some(&mut vec![]));
// 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),
+ if pick.is_none() {
+ return self.pick_all_method(None);
}
+ pick
}
fn pick_all_method(
@@ -1218,7 +1206,6 @@ impl<'a, 'tcx> ProbeContext<'a, '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)]
@@ -1228,26 +1215,17 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self_ty,
candidates.iter(),
&mut possibly_unsatisfied_predicates,
- Some(&mut unstable_candidates),
+ Some(&mut vec![]),
);
- 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);
+ if res.is_some() {
+ return res;
}
}
debug!("searching unstable candidates");
let res = self.consider_candidates(
self_ty,
- unstable_candidates.iter().map(|(c, _)| c),
+ self.inherent_candidates.iter().chain(&self.extension_candidates),
&mut possibly_unsatisfied_predicates,
None,
);
@@ -1302,7 +1280,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)>,
- unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
+ mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
) -> Option<PickResult<'tcx>>
where
ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone,
@@ -1326,7 +1304,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}
- if let Some(uc) = unstable_candidates {
+ if let Some(uc) = &mut unstable_candidates {
applicable_candidates.retain(|&(p, _)| {
if let stability::EvalResult::Deny { feature, .. } =
self.tcx.eval_stability(p.item.def_id, None, self.span, None)
@@ -1345,30 +1323,63 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
applicable_candidates.pop().map(|(probe, status)| {
if status == ProbeResult::Match {
- Ok(probe.to_unadjusted_pick(self_ty))
+ Ok(probe
+ .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default()))
} else {
Err(MethodError::BadReturnType)
}
})
}
+}
+
+impl<'tcx> Pick<'tcx> {
+ /// In case there were unstable name collisions, emit them as a lint.
+ /// Checks whether two picks do not refer to the same trait item for the same `Self` type.
+ /// Only useful for comparisons of picks in order to improve diagnostics.
+ /// Do not use for type checking.
+ pub fn differs_from(&self, other: &Self) -> bool {
+ let Self {
+ item:
+ AssocItem {
+ def_id,
+ name: _,
+ kind: _,
+ container: _,
+ trait_item_def_id: _,
+ fn_has_self_parameter: _,
+ },
+ kind: _,
+ import_ids: _,
+ autoderefs: _,
+ autoref_or_ptr_adjustment: _,
+ self_ty,
+ unstable_candidates: _,
+ } = *self;
+ self_ty != other.self_ty || def_id != other.item.def_id
+ }
- fn emit_unstable_name_collision_hint(
+ /// In case there were unstable name collisions, emit them as a lint.
+ pub fn maybe_emit_unstable_name_collision_hint(
&self,
- stable_pick: &Pick<'_>,
- unstable_candidates: &[(Candidate<'tcx>, Symbol)],
+ tcx: TyCtxt<'tcx>,
+ span: Span,
+ scope_expr_id: hir::HirId,
) {
- let def_kind = stable_pick.item.kind.as_def_kind();
- self.tcx.struct_span_lint_hir(
+ if self.unstable_candidates.is_empty() {
+ return;
+ }
+ let def_kind = self.item.kind.as_def_kind();
+ tcx.struct_span_lint_hir(
lint::builtin::UNSTABLE_NAME_COLLISIONS,
- self.scope_expr_id,
- self.span,
+ scope_expr_id,
+ 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),
+ def_kind.descr(self.item.def_id),
),
|lint| {
- match (stable_pick.item.kind, stable_pick.item.container) {
+ match (self.item.kind, self.item.container) {
(ty::AssocKind::Fn, _) => {
// FIXME: This should be a `span_suggestion` instead of `help`
// However `self.span` only
@@ -1377,31 +1388,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
lint.help(&format!(
"call with fully qualified syntax `{}(...)` to keep using the current \
method",
- self.tcx.def_path_str(stable_pick.item.def_id),
+ tcx.def_path_str(self.item.def_id),
));
}
(ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => {
- let def_id = stable_pick.item.container_id(self.tcx);
+ let def_id = self.item.container_id(tcx);
lint.span_suggestion(
- self.span,
+ 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
+ self.self_ty,
+ tcx.def_path_str(def_id),
+ self.item.name
),
Applicability::MachineApplicable,
);
}
_ => {}
}
- if self.tcx.sess.is_nightly_build() {
- for (candidate, feature) in unstable_candidates {
+ if tcx.sess.is_nightly_build() {
+ for (candidate, feature) in &self.unstable_candidates {
lint.help(&format!(
"add `#![feature({})]` to the crate attributes to enable `{}`",
feature,
- self.tcx.def_path_str(candidate.item.def_id),
+ tcx.def_path_str(candidate.item.def_id),
));
}
}
@@ -1410,14 +1421,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
},
);
}
+}
+impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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);
+ let predicate = ty::Binder::dummy(trait_ref);
+ let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
traits::SelectionContext::new(self).select(&obligation)
}
@@ -1476,7 +1489,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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;
@@ -1490,10 +1502,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// `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 {
+ let InferOk {
value: normalized_xform_ret_ty,
obligations: normalization_obligations,
- } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty);
+ } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty);
xform_ret_ty = normalized_xform_ret_ty;
debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty);
@@ -1501,8 +1513,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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);
+
+ let InferOk { value: impl_bounds, obligations: norm_obligations } =
+ self.fcx.at(&cause, self.param_env).normalize(impl_bounds);
// Convert the bounds into obligations.
let impl_obligations = traits::predicates_for_generics(
@@ -1548,7 +1561,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
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);
+ let obligation =
+ traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
if !self.predicate_may_hold(&obligation) {
result = ProbeResult::NoMatch;
if self.probe(|_| {
@@ -1669,6 +1683,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
autoderefs: 0,
autoref_or_ptr_adjustment: None,
self_ty,
+ unstable_candidates: vec![],
})
}
@@ -1688,7 +1703,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.return_type,
self.orig_steps_var_values.clone(),
steps,
- IsSuggestion(true),
self.scope_expr_id,
);
pcx.allow_similar_names = true;
@@ -1861,6 +1875,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.tcx.erase_late_bound_regions(value)
}
+ /// Determine if the given associated item type is relevant in the current context.
+ fn is_relevant_kind_for_mode(&self, kind: ty::AssocKind) -> bool {
+ match (self.mode, kind) {
+ (Mode::MethodCall, ty::AssocKind::Fn) => true,
+ (Mode::Path, ty::AssocKind::Const | ty::AssocKind::Fn) => true,
+ _ => false,
+ }
+ }
+
/// 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
@@ -1873,7 +1896,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
.associated_items(def_id)
.in_definition_order()
.filter(|x| {
- if x.kind.namespace() != Namespace::ValueNS {
+ if !self.is_relevant_kind_for_mode(x.kind) {
return false;
}
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
@@ -1887,16 +1910,26 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
} else {
self.fcx
.associated_value(def_id, name)
+ .filter(|x| self.is_relevant_kind_for_mode(x.kind))
.map_or_else(SmallVec::new, |x| SmallVec::from_buf([x]))
}
} else {
- self.tcx.associated_items(def_id).in_definition_order().copied().collect()
+ self.tcx
+ .associated_items(def_id)
+ .in_definition_order()
+ .filter(|x| self.is_relevant_kind_for_mode(x.kind))
+ .copied()
+ .collect()
}
}
}
impl<'tcx> Candidate<'tcx> {
- fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> {
+ fn to_unadjusted_pick(
+ &self,
+ self_ty: Ty<'tcx>,
+ unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>,
+ ) -> Pick<'tcx> {
Pick {
item: self.item,
kind: match self.kind {
@@ -1921,6 +1954,7 @@ impl<'tcx> Candidate<'tcx> {
autoderefs: 0,
autoref_or_ptr_adjustment: None,
self_ty,
+ unstable_candidates,
}
}
}
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 6c21ed902..db93cfab2 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -5,6 +5,7 @@ use crate::errors;
use crate::FnCtxt;
use rustc_ast::ast::Mutability;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::StashKey;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
@@ -13,27 +14,35 @@ 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::PatKind::Binding;
+use rustc_hir::PathSegment;
use rustc_hir::{ExprKind, Node, QPath};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{
+ type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+ RegionVariableOrigin,
+};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::util::supertraits;
+use rustc_middle::ty::fast_reject::DeepRejectCtxt;
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::{self, DefIdTree, GenericArgKind, 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::OnUnimplementedNote;
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,
+ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
};
-use std::cmp::Ordering;
-use std::iter;
-
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData};
+use rustc_hir::intravisit::Visitor;
+use std::cmp::Ordering;
+use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -62,22 +71,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 {
+ let trait_ref = tcx.mk_trait_ref(
+ fn_once,
+ [
+ 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(
+ tcx,
span,
self.body_id,
self.param_env,
- poly_trait_ref.without_const().to_predicate(tcx),
+ poly_trait_ref.without_const(),
);
self.predicate_may_hold(&obligation)
})
@@ -107,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let report_candidates = |span: Span,
err: &mut Diagnostic,
sources: &mut Vec<CandidateSource>,
- sugg_span: Span| {
+ sugg_span: Option<Span>| {
sources.sort();
sources.dedup();
// Dynamic limit to avoid hiding just one candidate, which is silly.
@@ -168,7 +178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.note(&note_str);
}
- if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
+ if let Some(sugg_span) = sugg_span
+ && 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 {
@@ -217,20 +228,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 let Some(sugg_span) = sugg_span {
+ 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,
+ );
+ }
}
}
}
@@ -248,7 +261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match error {
MethodError::NoMatch(NoMatchData {
- static_candidates: mut static_sources,
+ mut static_candidates,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
@@ -256,15 +269,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}) => {
let tcx = self.tcx;
- let actual = self.resolve_vars_if_possible(rcvr_ty);
- let ty_str = self.ty_to_string(actual);
+ let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
+ let ty_str = self.ty_to_string(rcvr_ty);
let is_method = mode == Mode::MethodCall;
let item_kind = if is_method {
"method"
- } else if actual.is_enum() {
+ } else if rcvr_ty.is_enum() {
"variant or associated item"
} else {
- match (item_name.as_str().chars().next(), actual.is_fresh_ty()) {
+ match (item_name.as_str().chars().next(), rcvr_ty.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",
@@ -273,24 +286,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if self.suggest_wrapping_range_with_parens(
- tcx, actual, source, span, item_name, &ty_str,
+ tcx, rcvr_ty, source, span, item_name, &ty_str,
) || self.suggest_constraining_numerical_ty(
- tcx, actual, source, span, item_kind, item_name, &ty_str,
+ tcx, rcvr_ty, 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 let ty::Adt(_, generics) = rcvr_ty.kind() {
if generics.len() > 0 {
- let mut autoderef = self.autoderef(span, actual);
+ let mut autoderef = self.autoderef(span, rcvr_ty);
let candidate_found = autoderef.any(|(ty, _)| {
- if let ty::Adt(adt_deref, _) = ty.kind() {
+ if let ty::Adt(adt_def, _) = ty.kind() {
self.tcx
- .inherent_impls(adt_deref.did())
+ .inherent_impls(adt_def.did())
.iter()
.filter_map(|def_id| self.associated_value(*def_id, item_name))
.count()
@@ -315,16 +327,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"no {} named `{}` found for {} `{}` in the current scope",
item_kind,
item_name,
- actual.prefix_string(self.tcx),
+ rcvr_ty.prefix_string(self.tcx),
ty_str_reported,
);
- if actual.references_error() {
+ if rcvr_ty.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,
+ &mut err, item_name, rcvr_ty, cal, span,
);
}
if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
@@ -335,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
}
- if let ty::RawPtr(_) = &actual.kind() {
+ if let ty::RawPtr(_) = &rcvr_ty.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/\
@@ -347,22 +359,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
- 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))
- }
+ let ty_span = match rcvr_ty.kind() {
+ ty::Param(param_type) => Some(
+ param_type.span_from_generics(self.tcx, self.body_id.owner.to_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)
+ rcvr_ty.prefix_string(self.tcx)
),
);
}
@@ -374,7 +383,6 @@ impl<'a, 'tcx> FnCtxt<'a, '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,
@@ -386,7 +394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut custom_span_label = false;
- if !static_sources.is_empty() {
+ if !static_candidates.is_empty() {
err.note(
"found the following associated functions; to be used as methods, \
functions must have a `self` parameter",
@@ -394,43 +402,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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,));
- }
+ if static_candidates.len() == 1 {
+ self.suggest_associated_call_syntax(
+ &mut err,
+ &static_candidates,
+ rcvr_ty,
+ source,
+ item_name,
+ args,
+ sugg_span,
+ );
- 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);
+ report_candidates(span, &mut err, &mut static_candidates, None);
+ } else if static_candidates.len() > 1 {
+ report_candidates(span, &mut err, &mut static_candidates, Some(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) {
+ if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
let msg = "consider using `len` instead";
if let SelfSource::MethodCall(_expr) = source {
err.span_suggestion_short(
@@ -444,7 +435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
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"));
+ err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"));
}
} else if !unsatisfied_predicates.is_empty() {
let mut type_params = FxHashMap::default();
@@ -454,7 +445,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)) =
+ if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) =
(predicate.kind().skip_binder(), cause.as_ref())
{
if p.trait_ref.self_ty() != rcvr_ty {
@@ -481,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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)
+ ty::PredicateKind::Clause(ty::Clause::Trait(p))
if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
_ => {
unimplemented_traits_only = false;
@@ -493,27 +484,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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)) =
+ if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) =
(self_ty.kind(), parent_pred.kind().skip_binder())
{
+ let hir = self.tcx.hir();
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)),
- )
+ let parent_body =
+ hir.body_owner(hir::BodyId { hir_id: self.body_id });
+ Some(hir.get(parent_body))
+ }
+ ty::Adt(def, _) => {
+ def.did().as_local().map(|def_id| hir.get_by_def_id(def_id))
}
- 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 {
@@ -562,7 +547,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut format_pred = |pred: ty::Predicate<'tcx>| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Projection(pred) => {
+ ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`.
let projection_ty = pred.skip_binder().projection_ty;
@@ -585,7 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
Some((obligation, projection_ty.self_ty()))
}
- ty::PredicateKind::Trait(poly_trait_ref) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
let p = poly_trait_ref.trait_ref;
let self_ty = p.self_ty();
let path = p.print_only_trait_path();
@@ -605,7 +590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.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) => {
+ ObligationCauseCode::ImplDerivedObligation(data) => {
Some((&data.derived, p, parent, data.impl_def_id, data))
}
_ => None,
@@ -621,22 +606,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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!(
@@ -659,34 +628,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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:
@@ -695,23 +636,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}),
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) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
Some(pred.def_id())
== self.tcx.lang_items().sized_trait()
&& pred.polarity == ty::ImplPolarity::Positive
@@ -759,7 +688,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let entry = spanned_predicates.entry(spans);
entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
}
- _ => {}
+ Some(_) => unreachable!(),
+ None => (),
}
}
let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
@@ -844,7 +774,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.map(|(_, path)| path)
.collect::<Vec<_>>()
.join("\n");
- let actual_prefix = actual.prefix_string(self.tcx);
+ let actual_prefix = rcvr_ty.prefix_string(self.tcx);
info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
let (primary_message, label) =
if unimplemented_traits.len() == 1 && unimplemented_traits_only {
@@ -853,7 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.next()
.map(|(_, (trait_ref, obligation))| {
if trait_ref.self_ty().references_error()
- || actual.references_error()
+ || rcvr_ty.references_error()
{
// Avoid crashing.
return (None, None);
@@ -863,7 +793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.on_unimplemented_note(trait_ref, &obligation);
(message, label)
})
- .unwrap_or((None, None))
+ .unwrap()
} else {
(None, None)
};
@@ -889,15 +819,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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() {
+ let is_string_or_ref_str = match rcvr_ty.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, _) if Some(adt.did()) == self.tcx.lang_items().string()
)
}
- ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()),
+ ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
_ => false,
};
if is_string_or_ref_str && item_name.name == sym::iter {
@@ -925,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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
&& self.tcx.at(span).type_of(*def_id) != rcvr_ty
}
(Mode::Path, false, _) => true,
@@ -972,7 +902,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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)
+ if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
&& lev_candidate.is_none()
&& !custom_span_label
{
@@ -982,13 +912,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
label_span_not_found(&mut err);
}
- // Don't suggest (for example) `expr.field.method()` if `expr.method()`
- // doesn't exist due to unsatisfied predicates.
+ // Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
+ // can't be called due to `typeof(expr): Clone` not holding.
if unsatisfied_predicates.is_empty() {
- self.check_for_field_method(&mut err, source, span, actual, item_name);
+ self.suggest_calling_method_on_field(
+ &mut err, source, span, rcvr_ty, item_name,
+ );
}
- self.check_for_inner_self(&mut err, source, span, actual, item_name);
+ self.check_for_inner_self(&mut err, source, rcvr_ty, item_name);
bound_spans.sort();
bound_spans.dedup();
@@ -996,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(span, &msg);
}
- if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
+ if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
} else {
self.suggest_traits_to_import(
&mut err,
@@ -1007,15 +939,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
source,
out_of_scope_traits,
&unsatisfied_predicates,
- &static_sources,
+ &static_candidates,
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 unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() {
+ let adt_def = rcvr_ty.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,
@@ -1030,7 +962,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+ if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() {
let msg = "remove this method call";
let mut fallback_span = true;
if let SelfSource::MethodCall(expr) = source {
@@ -1089,7 +1021,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
err.span_label(item_name.span, format!("multiple `{}` found", item_name));
- report_candidates(span, &mut err, &mut sources, sugg_span);
+ report_candidates(span, &mut err, &mut sources, Some(sugg_span));
err.emit();
}
@@ -1146,7 +1078,133 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
}
- fn suggest_field_call(
+ /// Suggest calling `Ty::method` if `.method()` isn't found because the method
+ /// doesn't take a `self` receiver.
+ fn suggest_associated_call_syntax(
+ &self,
+ err: &mut Diagnostic,
+ static_candidates: &Vec<CandidateSource>,
+ rcvr_ty: Ty<'tcx>,
+ source: SelfSource<'tcx>,
+ item_name: Ident,
+ args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>,
+ sugg_span: Span,
+ ) {
+ let mut has_unsuggestable_args = false;
+ let ty_str = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.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 impl_ty = self.tcx.type_of(*impl_did);
+ let target_ty = self
+ .autoderef(sugg_span, rcvr_ty)
+ .find(|(rcvr_ty, _)| {
+ DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer }
+ .types_may_unify(*rcvr_ty, impl_ty)
+ })
+ .map_or(impl_ty, |(ty, _)| ty)
+ .peel_refs();
+ if let ty::Adt(def, substs) = target_ty.kind() {
+ // If there are any inferred arguments, (`{integer}`), we should replace
+ // them with underscores to allow the compiler to infer them
+ let infer_substs = self.tcx.mk_substs(substs.into_iter().map(|arg| {
+ if !arg.is_suggestable(self.tcx, true) {
+ has_unsuggestable_args = true;
+ match arg.unpack() {
+ GenericArgKind::Lifetime(_) => self
+ .next_region_var(RegionVariableOrigin::MiscVariable(
+ rustc_span::DUMMY_SP,
+ ))
+ .into(),
+ GenericArgKind::Type(_) => self
+ .next_ty_var(TypeVariableOrigin {
+ span: rustc_span::DUMMY_SP,
+ kind: TypeVariableOriginKind::MiscVariable,
+ })
+ .into(),
+ GenericArgKind::Const(arg) => self
+ .next_const_var(
+ arg.ty(),
+ ConstVariableOrigin {
+ span: rustc_span::DUMMY_SP,
+ kind: ConstVariableOriginKind::MiscVariable,
+ },
+ )
+ .into(),
+ }
+ } else {
+ arg
+ }
+ }));
+
+ self.tcx.value_path_str_with_substs(def.did(), infer_substs)
+ } else {
+ self.ty_to_value_string(target_ty)
+ }
+ } else {
+ self.ty_to_value_string(rcvr_ty.peel_refs())
+ };
+ if let SelfSource::MethodCall(_) = source {
+ let first_arg = if let Some(CandidateSource::Impl(impl_did)) = static_candidates.get(0)
+ && let Some(assoc) = self.associated_value(*impl_did, item_name)
+ && assoc.kind == ty::AssocKind::Fn
+ {
+ let sig = self.tcx.fn_sig(assoc.def_id);
+ sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() {
+ None
+ } else {
+ Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()))
+ })
+ } else {
+ None
+ };
+ let mut applicability = Applicability::MachineApplicable;
+ let args = if let Some((receiver, args)) = args {
+ // The first arg is the same kind as the receiver
+ let explicit_args = if first_arg.is_some() {
+ std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>()
+ } else {
+ // There is no `Self` kind to infer the arguments from
+ if has_unsuggestable_args {
+ applicability = Applicability::HasPlaceholders;
+ }
+ args.iter().collect()
+ };
+ format!(
+ "({}{})",
+ first_arg.unwrap_or(""),
+ explicit_args
+ .iter()
+ .map(|arg| self
+ .tcx
+ .sess
+ .source_map()
+ .span_to_snippet(arg.span)
+ .unwrap_or_else(|_| {
+ applicability = Applicability::HasPlaceholders;
+ "_".to_owned()
+ }))
+ .collect::<Vec<_>>()
+ .join(", "),
+ )
+ } else {
+ applicability = Applicability::HasPlaceholders;
+ "(...)".to_owned()
+ };
+ err.span_suggestion(
+ sugg_span,
+ "use associated function syntax instead",
+ format!("{}::{}{}", ty_str, item_name, args),
+ applicability,
+ );
+ } else {
+ err.help(&format!("try with `{}::{}`", ty_str, item_name,));
+ }
+ }
+
+ /// Suggest calling a field with a type that implements the `Fn*` traits instead of a method with
+ /// the same name as the field i.e. `(a.my_fn_ptr)(10)` instead of `a.my_fn_ptr(10)`.
+ fn suggest_calling_field_as_fn(
&self,
span: Span,
rcvr_ty: Ty<'tcx>,
@@ -1261,7 +1319,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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),
@@ -1408,7 +1465,66 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
- fn check_for_field_method(
+ /// For code `rect::area(...)`,
+ /// if `rect` is a local variable and `area` is a valid assoc method for it,
+ /// we try to suggest `rect.area()`
+ pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) {
+ debug!("suggest_assoc_method_call segs: {:?}", segs);
+ let [seg1, seg2] = segs else { return; };
+ let Some(mut diag) =
+ self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
+ else { return };
+
+ let map = self.infcx.tcx.hir();
+ let body = map.body(rustc_hir::BodyId { hir_id: self.body_id });
+ struct LetVisitor<'a> {
+ result: Option<&'a hir::Expr<'a>>,
+ ident_name: Symbol,
+ }
+
+ // FIXME: This really should be taking scoping, etc into account.
+ impl<'v> Visitor<'v> for LetVisitor<'v> {
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+ if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
+ && let Binding(_, _, ident, ..) = pat.kind
+ && ident.name == self.ident_name
+ {
+ self.result = *init;
+ } else {
+ hir::intravisit::walk_stmt(self, ex);
+ }
+ }
+ }
+
+ let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
+ visitor.visit_body(&body);
+
+ let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
+ if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent)
+ && let Some(expr) = visitor.result
+ && let Some(self_ty) = self.node_ty_opt(expr.hir_id)
+ {
+ let probe = self.lookup_probe(
+ seg2.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ );
+ if probe.is_ok() {
+ let sm = self.infcx.tcx.sess.source_map();
+ diag.span_suggestion_verbose(
+ sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
+ "you may have meant to call an instance method",
+ ".".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ diag.emit();
+ }
+
+ /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
+ fn suggest_calling_method_on_field(
&self,
err: &mut Diagnostic,
source: SelfSource<'tcx>,
@@ -1439,7 +1555,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
&|_, field_ty| {
self.lookup_probe(
- span,
item_name,
field_ty,
call_expr,
@@ -1487,7 +1602,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
err: &mut Diagnostic,
source: SelfSource<'tcx>,
- span: Span,
actual: Ty<'tcx>,
item_name: Ident,
) {
@@ -1510,15 +1624,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
}
- self.lookup_probe(
- span,
- item_name,
- field_ty,
- call_expr,
- ProbeScope::TraitsInScope,
- )
- .ok()
- .map(|pick| (variant, field, pick))
+ self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope)
+ .ok()
+ .map(|pick| (variant, field, pick))
})
.collect();
@@ -1583,12 +1691,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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; };
+ item_name,
+ ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ ) else { return; };
let name = self.ty_to_value_string(actual);
let inner_id = kind.did();
@@ -1668,7 +1775,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() {
ty::Adt(def, _) => def.did().is_local(),
_ => false,
},
@@ -1677,7 +1784,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut preds: Vec<_> = errors
.iter()
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(pred) => Some(pred),
+ ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred),
_ => None,
})
.collect();
@@ -1748,7 +1855,7 @@ impl<'a, 'tcx> FnCtxt<'a, '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 ty::PredicateKind::Clause(ty::Clause::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,
@@ -1838,7 +1945,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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),
@@ -1865,6 +1971,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::Str
| ty::Projection(_)
| ty::Param(_) => format!("{deref_ty}"),
+ // we need to test something like <&[_]>::len or <(&[u32])>::len
+ // and Vec::function();
+ // <&[_]>::len or <&[u32]>::len doesn't need an extra "<>" between
+ // but for Adt type like Vec::function()
+ // we would suggest <[_]>::function();
+ _ if self.tcx.sess.source_map().span_wrapped_by_angle_or_parentheses(ty.span) => format!("{deref_ty}"),
_ => format!("<{deref_ty}>"),
};
err.span_suggestion_verbose(
@@ -1887,7 +1999,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// 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)),
+ ty::Adt(def, substs) => self.tcx.def_path_str_with_substs(def.did(), substs),
_ => self.ty_to_string(ty),
}
}
@@ -1901,7 +2013,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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(),
+ Some(output_ty) => self.resolve_vars_if_possible(output_ty),
_ => return,
};
let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true);
@@ -2021,7 +2133,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let mut alt_rcvr_sugg = false;
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
- debug!(?span, ?item_name, ?rcvr_ty, ?rcvr);
+ debug!(
+ "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
+ span, item_name, rcvr_ty, rcvr
+ );
let skippable = [
self.tcx.lang_items().clone_trait(),
self.tcx.lang_items().deref_trait(),
@@ -2037,7 +2152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(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) {
+ match self.lookup_probe(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
@@ -2060,7 +2175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// suggestions are generally misleading (see #94218).
break;
}
- _ => {}
+ Err(_) => (),
}
for (rcvr_ty, pre) in &[
@@ -2071,7 +2186,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
] {
if let Some(new_rcvr_t) = *rcvr_ty
&& let Ok(pick) = self.lookup_probe(
- span,
item_name,
new_rcvr_t,
rcvr,
@@ -2151,8 +2265,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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) => {
+ ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ t.def_id() == info.def_id
+ }
+ ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
p.projection_ty.item_def_id == info.def_id
}
_ => false,
@@ -2452,7 +2568,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span: method_name.span,
};
let probe = self.lookup_probe(
- expr.span,
new_name,
self_ty,
self_expr,
@@ -2565,11 +2680,7 @@ fn print_disambiguation_help<'tcx>(
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 {
- ""
- },
+ rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
std::iter::once(receiver)
.chain(args.iter())
.map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {