Skip to content

Commit 44f4a87

Browse files
committed
Auto merge of #85707 - jam1garner:future_prelude_collision_lint, r=nikomatsakis
Add `future_prelude_collision` lint Implements #84594. (RFC rust-lang/rfcs#3114 ([rendered](https://github.com/rust-lang/rfcs/blob/master/text/3114-prelude-2021.md))) Not entirely complete but wanted to have my progress decently available while I finish off the last little bits. Things left to implement: * [x] UI tests for lints * [x] Only emit lint for 2015 and 2018 editions * [ ] Lint name/message bikeshedding * [x] Implement for `FromIterator` (from best I can tell, the current approach as mentioned from [this comment](#84594 (comment)) won't work due to `FromIterator` instances not using dot-call syntax, but if I'm correct about this then that would also need to be fixed for `TryFrom`/`TryInto`)* * [x] Add to `rust-2021-migration` group? (See #85512) (added to `rust-2021-compatibility` group) * [ ] Link to edition guide in lint docs *edit: looked into it, `lookup_method` will also not be hit for `TryFrom`/`TryInto` for non-dotcall syntax. If anyone who is more familiar with typecheck knows the equivalent for looking up associated functions, feel free to chime in.
2 parents c38111c + aa3580b commit 44f4a87

22 files changed

+1144
-31
lines changed

compiler/rustc_lint_defs/src/builtin.rs

+50
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

@@ -3244,3 +3245,52 @@ declare_lint! {
32443245
edition: Some(Edition::Edition2021),
32453246
};
32463247
}
3248+
3249+
declare_lint! {
3250+
/// The `future_prelude_collision` lint detects the usage of trait methods which are ambiguous
3251+
/// with traits added to the prelude in future editions.
3252+
///
3253+
/// ### Example
3254+
///
3255+
/// ```rust,compile_fail
3256+
/// #![deny(future_prelude_collision)]
3257+
///
3258+
/// trait Foo {
3259+
/// fn try_into(self) -> Result<String, !>;
3260+
/// }
3261+
///
3262+
/// impl Foo for &str {
3263+
/// fn try_into(self) -> Result<String, !> {
3264+
/// Ok(String::from(self))
3265+
/// }
3266+
/// }
3267+
///
3268+
/// fn main() {
3269+
/// let x: String = "3".try_into().unwrap();
3270+
/// // ^^^^^^^^
3271+
/// // This call to try_into matches both Foo:try_into and TryInto::try_into as
3272+
/// // `TryInto` has been added to the Rust prelude in 2021 edition.
3273+
/// println!("{}", x);
3274+
/// }
3275+
/// ```
3276+
///
3277+
/// {{produces}}
3278+
///
3279+
/// ### Explanation
3280+
///
3281+
/// In Rust 2021, one of the important introductions is the [prelude changes], which add
3282+
/// `TryFrom`, `TryInto`, and `FromIterator` into the standard library's prelude. Since this
3283+
/// results in an amiguity as to which method/function to call when an existing `try_into`
3284+
/// method is called via dot-call syntax or a `try_from`/`from_iter` associated function
3285+
/// is called directly on a type.
3286+
///
3287+
/// [prelude changes]: https://blog.rust-lang.org/inside-rust/2021/03/04/planning-rust-2021.html#prelude-changes
3288+
pub FUTURE_PRELUDE_COLLISION,
3289+
Allow,
3290+
"detects the usage of trait methods which are ambiguous with traits added to the \
3291+
prelude in future editions",
3292+
@future_incompatible = FutureIncompatibleInfo {
3293+
reference: "issue #85684 <https://github.com/rust-lang/rust/issues/85684>",
3294+
edition: Some(Edition::Edition2021),
3295+
};
3296+
}

compiler/rustc_span/src/symbol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ symbols! {
586586
from,
587587
from_desugaring,
588588
from_generator,
589+
from_iter,
589590
from_method,
590591
from_output,
591592
from_residual,
@@ -1238,7 +1239,9 @@ symbols! {
12381239
truncf32,
12391240
truncf64,
12401241
try_blocks,
1242+
try_from,
12411243
try_from_trait,
1244+
try_into,
12421245
try_into_trait,
12431246
try_trait_v2,
12441247
tt,

compiler/rustc_typeck/src/check/expr.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
466466
expr: &'tcx hir::Expr<'tcx>,
467467
) -> Ty<'tcx> {
468468
let tcx = self.tcx;
469-
let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span);
469+
let (res, opt_ty, segs) =
470+
self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span);
470471
let ty = match res {
471472
Res::Err => {
472473
self.set_tainted_by_errors();
@@ -940,7 +941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
940941
// no need to check for bot/err -- callee does that
941942
let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
942943

943-
let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) {
944+
let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
944945
Ok(method) => {
945946
// We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
946947
// trigger this codepath causing `structuraly_resolved_type` to emit an error.

compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+28-21
Original file line numberDiff line numberDiff line change
@@ -906,13 +906,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
906906

907907
/// Resolves an associated value path into a base type and associated constant, or method
908908
/// resolution. The newly resolved definition is written into `type_dependent_defs`.
909-
pub fn resolve_ty_and_res_ufcs(
909+
pub fn resolve_ty_and_res_fully_qualified_call(
910910
&self,
911911
qpath: &'tcx QPath<'tcx>,
912912
hir_id: hir::HirId,
913913
span: Span,
914914
) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) {
915-
debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span);
915+
debug!(
916+
"resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}",
917+
qpath, hir_id, span
918+
);
916919
let (ty, qself, item_segment) = match *qpath {
917920
QPath::Resolved(ref opt_qself, ref path) => {
918921
return (
@@ -922,7 +925,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
922925
);
923926
}
924927
QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment),
925-
QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"),
928+
QPath::LangItem(..) => {
929+
bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`")
930+
}
926931
};
927932
if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id)
928933
{
@@ -932,25 +937,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
932937
return (def, Some(ty), slice::from_ref(&**item_segment));
933938
}
934939
let item_name = item_segment.ident;
935-
let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| {
936-
let result = match error {
937-
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
938-
_ => Err(ErrorReported),
939-
};
940-
if item_name.name != kw::Empty {
941-
if let Some(mut e) = self.report_method_error(
942-
span,
943-
ty,
944-
item_name,
945-
SelfSource::QPath(qself),
946-
error,
947-
None,
948-
) {
949-
e.emit();
940+
let result = self
941+
.resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id)
942+
.or_else(|error| {
943+
let result = match error {
944+
method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)),
945+
_ => Err(ErrorReported),
946+
};
947+
if item_name.name != kw::Empty {
948+
if let Some(mut e) = self.report_method_error(
949+
span,
950+
ty,
951+
item_name,
952+
SelfSource::QPath(qself),
953+
error,
954+
None,
955+
) {
956+
e.emit();
957+
}
950958
}
951-
}
952-
result
953-
});
959+
result
960+
});
954961

955962
if result.is_ok() {
956963
self.maybe_lint_bare_trait(qpath, hir_id);

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

+41-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html
44
55
mod confirm;
6+
mod prelude2021;
67
pub mod probe;
78
mod suggest;
89

@@ -173,14 +174,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
173174
///
174175
/// # Arguments
175176
///
176-
/// Given a method call like `foo.bar::<T1,...Tn>(...)`:
177+
/// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`:
177178
///
178179
/// * `self`: the surrounding `FnCtxt` (!)
179180
/// * `self_ty`: the (unadjusted) type of the self expression (`foo`)
180181
/// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`)
181182
/// * `span`: the span for the method call
182183
/// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`)
183184
/// * `self_expr`: the self expression (`foo`)
185+
/// * `args`: the expressions of the arguments (`a, b + 1, ...`)
184186
#[instrument(level = "debug", skip(self, call_expr, self_expr))]
185187
pub fn lookup_method(
186188
&self,
@@ -189,6 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
189191
span: Span,
190192
call_expr: &'tcx hir::Expr<'tcx>,
191193
self_expr: &'tcx hir::Expr<'tcx>,
194+
args: &'tcx [hir::Expr<'tcx>],
192195
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
193196
debug!(
194197
"lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
@@ -198,6 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
198201
let pick =
199202
self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
200203

204+
self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);
205+
201206
for import_id in &pick.import_ids {
202207
debug!("used_trait_import: {:?}", import_id);
203208
Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports)
@@ -417,16 +422,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
417422
Some(InferOk { obligations, value: callee })
418423
}
419424

425+
/// Performs a [full-qualified function call] (formerly "universal function call") lookup. If
426+
/// lookup is successful, it will return the type of definition and the [`DefId`] of the found
427+
/// function definition.
428+
///
429+
/// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls
430+
///
431+
/// # Arguments
432+
///
433+
/// Given a function call like `Foo::bar::<T1,...Tn>(...)`:
434+
///
435+
/// * `self`: the surrounding `FnCtxt` (!)
436+
/// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`)
437+
/// * `method_name`: the identifier of the function within the container type (`bar`)
438+
/// * `self_ty`: the type to search within (`Foo`)
439+
/// * `self_ty_span` the span for the type being searched within (span of `Foo`)
440+
/// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call
420441
#[instrument(level = "debug", skip(self))]
421-
pub fn resolve_ufcs(
442+
pub fn resolve_fully_qualified_call(
422443
&self,
423444
span: Span,
424445
method_name: Ident,
425446
self_ty: Ty<'tcx>,
447+
self_ty_span: Span,
426448
expr_id: hir::HirId,
427449
) -> Result<(DefKind, DefId), MethodError<'tcx>> {
428450
debug!(
429-
"resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}",
451+
"resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}",
430452
method_name, self_ty, expr_id,
431453
);
432454

@@ -463,18 +485,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
463485
expr_id,
464486
ProbeScope::TraitsInScope,
465487
)?;
466-
debug!("resolve_ufcs: pick={:?}", pick);
488+
489+
self.lint_fully_qualified_call_from_2018(
490+
span,
491+
method_name,
492+
self_ty,
493+
self_ty_span,
494+
expr_id,
495+
&pick,
496+
);
497+
498+
debug!("resolve_fully_qualified_call: pick={:?}", pick);
467499
{
468500
let mut typeck_results = self.typeck_results.borrow_mut();
469501
let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap();
470502
for import_id in pick.import_ids {
471-
debug!("resolve_ufcs: used_trait_import: {:?}", import_id);
503+
debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id);
472504
used_trait_imports.insert(import_id);
473505
}
474506
}
475507

476508
let def_kind = pick.item.kind.as_def_kind();
477-
debug!("resolve_ufcs: def_kind={:?}, def_id={:?}", def_kind, pick.item.def_id);
509+
debug!(
510+
"resolve_fully_qualified_call: def_kind={:?}, def_id={:?}",
511+
def_kind, pick.item.def_id
512+
);
478513
tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span));
479514
Ok((def_kind, pick.item.def_id))
480515
}

0 commit comments

Comments
 (0)