Add future_prelude_collision lint
This commit is contained in:
parent
a216131c35
commit
79388aa067
3 changed files with 95 additions and 1 deletions
|
@ -3001,6 +3001,7 @@ declare_lint_pass! {
|
||||||
PROC_MACRO_BACK_COMPAT,
|
PROC_MACRO_BACK_COMPAT,
|
||||||
OR_PATTERNS_BACK_COMPAT,
|
OR_PATTERNS_BACK_COMPAT,
|
||||||
LARGE_ASSIGNMENTS,
|
LARGE_ASSIGNMENTS,
|
||||||
|
FUTURE_PRELUDE_COLLISION,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3240,3 +3241,47 @@ declare_lint! {
|
||||||
Allow,
|
Allow,
|
||||||
"detects usage of old versions of or-patterns",
|
"detects usage of old versions of or-patterns",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `future_prelude_collision` lint detects the usage of trait methods which are ambiguous
|
||||||
|
/// with traits added to the prelude in future editions.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,compile_fail
|
||||||
|
/// #![deny(future_prelude_collision)]
|
||||||
|
///
|
||||||
|
/// trait Foo {
|
||||||
|
/// fn try_into(self) -> Result<String, !>;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Foo for &str {
|
||||||
|
/// fn try_into(self) -> Result<String, !> {
|
||||||
|
/// Ok(String::from(self))
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// let x: String = "3".try_into().unwrap();
|
||||||
|
/// // ^^^^^^^^
|
||||||
|
/// // This call to try_into matches both Foo:try_into and TryInto::try_into as
|
||||||
|
/// // `TryInto` has been added to the Rust prelude in 2021 edition.
|
||||||
|
/// println!("{}", x);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// In Rust 2021, one of the important introductions is the [prelude changes], which add
|
||||||
|
/// `TryFrom` and `TryInto` into the standard library's prelude. Since this results in an
|
||||||
|
/// amiguity as to which method to call when an existing `try_from` or `try_into` method is
|
||||||
|
/// called via dot-call syntax.
|
||||||
|
///
|
||||||
|
/// [prelude changes]: https://blog.rust-lang.org/inside-rust/2021/03/04/planning-rust-2021.html#prelude-changes
|
||||||
|
pub FUTURE_PRELUDE_COLLISION,
|
||||||
|
Warn,
|
||||||
|
"detects the usage of trait methods which are ambiguous with traits added to the \
|
||||||
|
prelude in future editions",
|
||||||
|
}
|
||||||
|
|
|
@ -1236,7 +1236,9 @@ symbols! {
|
||||||
truncf32,
|
truncf32,
|
||||||
truncf64,
|
truncf64,
|
||||||
try_blocks,
|
try_blocks,
|
||||||
|
try_from,
|
||||||
try_from_trait,
|
try_from_trait,
|
||||||
|
try_into,
|
||||||
try_into_trait,
|
try_into_trait,
|
||||||
try_trait_v2,
|
try_trait_v2,
|
||||||
tt,
|
tt,
|
||||||
|
|
|
@ -21,7 +21,8 @@ use rustc_middle::ty::subst::Subst;
|
||||||
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
|
||||||
use rustc_middle::ty::GenericParamDefKind;
|
use rustc_middle::ty::GenericParamDefKind;
|
||||||
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness};
|
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
|
||||||
|
use rustc_span::symbol::{sym, Ident};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
|
@ -198,6 +199,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let pick =
|
let pick =
|
||||||
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
|
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
|
||||||
|
|
||||||
|
if let sym::try_from | sym::try_into = segment.ident.name {
|
||||||
|
if let probe::PickKind::TraitPick = pick.kind {
|
||||||
|
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.assert_trait());
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
lint.span_suggestion(
|
||||||
|
sp,
|
||||||
|
"disambiguate the associated function",
|
||||||
|
format!(
|
||||||
|
"{}::{}({})",
|
||||||
|
trait_name, segment.ident.name, self_expr,
|
||||||
|
),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
lint.span_help(
|
||||||
|
sp,
|
||||||
|
&format!(
|
||||||
|
"disambiguate the associated function with `{}::{}(...)`",
|
||||||
|
trait_name, segment.ident,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lint.emit();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for import_id in &pick.import_ids {
|
for import_id in &pick.import_ids {
|
||||||
debug!("used_trait_import: {:?}", import_id);
|
debug!("used_trait_import: {:?}", import_id);
|
||||||
Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
|
Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue