1
Fork 0

extract Rust 2021 prelude logic to its own module

This commit is contained in:
Niko Matsakis 2021-06-14 13:43:58 -04:00
parent 8d42f3da63
commit 17ab9c0ff9
2 changed files with 178 additions and 124 deletions

View file

@ -3,6 +3,7 @@
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
mod confirm;
mod prelude2021;
pub mod probe;
mod suggest;
@ -10,9 +11,7 @@ pub use self::suggest::{SelfSource, TraitInfo};
pub use self::CandidateSource::*;
pub use self::MethodError::*;
use crate::check::method::probe::PickKind;
use crate::check::FnCtxt;
use rustc_ast::ast::Mutability;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
@ -23,9 +22,7 @@ use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness};
use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
use rustc_span::edition::Edition;
use rustc_span::symbol::{sym, Ident};
use rustc_span::symbol::Ident;
use rustc_span::Span;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@ -202,71 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let pick =
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
if span.edition() < Edition::Edition2021 {
if let sym::try_into = segment.ident.name {
if !matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
self.tcx.struct_span_lint_hir(
FUTURE_PRELUDE_COLLISION,
call_expr.hir_id,
call_expr.span,
|lint| {
let sp = call_expr.span;
let trait_name = self.tcx.def_path_str(pick.item.container.id());
let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
if let Ok(self_expr) =
self.sess().source_map().span_to_snippet(self_expr.span)
{
let derefs = "*".repeat(pick.autoderefs);
let autoref = match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref {
mutbl: Mutability::Mut,
..
}) => "&mut ",
Some(probe::AutorefOrPtrAdjustment::Autoref {
mutbl: Mutability::Not,
..
}) => "&",
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
};
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 associated function",
format!(
"{}::{}({})",
trait_name, segment.ident.name, self_adjusted,
),
Applicability::MachineApplicable,
);
} else {
lint.span_help(
sp,
&format!(
"disambiguate the associated function with `{}::{}(...)`",
trait_name, segment.ident,
),
);
}
lint.emit();
},
);
}
}
}
self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick);
for import_id in &pick.import_ids {
debug!("used_trait_import: {:?}", import_id);
@ -551,60 +484,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ProbeScope::TraitsInScope,
)?;
if span.edition() < Edition::Edition2021 {
if let sym::try_into | sym::try_from | sym::from_iter = method_name.name {
// No need to warn if either:
//
// * The method comes from std/core, since ten it's the built-in trait.
// * This is an inherent method called on a specific type, like `Vec::foo(...)`,
// since such methods take precedence over trait methods.
if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core)
&& !matches!(pick.kind, PickKind::InherentImplPick)
{
tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, 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 type_name = tcx.def_path_str(pick.item.container.id());
let type_generics = tcx.generics_of(pick.item.container.id());
let parameter_count =
type_generics.count() - (type_generics.has_self as usize);
let trait_name = if parameter_count == 0 {
type_name
} else {
format!(
"{}<{}>",
type_name,
std::iter::repeat("_")
.take(parameter_count)
.collect::<Vec<_>>()
.join(", ")
)
};
let mut lint = lint.build(&format!(
"trait-associated function `{}` will become ambiguous in Rust 2021",
method_name.name
));
let self_ty = self
.sess()
.source_map()
.span_to_snippet(self_ty_span)
.unwrap_or_else(|_| self_ty.to_string());
lint.span_suggestion(
span,
"disambiguate the associated function",
format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,),
Applicability::MachineApplicable,
);
lint.emit();
});
}
}
}
self.lint_fully_qualified_call_from_2018(
span,
method_name,
self_ty,
self_ty_span,
expr_id,
&pick,
);
debug!("resolve_fully_qualified_call: pick={:?}", pick);
{

View file

@ -0,0 +1,167 @@
use rustc_ast::Mutability;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::ty::Ty;
use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
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>,
) {
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;
}
// These are the method names that were added to prelude in Rust 2021
if !matches!(segment.ident.name, sym::try_into) {
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;
}
self.tcx.struct_span_lint_hir(
FUTURE_PRELUDE_COLLISION,
call_expr.hir_id,
call_expr.span,
|lint| {
let sp = call_expr.span;
let trait_name = self.tcx.def_path_str(pick.item.container.id());
let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021",
segment.ident.name
));
if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) {
let derefs = "*".repeat(pick.autoderefs);
let autoref = match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref {
mutbl: Mutability::Mut,
..
}) => "&mut ",
Some(probe::AutorefOrPtrAdjustment::Autoref {
mutbl: Mutability::Not,
..
}) => "&",
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "",
};
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 associated function",
format!("{}::{}({})", trait_name, segment.ident.name, self_adjusted,),
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;
}
// 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(FUTURE_PRELUDE_COLLISION, 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 type_name = self.tcx.def_path_str(pick.item.container.id());
let type_generics = self.tcx.generics_of(pick.item.container.id());
let parameter_count = type_generics.count() - (type_generics.has_self as usize);
let trait_name = if parameter_count == 0 {
type_name
} else {
format!(
"{}<{}>",
type_name,
std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
)
};
let mut lint = lint.build(&format!(
"trait-associated function `{}` will become ambiguous in Rust 2021",
method_name.name
));
let self_ty = self
.sess()
.source_map()
.span_to_snippet(self_ty_span)
.unwrap_or_else(|_| self_ty.to_string());
lint.span_suggestion(
span,
"disambiguate the associated function",
format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,),
Applicability::MachineApplicable,
);
lint.emit();
});
}
}