Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 79388aa

Browse files
jam1garnernikomatsakis
authored andcommitted
Add future_prelude_collision lint
1 parent a216131 commit 79388aa

File tree

3 files changed

+95
-1
lines changed

3 files changed

+95
-1
lines changed

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3001,6 +3001,7 @@ declare_lint_pass! {
30013001
PROC_MACRO_BACK_COMPAT,
30023002
OR_PATTERNS_BACK_COMPAT,
30033003
LARGE_ASSIGNMENTS,
3004+
FUTURE_PRELUDE_COLLISION,
30043005
]
30053006
}
30063007

@@ -3240,3 +3241,47 @@ declare_lint! {
32403241
Allow,
32413242
"detects usage of old versions of or-patterns",
32423243
}
3244+
3245+
declare_lint! {
3246+
/// The `future_prelude_collision` lint detects the usage of trait methods which are ambiguous
3247+
/// with traits added to the prelude in future editions.
3248+
///
3249+
/// ### Example
3250+
///
3251+
/// ```rust,compile_fail
3252+
/// #![deny(future_prelude_collision)]
3253+
///
3254+
/// trait Foo {
3255+
/// fn try_into(self) -> Result<String, !>;
3256+
/// }
3257+
///
3258+
/// impl Foo for &str {
3259+
/// fn try_into(self) -> Result<String, !> {
3260+
/// Ok(String::from(self))
3261+
/// }
3262+
/// }
3263+
///
3264+
/// fn main() {
3265+
/// let x: String = "3".try_into().unwrap();
3266+
/// // ^^^^^^^^
3267+
/// // This call to try_into matches both Foo:try_into and TryInto::try_into as
3268+
/// // `TryInto` has been added to the Rust prelude in 2021 edition.
3269+
/// println!("{}", x);
3270+
/// }
3271+
/// ```
3272+
///
3273+
/// {{produces}}
3274+
///
3275+
/// ### Explanation
3276+
///
3277+
/// In Rust 2021, one of the important introductions is the [prelude changes], which add
3278+
/// `TryFrom` and `TryInto` into the standard library's prelude. Since this results in an
3279+
/// amiguity as to which method to call when an existing `try_from` or `try_into` method is
3280+
/// called via dot-call syntax.
3281+
///
3282+
/// [prelude changes]: https://blog.rust-lang.org/inside-rust/2021/03/04/planning-rust-2021.html#prelude-changes
3283+
pub FUTURE_PRELUDE_COLLISION,
3284+
Warn,
3285+
"detects the usage of trait methods which are ambiguous with traits added to the \
3286+
prelude in future editions",
3287+
}

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,7 +1236,9 @@ symbols! {
12361236
truncf32,
12371237
truncf64,
12381238
try_blocks,
1239+
try_from,
12391240
try_from_trait,
1241+
try_into,
12401242
try_into_trait,
12411243
try_trait_v2,
12421244
tt,

compiler/rustc_typeck/src/check/method/mod.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use rustc_middle::ty::subst::Subst;
2121
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
2222
use rustc_middle::ty::GenericParamDefKind;
2323
use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TypeFoldable, WithConstness};
24-
use rustc_span::symbol::Ident;
24+
use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION;
25+
use rustc_span::symbol::{sym, Ident};
2526
use rustc_span::Span;
2627
use rustc_trait_selection::traits;
2728
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -198,6 +199,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
198199
let pick =
199200
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
200201

202+
if let sym::try_from | sym::try_into = segment.ident.name {
203+
if let probe::PickKind::TraitPick = pick.kind {
204+
if !matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) {
205+
self.tcx.struct_span_lint_hir(
206+
FUTURE_PRELUDE_COLLISION,
207+
call_expr.hir_id,
208+
call_expr.span,
209+
|lint| {
210+
let sp = call_expr.span;
211+
let trait_name =
212+
self.tcx.def_path_str(pick.item.container.assert_trait());
213+
214+
let mut lint = lint.build(&format!(
215+
"trait method `{}` will become ambiguous in Rust 2021",
216+
segment.ident.name
217+
));
218+
219+
if let Ok(self_expr) =
220+
self.sess().source_map().span_to_snippet(self_expr.span)
221+
{
222+
lint.span_suggestion(
223+
sp,
224+
"disambiguate the associated function",
225+
format!(
226+
"{}::{}({})",
227+
trait_name, segment.ident.name, self_expr,
228+
),
229+
Applicability::MachineApplicable,
230+
);
231+
} else {
232+
lint.span_help(
233+
sp,
234+
&format!(
235+
"disambiguate the associated function with `{}::{}(...)`",
236+
trait_name, segment.ident,
237+
),
238+
);
239+
}
240+
241+
lint.emit();
242+
},
243+
);
244+
}
245+
}
246+
}
247+
201248
for import_id in &pick.import_ids {
202249
debug!("used_trait_import: {:?}", import_id);
203250
Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)

0 commit comments

Comments
 (0)