diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 352146d6463..168ea713d19 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3001,6 +3001,7 @@ declare_lint_pass! { PROC_MACRO_BACK_COMPAT, OR_PATTERNS_BACK_COMPAT, LARGE_ASSIGNMENTS, + FUTURE_PRELUDE_COLLISION, ] } @@ -3240,3 +3241,47 @@ declare_lint! { Allow, "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; + /// } + /// + /// impl Foo for &str { + /// fn try_into(self) -> Result { + /// 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", +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index fb37c5e9c1e..8019569ab9b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1236,7 +1236,9 @@ symbols! { truncf32, truncf64, try_blocks, + try_from, try_from_trait, + try_into, try_into_trait, try_trait_v2, tt, diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 427102afee1..164b1a4910b 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -21,7 +21,8 @@ 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_span::symbol::Ident; +use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -198,6 +199,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = 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 { debug!("used_trait_import: {:?}", import_id); Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)