rustc_typeck to rustc_hir_analysis

This commit is contained in:
lcnr
2022-09-26 13:00:29 +02:00
parent de0b511daa
commit 1fc86a63f4
140 changed files with 101 additions and 102 deletions

View File

@@ -0,0 +1,594 @@
use super::{probe, MethodCallee};
use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
use crate::check::{callee, FnCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
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)
}
}

View File

@@ -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::check::{Expectation, FnCtxt};
use crate::ObligationCause;
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::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()
}
}

View File

@@ -0,0 +1,418 @@
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;
use crate::check::{
method::probe::{self, Pick},
FnCtxt,
};
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,
|lint| {
let sp = self_expr.span;
let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
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.emit();
},
);
} 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,
|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 mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
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.emit();
},
);
}
}
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, |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 lint = lint.build(&format!(
"trait-associated function `{}` will become ambiguous in Rust 2021",
method_name.name
));
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.emit();
});
}
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)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff