From 79388aa067e27824973f8f25fd6c4775d35388fd Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Tue, 25 May 2021 23:27:26 -0400 Subject: [PATCH 01/25] Add future_prelude_collision lint --- compiler/rustc_lint_defs/src/builtin.rs | 45 +++++++++++++++++ compiler/rustc_span/src/symbol.rs | 2 + compiler/rustc_typeck/src/check/method/mod.rs | 49 ++++++++++++++++++- 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 352146d64635a..168ea713d1947 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 fb37c5e9c1eff..8019569ab9bd1 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 427102afee103..164b1a4910bcf 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) From 01bdb8e38a5ba8c0db8ccaaeab66b1edd315aeca Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Wed, 26 May 2021 00:53:30 -0400 Subject: [PATCH 02/25] Disable `future_prelude_collision` for 2021 edition --- compiler/rustc_typeck/src/check/method/mod.rs | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 164b1a4910bcf..ba4635cc1bf4f 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -22,6 +22,7 @@ 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::Span; use rustc_trait_selection::traits; @@ -199,48 +200,51 @@ 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(); - }, - ); + if span.edition() < Edition::Edition2021 { + 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(); + }, + ); + } } } } From a9dc234c436b17896e3109a88c12c093db515666 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Wed, 26 May 2021 21:52:56 -0400 Subject: [PATCH 03/25] Add docs for FnCtxt::resolve_ufcs --- compiler/rustc_typeck/src/check/method/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index ba4635cc1bf4f..d2bd4b0dd32ae 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -468,6 +468,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(InferOk { obligations, value: callee }) } + /// Performs "universal function call" lookup. If lookup is successful, it will return the type + /// of definition and the [`DefId`] of the found definition. + /// + /// # Arguments + /// + /// Given a function call like `Foo::bar::(...)`: + /// + /// * `self`: the surrounding `FnCtxt` (!) + /// * `span`: the span of the entire function call + /// * `method_name`: the identifier of the function within the container type (`bar`) + /// * `self_ty`: the type to search within (`Foo`) + /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call #[instrument(level = "debug", skip(self))] pub fn resolve_ufcs( &self, From 1626e1938ab6378c2b44891a1e4bd73c3f96719c Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Wed, 26 May 2021 23:00:19 -0400 Subject: [PATCH 04/25] Add support for associated functions to `future_prelude_collision` lint --- compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/method/mod.rs | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8019569ab9bd1..19f55c47d581c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -587,6 +587,7 @@ symbols! { from, from_desugaring, from_generator, + from_iter, from_method, from_output, from_residual, diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index d2bd4b0dd32ae..1960e757bad59 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -201,7 +201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; if span.edition() < Edition::Edition2021 { - if let sym::try_from | sym::try_into = segment.ident.name { + if let 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) { @@ -526,6 +526,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_id, ProbeScope::TraitsInScope, )?; + + if span.edition() < Edition::Edition2021 { + if let sym::try_into | sym::try_from | sym::from_iter = method_name.name { + if let probe::PickKind::TraitPick = pick.kind { + if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| { + let trait_name = tcx.def_path_str(pick.item.container.assert_trait()); + + let mut lint = lint.build(&format!( + "trait method `{}` will become ambiguous in Rust 2021", + method_name.name + )); + + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint.emit(); + }); + } + } + } + } + debug!("resolve_ufcs: pick={:?}", pick); { let mut typeck_results = self.typeck_results.borrow_mut(); From c341d5b9d7ebf0f2a2000de81fe236ae2f3445b0 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Wed, 26 May 2021 23:27:24 -0400 Subject: [PATCH 05/25] Improve documentation for `future_prelude_collision` lint --- compiler/rustc_lint_defs/src/builtin.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 168ea713d1947..cfa7c160b0ff5 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3275,9 +3275,10 @@ declare_lint! { /// ### 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. + /// `TryFrom`, `TryInto`, and `FromIterator` into the standard library's prelude. Since this + /// results in an amiguity as to which method/function to call when an existing `try_into` + /// method is called via dot-call syntax or a `try_from`/`from_iter` associated function + /// is called directly on a type. /// /// [prelude changes]: https://blog.rust-lang.org/inside-rust/2021/03/04/planning-rust-2021.html#prelude-changes pub FUTURE_PRELUDE_COLLISION, From 35af38353e928c0d244e9bf0daaeaea8bf064fbf Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 00:30:55 -0400 Subject: [PATCH 06/25] Add UI tests for `future_prelude_collision` lint --- compiler/rustc_typeck/src/check/method/mod.rs | 2 +- .../ui/lint/future-prelude-collision.fixed | 57 +++++++++++++++++++ src/test/ui/lint/future-prelude-collision.rs | 57 +++++++++++++++++++ .../ui/lint/future-prelude-collision.stderr | 28 +++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/lint/future-prelude-collision.fixed create mode 100644 src/test/ui/lint/future-prelude-collision.rs create mode 100644 src/test/ui/lint/future-prelude-collision.stderr diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 1960e757bad59..1d9ee4595d107 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -535,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let trait_name = tcx.def_path_str(pick.item.container.assert_trait()); let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", + "trait-associated function `{}` will become ambiguous in Rust 2021", method_name.name )); diff --git a/src/test/ui/lint/future-prelude-collision.fixed b/src/test/ui/lint/future-prelude-collision.fixed new file mode 100644 index 0000000000000..06d0bb0153830 --- /dev/null +++ b/src/test/ui/lint/future-prelude-collision.fixed @@ -0,0 +1,57 @@ +// run-rustfix +// edition:2018 +// check-pass + +trait TryIntoU32 { + fn try_into(self) -> Result; +} + +impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } +} + +trait TryFromU8: Sized { + fn try_from(x: u8) -> Result; +} + +impl TryFromU8 for u32 { + fn try_from(x: u8) -> Result { + Ok(x as u32) + } +} + +trait FromByteIterator { + fn from_iter(iter: T) -> Self + where T: Iterator; +} + +impl FromByteIterator for Vec { + fn from_iter(iter: T) -> Self + where T: Iterator + { + iter.collect() + } +} + +fn main() { + // test dot-call that will break in 2021 edition + let _: u32 = TryIntoU32::try_into(3u8).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + // test associated function call that will break in 2021 edition + let _ = ::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + + // test reverse turbofish too + let _ = as FromByteIterator>::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); + //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + + // negative testing lint (this line should *not* emit a warning) + let _: u32 = TryFromU8::try_from(3u8).unwrap(); + + // test type omission + let _: u32 = <_ as TryFromU8>::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 +} diff --git a/src/test/ui/lint/future-prelude-collision.rs b/src/test/ui/lint/future-prelude-collision.rs new file mode 100644 index 0000000000000..61658ac2725da --- /dev/null +++ b/src/test/ui/lint/future-prelude-collision.rs @@ -0,0 +1,57 @@ +// run-rustfix +// edition:2018 +// check-pass + +trait TryIntoU32 { + fn try_into(self) -> Result; +} + +impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } +} + +trait TryFromU8: Sized { + fn try_from(x: u8) -> Result; +} + +impl TryFromU8 for u32 { + fn try_from(x: u8) -> Result { + Ok(x as u32) + } +} + +trait FromByteIterator { + fn from_iter(iter: T) -> Self + where T: Iterator; +} + +impl FromByteIterator for Vec { + fn from_iter(iter: T) -> Self + where T: Iterator + { + iter.collect() + } +} + +fn main() { + // test dot-call that will break in 2021 edition + let _: u32 = 3u8.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + // test associated function call that will break in 2021 edition + let _ = u32::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + + // test reverse turbofish too + let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); + //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + + // negative testing lint (this line should *not* emit a warning) + let _: u32 = TryFromU8::try_from(3u8).unwrap(); + + // test type omission + let _: u32 = <_>::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 +} diff --git a/src/test/ui/lint/future-prelude-collision.stderr b/src/test/ui/lint/future-prelude-collision.stderr new file mode 100644 index 0000000000000..79f4f1412d2f0 --- /dev/null +++ b/src/test/ui/lint/future-prelude-collision.stderr @@ -0,0 +1,28 @@ +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:40:18 + | +LL | let _: u32 = 3u8.try_into().unwrap(); + | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(3u8)` + | + = note: `#[warn(future_prelude_collision)]` on by default + +warning: trait-associated function `try_from` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:44:13 + | +LL | let _ = u32::try_from(3u8).unwrap(); + | ^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` + +warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:48:13 + | +LL | let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); + | ^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as FromByteIterator>::from_iter` + +warning: trait-associated function `try_from` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:55:18 + | +LL | let _: u32 = <_>::try_from(3u8).unwrap(); + | ^^^^^^^^^^^^^ help: disambiguate the associated function: `<_ as TryFromU8>::try_from` + +warning: 4 warnings emitted + From c41a157b908743812fc179e64654dbe0a65caaca Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 00:56:37 -0400 Subject: [PATCH 07/25] Fix incorrect argument description on FnCtxt::resolve_ufcs --- compiler/rustc_typeck/src/check/method/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 1d9ee4595d107..ced08734bcf39 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -469,14 +469,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Performs "universal function call" lookup. If lookup is successful, it will return the type - /// of definition and the [`DefId`] of the found definition. + /// of definition and the [`DefId`] of the found function definition. /// /// # Arguments /// /// Given a function call like `Foo::bar::(...)`: /// /// * `self`: the surrounding `FnCtxt` (!) - /// * `span`: the span of the entire function call + /// * `span`: the span of the call, excluding arguments (`Foo::bar::`) /// * `method_name`: the identifier of the function within the container type (`bar`) /// * `self_ty`: the type to search within (`Foo`) /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call From 327697a540cfab44c7d9c9c7c62948f8f9667fc5 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 13:28:55 -0400 Subject: [PATCH 08/25] Fix autoderef and autoref for `future_prelude_collision` lint --- compiler/rustc_typeck/src/check/method/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index ced08734bcf39..c04979ed75c89 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -11,6 +11,7 @@ pub use self::CandidateSource::*; pub use self::MethodError::*; 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; @@ -222,12 +223,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) { + let derefs = "*".repeat(pick.autoderefs); + let self_adjusted = match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { + mutbl: Mutability::Mut, .. + }) => format!("&mut {}{}", derefs, self_expr), + Some(probe::AutorefOrPtrAdjustment::Autoref { + mutbl: Mutability::Not, .. + }) => format!("&{}{}", derefs, self_expr), + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None + => format!("{}{}", derefs, self_expr), + }; lint.span_suggestion( sp, "disambiguate the associated function", format!( "{}::{}({})", - trait_name, segment.ident.name, self_expr, + trait_name, segment.ident.name, self_adjusted, ), Applicability::MachineApplicable, ); From eb5e0af3a990747174fb8ee84fbfe52e57f00ecf Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 13:38:06 -0400 Subject: [PATCH 09/25] Add autoderef and autoref tests for `future_prelude_collision` lint --- .../ui/lint/future-prelude-collision.fixed | 15 +++++++++++++ src/test/ui/lint/future-prelude-collision.rs | 15 +++++++++++++ .../ui/lint/future-prelude-collision.stderr | 22 ++++++++++++++----- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/test/ui/lint/future-prelude-collision.fixed b/src/test/ui/lint/future-prelude-collision.fixed index 06d0bb0153830..6858616adde13 100644 --- a/src/test/ui/lint/future-prelude-collision.fixed +++ b/src/test/ui/lint/future-prelude-collision.fixed @@ -12,6 +12,13 @@ impl TryIntoU32 for u8 { } } +// needed for autoref test +impl TryIntoU32 for &f32 { + fn try_into(self) -> Result { + Ok(*self as u32) + } +} + trait TryFromU8: Sized { fn try_from(x: u8) -> Result; } @@ -54,4 +61,12 @@ fn main() { // test type omission let _: u32 = <_ as TryFromU8>::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + + // test autoderef + let _: u32 = TryIntoU32::try_into(*(&3u8)).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + // test autoref + let _: u32 = TryIntoU32::try_into(&3.0).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.rs b/src/test/ui/lint/future-prelude-collision.rs index 61658ac2725da..ba02757ab3275 100644 --- a/src/test/ui/lint/future-prelude-collision.rs +++ b/src/test/ui/lint/future-prelude-collision.rs @@ -12,6 +12,13 @@ impl TryIntoU32 for u8 { } } +// needed for autoref test +impl TryIntoU32 for &f32 { + fn try_into(self) -> Result { + Ok(*self as u32) + } +} + trait TryFromU8: Sized { fn try_from(x: u8) -> Result; } @@ -54,4 +61,12 @@ fn main() { // test type omission let _: u32 = <_>::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + + // test autoderef + let _: u32 = (&3u8).try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + // test autoref + let _: u32 = 3.0.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.stderr b/src/test/ui/lint/future-prelude-collision.stderr index 79f4f1412d2f0..3a3c04a093d83 100644 --- a/src/test/ui/lint/future-prelude-collision.stderr +++ b/src/test/ui/lint/future-prelude-collision.stderr @@ -1,5 +1,5 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:40:18 + --> $DIR/future-prelude-collision.rs:47:18 | LL | let _: u32 = 3u8.try_into().unwrap(); | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(3u8)` @@ -7,22 +7,34 @@ LL | let _: u32 = 3u8.try_into().unwrap(); = note: `#[warn(future_prelude_collision)]` on by default warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:44:13 + --> $DIR/future-prelude-collision.rs:51:13 | LL | let _ = u32::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:48:13 + --> $DIR/future-prelude-collision.rs:55:13 | LL | let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); | ^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as FromByteIterator>::from_iter` warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:55:18 + --> $DIR/future-prelude-collision.rs:62:18 | LL | let _: u32 = <_>::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `<_ as TryFromU8>::try_from` -warning: 4 warnings emitted +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:66:18 + | +LL | let _: u32 = (&3u8).try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(*(&3u8))` + +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:70:18 + | +LL | let _: u32 = 3.0.try_into().unwrap(); + | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(&3.0)` + +warning: 6 warnings emitted From 93c60f26bfe5a6ae07774fb9aa03f3cacd48bae4 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 14:54:16 -0400 Subject: [PATCH 10/25] Fix missing generic parameters from `future_prelude_collision` lint suggestion --- compiler/rustc_typeck/src/check/method/mod.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index c04979ed75c89..9497f939b3d28 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -544,7 +544,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let probe::PickKind::TraitPick = pick.kind { if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| { - let trait_name = tcx.def_path_str(pick.item.container.assert_trait()); + let trait_def_id = pick.item.container.assert_trait(); + let trait_generics = tcx.generics_of(trait_def_id); + let parameter_count = trait_generics.count() - (trait_generics.has_self as usize); + + let trait_name = if parameter_count == 0 { + tcx.def_path_str(trait_def_id) + } else { + format!( + "{}<{}>", + tcx.def_path_str(trait_def_id), + std::iter::repeat("_").take(parameter_count).collect::>().join(", ") + ) + }; let mut lint = lint.build(&format!( "trait-associated function `{}` will become ambiguous in Rust 2021", From cb4999242d4921988b371dc62e40c877e7097a1f Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 22:29:22 -0400 Subject: [PATCH 11/25] Fix `future_prelude_collision` lint breaking for pointer mutabilty coercion --- compiler/rustc_typeck/src/check/method/mod.rs | 193 ++++++++++-------- .../ui/lint/future-prelude-collision.fixed | 11 + src/test/ui/lint/future-prelude-collision.rs | 11 + .../ui/lint/future-prelude-collision.stderr | 20 +- 4 files changed, 144 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 9497f939b3d28..a1383fc4af0ae 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -203,60 +203,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if span.edition() < Edition::Edition2021 { if let 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) - { - let derefs = "*".repeat(pick.autoderefs); - let self_adjusted = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Mut, .. - }) => format!("&mut {}{}", derefs, self_expr), - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Not, .. - }) => format!("&{}{}", derefs, self_expr), - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None - => format!("{}{}", derefs, self_expr), + 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 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::>() + .join(", ") + ) + }; + + 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(); - }, - ); - } + 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(); + }, + ); } } } @@ -541,38 +562,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if span.edition() < Edition::Edition2021 { if let sym::try_into | sym::try_from | sym::from_iter = method_name.name { - if let probe::PickKind::TraitPick = pick.kind { - if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { - tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| { - let trait_def_id = pick.item.container.assert_trait(); - let trait_generics = tcx.generics_of(trait_def_id); - let parameter_count = trait_generics.count() - (trait_generics.has_self as usize); - - let trait_name = if parameter_count == 0 { - tcx.def_path_str(trait_def_id) - } else { - format!( - "{}<{}>", - tcx.def_path_str(trait_def_id), - std::iter::repeat("_").take(parameter_count).collect::>().join(", ") - ) - }; - - let mut lint = lint.build(&format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); + if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + 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::>() + .join(", ") + ) + }; + + let mut lint = lint.build(&format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + )); + + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); - lint.emit(); - }); - } + lint.emit(); + }); } } } diff --git a/src/test/ui/lint/future-prelude-collision.fixed b/src/test/ui/lint/future-prelude-collision.fixed index 6858616adde13..2a0f74edc09ae 100644 --- a/src/test/ui/lint/future-prelude-collision.fixed +++ b/src/test/ui/lint/future-prelude-collision.fixed @@ -29,6 +29,12 @@ impl TryFromU8 for u32 { } } +impl TryIntoU32 for *const u16 { + fn try_into(self) -> Result { + Ok(unsafe { *self } as u32) + } +} + trait FromByteIterator { fn from_iter(iter: T) -> Self where T: Iterator; @@ -69,4 +75,9 @@ fn main() { // test autoref let _: u32 = TryIntoU32::try_into(&3.0).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + let mut data = 3u16; + let mut_ptr = std::ptr::addr_of_mut!(data); + let _: u32 = TryIntoU32::try_into(mut_ptr as *const _).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.rs b/src/test/ui/lint/future-prelude-collision.rs index ba02757ab3275..9a38bc065bf69 100644 --- a/src/test/ui/lint/future-prelude-collision.rs +++ b/src/test/ui/lint/future-prelude-collision.rs @@ -29,6 +29,12 @@ impl TryFromU8 for u32 { } } +impl TryIntoU32 for *const u16 { + fn try_into(self) -> Result { + Ok(unsafe { *self } as u32) + } +} + trait FromByteIterator { fn from_iter(iter: T) -> Self where T: Iterator; @@ -69,4 +75,9 @@ fn main() { // test autoref let _: u32 = 3.0.try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + let mut data = 3u16; + let mut_ptr = std::ptr::addr_of_mut!(data); + let _: u32 = mut_ptr.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.stderr b/src/test/ui/lint/future-prelude-collision.stderr index 3a3c04a093d83..13c8b8afddfb4 100644 --- a/src/test/ui/lint/future-prelude-collision.stderr +++ b/src/test/ui/lint/future-prelude-collision.stderr @@ -1,5 +1,5 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:47:18 + --> $DIR/future-prelude-collision.rs:53:18 | LL | let _: u32 = 3u8.try_into().unwrap(); | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(3u8)` @@ -7,34 +7,40 @@ LL | let _: u32 = 3u8.try_into().unwrap(); = note: `#[warn(future_prelude_collision)]` on by default warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:51:13 + --> $DIR/future-prelude-collision.rs:57:13 | LL | let _ = u32::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:55:13 + --> $DIR/future-prelude-collision.rs:61:13 | LL | let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); | ^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as FromByteIterator>::from_iter` warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:62:18 + --> $DIR/future-prelude-collision.rs:68:18 | LL | let _: u32 = <_>::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `<_ as TryFromU8>::try_from` warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:66:18 + --> $DIR/future-prelude-collision.rs:72:18 | LL | let _: u32 = (&3u8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(*(&3u8))` warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:70:18 + --> $DIR/future-prelude-collision.rs:76:18 | LL | let _: u32 = 3.0.try_into().unwrap(); | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(&3.0)` -warning: 6 warnings emitted +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:81:18 + | +LL | let _: u32 = mut_ptr.try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(mut_ptr as *const _)` + +warning: 7 warnings emitted From 4a21a0bebc0493c963525d827c34e62a726be249 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Thu, 27 May 2021 23:39:07 -0400 Subject: [PATCH 12/25] Fix `future_prelude_collision` not maintaining type aliases --- compiler/rustc_typeck/src/check/expr.rs | 3 +- .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 49 +++++++++++-------- compiler/rustc_typeck/src/check/method/mod.rs | 28 ++++++++--- compiler/rustc_typeck/src/check/pat.rs | 7 ++- .../ui/lint/future-prelude-collision.fixed | 4 ++ src/test/ui/lint/future-prelude-collision.rs | 4 ++ .../ui/lint/future-prelude-collision.stderr | 8 ++- 7 files changed, 71 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index d0cbb58fb10eb..e6a2c5d5d14de 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -466,7 +466,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, ) -> Ty<'tcx> { let tcx = self.tcx; - let (res, opt_ty, segs) = self.resolve_ty_and_res_ufcs(qpath, expr.hir_id, expr.span); + let (res, opt_ty, segs) = + self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); let ty = match res { Res::Err => { self.set_tainted_by_errors(); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 96569ae0e7720..8e33f4f9e12f6 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -906,13 +906,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Resolves an associated value path into a base type and associated constant, or method /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_ufcs( + pub fn resolve_ty_and_res_fully_qualified_call( &self, qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { - debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); + debug!( + "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", + qpath, hir_id, span + ); let (ty, qself, item_segment) = match *qpath { QPath::Resolved(ref opt_qself, ref path) => { return ( @@ -922,7 +925,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), - QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), + QPath::LangItem(..) => { + bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") + } }; if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) { @@ -932,25 +937,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return (def, Some(ty), slice::from_ref(&**item_segment)); } let item_name = item_segment.ident; - let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { - let result = match error { - method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorReported), - }; - if item_name.name != kw::Empty { - if let Some(mut e) = self.report_method_error( - span, - ty, - item_name, - SelfSource::QPath(qself), - error, - None, - ) { - e.emit(); + let result = self + .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id) + .or_else(|error| { + let result = match error { + method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), + _ => Err(ErrorReported), + }; + if item_name.name != kw::Empty { + if let Some(mut e) = self.report_method_error( + span, + ty, + item_name, + SelfSource::QPath(qself), + error, + None, + ) { + e.emit(); + } } - } - result - }); + result + }); if result.is_ok() { self.maybe_lint_bare_trait(qpath, hir_id); diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index a1383fc4af0ae..2198455c16347 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -501,8 +501,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(InferOk { obligations, value: callee }) } - /// Performs "universal function call" lookup. If lookup is successful, it will return the type - /// of definition and the [`DefId`] of the found function definition. + /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If + /// lookup is successful, it will return the type of definition and the [`DefId`] of the found + /// function definition. + /// + /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls /// /// # Arguments /// @@ -512,17 +515,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `span`: the span of the call, excluding arguments (`Foo::bar::`) /// * `method_name`: the identifier of the function within the container type (`bar`) /// * `self_ty`: the type to search within (`Foo`) + /// * `self_ty_span` the span for the type being searched within (span of `Foo`) /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call #[instrument(level = "debug", skip(self))] - pub fn resolve_ufcs( + pub fn resolve_fully_qualified_call( &self, span: Span, method_name: Ident, self_ty: Ty<'tcx>, + self_ty_span: Span, expr_id: hir::HirId, ) -> Result<(DefKind, DefId), MethodError<'tcx>> { debug!( - "resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}", + "resolve_fully_qualified_call: method_name={:?} self_ty={:?} expr_id={:?}", method_name, self_ty, expr_id, ); @@ -589,6 +594,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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", @@ -602,18 +613,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - debug!("resolve_ufcs: pick={:?}", pick); + debug!("resolve_fully_qualified_call: pick={:?}", pick); { let mut typeck_results = self.typeck_results.borrow_mut(); let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); for import_id in pick.import_ids { - debug!("resolve_ufcs: used_trait_import: {:?}", import_id); + debug!("resolve_fully_qualified_call: used_trait_import: {:?}", import_id); used_trait_imports.insert(import_id); } } let def_kind = pick.item.kind.as_def_kind(); - debug!("resolve_ufcs: def_kind={:?}, def_id={:?}", def_kind, pick.item.def_id); + debug!( + "resolve_fully_qualified_call: def_kind={:?}, def_id={:?}", + def_kind, pick.item.def_id + ); tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); Ok((def_kind, pick.item.def_id)) } diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 3ac760e23634e..2879614d0c808 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -160,7 +160,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ti: TopInfo<'tcx>, ) { let path_res = match &pat.kind { - PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)), + PatKind::Path(qpath) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + } _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); @@ -904,7 +906,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // Resolve the path and check the definition for errors. - let (res, opt_ty, segments) = self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span); + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); if res == Res::Err { self.set_tainted_by_errors(); on_error(); diff --git a/src/test/ui/lint/future-prelude-collision.fixed b/src/test/ui/lint/future-prelude-collision.fixed index 2a0f74edc09ae..922c0d54a0075 100644 --- a/src/test/ui/lint/future-prelude-collision.fixed +++ b/src/test/ui/lint/future-prelude-collision.fixed @@ -80,4 +80,8 @@ fn main() { let mut_ptr = std::ptr::addr_of_mut!(data); let _: u32 = TryIntoU32::try_into(mut_ptr as *const _).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + type U32Alias = u32; + let _ = ::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.rs b/src/test/ui/lint/future-prelude-collision.rs index 9a38bc065bf69..154dde16d9e9b 100644 --- a/src/test/ui/lint/future-prelude-collision.rs +++ b/src/test/ui/lint/future-prelude-collision.rs @@ -80,4 +80,8 @@ fn main() { let mut_ptr = std::ptr::addr_of_mut!(data); let _: u32 = mut_ptr.try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + + type U32Alias = u32; + let _ = U32Alias::try_from(3u8).unwrap(); + //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 } diff --git a/src/test/ui/lint/future-prelude-collision.stderr b/src/test/ui/lint/future-prelude-collision.stderr index 13c8b8afddfb4..9c92074c36f58 100644 --- a/src/test/ui/lint/future-prelude-collision.stderr +++ b/src/test/ui/lint/future-prelude-collision.stderr @@ -42,5 +42,11 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 LL | let _: u32 = mut_ptr.try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(mut_ptr as *const _)` -warning: 7 warnings emitted +warning: trait-associated function `try_from` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision.rs:85:13 + | +LL | let _ = U32Alias::try_from(3u8).unwrap(); + | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` + +warning: 8 warnings emitted From 64c61a32f6051fe769928b09bfb6e829b74d6f5e Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Fri, 28 May 2021 00:55:32 -0400 Subject: [PATCH 13/25] Fix `future_prelude_collision` adding unneeded generic arguments --- compiler/rustc_typeck/src/check/method/mod.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 2198455c16347..4a855d6e40833 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -210,22 +210,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr.span, |lint| { let sp = call_expr.span; - 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::>() - .join(", ") - ) - }; + 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", From 32408cf0d9cf997d6665ae7cff1b9f9c3c034832 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 4 Jun 2021 15:07:51 -0400 Subject: [PATCH 14/25] move test to rust-2021 directory --- src/test/ui/{lint => rust-2021}/future-prelude-collision.fixed | 0 src/test/ui/{lint => rust-2021}/future-prelude-collision.rs | 0 src/test/ui/{lint => rust-2021}/future-prelude-collision.stderr | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{lint => rust-2021}/future-prelude-collision.fixed (100%) rename src/test/ui/{lint => rust-2021}/future-prelude-collision.rs (100%) rename src/test/ui/{lint => rust-2021}/future-prelude-collision.stderr (100%) diff --git a/src/test/ui/lint/future-prelude-collision.fixed b/src/test/ui/rust-2021/future-prelude-collision.fixed similarity index 100% rename from src/test/ui/lint/future-prelude-collision.fixed rename to src/test/ui/rust-2021/future-prelude-collision.fixed diff --git a/src/test/ui/lint/future-prelude-collision.rs b/src/test/ui/rust-2021/future-prelude-collision.rs similarity index 100% rename from src/test/ui/lint/future-prelude-collision.rs rename to src/test/ui/rust-2021/future-prelude-collision.rs diff --git a/src/test/ui/lint/future-prelude-collision.stderr b/src/test/ui/rust-2021/future-prelude-collision.stderr similarity index 100% rename from src/test/ui/lint/future-prelude-collision.stderr rename to src/test/ui/rust-2021/future-prelude-collision.stderr From 19ba219e99f2f78fea290716759aa1856172332f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 4 Jun 2021 15:31:18 -0400 Subject: [PATCH 15/25] add inherent-method-collision test --- .../ui/rust-2021/inherent-method-collision.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/test/ui/rust-2021/inherent-method-collision.rs diff --git a/src/test/ui/rust-2021/inherent-method-collision.rs b/src/test/ui/rust-2021/inherent-method-collision.rs new file mode 100644 index 0000000000000..c638351d5fc09 --- /dev/null +++ b/src/test/ui/rust-2021/inherent-method-collision.rs @@ -0,0 +1,15 @@ +// Test that we do NOT warn for inherent methods invoked via `T::` form. +// +// check-pass + +#![deny(future_prelude_collision)] + +pub struct MySeq {} + +impl MySeq { + pub fn from_iter(_: impl IntoIterator) {} +} + +fn main() { + MySeq::from_iter(Some(22)); +} From 8d42f3da6358e80c0f13dc057152e04b7283def8 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 14 Jun 2021 13:17:15 -0400 Subject: [PATCH 16/25] don't warn for fully qual inherent methods But do continue to warn for trait methods. --- compiler/rustc_typeck/src/check/method/mod.rs | 10 +++++++++- .../ui/rust-2021/generic-type-collision.fixed | 16 ++++++++++++++++ src/test/ui/rust-2021/generic-type-collision.rs | 16 ++++++++++++++++ .../ui/rust-2021/generic-type-collision.stderr | 10 ++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/rust-2021/generic-type-collision.fixed create mode 100644 src/test/ui/rust-2021/generic-type-collision.rs create mode 100644 src/test/ui/rust-2021/generic-type-collision.stderr diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 4a855d6e40833..f321d62edf6db 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -10,6 +10,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; @@ -552,7 +553,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if span.edition() < Edition::Edition2021 { if let sym::try_into | sym::try_from | sym::from_iter = method_name.name { - if !matches!(tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { + // 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. diff --git a/src/test/ui/rust-2021/generic-type-collision.fixed b/src/test/ui/rust-2021/generic-type-collision.fixed new file mode 100644 index 0000000000000..1ae2b95d515d8 --- /dev/null +++ b/src/test/ui/rust-2021/generic-type-collision.fixed @@ -0,0 +1,16 @@ +// check-pass +// run-rustfix +// edition 2018 + +trait MyTrait { + fn from_iter(x: Option); +} + +impl MyTrait<()> for Vec { + fn from_iter(_: Option<()>) {} +} + +fn main() { + as MyTrait<_>>::from_iter(None); + //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 +} diff --git a/src/test/ui/rust-2021/generic-type-collision.rs b/src/test/ui/rust-2021/generic-type-collision.rs new file mode 100644 index 0000000000000..e203656163c61 --- /dev/null +++ b/src/test/ui/rust-2021/generic-type-collision.rs @@ -0,0 +1,16 @@ +// check-pass +// run-rustfix +// edition 2018 + +trait MyTrait { + fn from_iter(x: Option); +} + +impl MyTrait<()> for Vec { + fn from_iter(_: Option<()>) {} +} + +fn main() { + >::from_iter(None); + //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 +} diff --git a/src/test/ui/rust-2021/generic-type-collision.stderr b/src/test/ui/rust-2021/generic-type-collision.stderr new file mode 100644 index 0000000000000..3acc6185a61b3 --- /dev/null +++ b/src/test/ui/rust-2021/generic-type-collision.stderr @@ -0,0 +1,10 @@ +warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 + --> $DIR/generic-type-collision.rs:14:5 + | +LL | >::from_iter(None); + | ^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as MyTrait<_>>::from_iter` + | + = note: `#[warn(future_prelude_collision)]` on by default + +warning: 1 warning emitted + From 17ab9c0ff985dfa31235bcfd44b57ef15610e9b9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 14 Jun 2021 13:43:58 -0400 Subject: [PATCH 17/25] extract Rust 2021 prelude logic to its own module --- compiler/rustc_typeck/src/check/method/mod.rs | 135 ++------------ .../src/check/method/prelude2021.rs | 167 ++++++++++++++++++ 2 files changed, 178 insertions(+), 124 deletions(-) create mode 100644 compiler/rustc_typeck/src/check/method/prelude2021.rs diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index f321d62edf6db..e956637dec1d6 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -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::>() - .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); { diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs new file mode 100644 index 0000000000000..b141a4f6f89da --- /dev/null +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -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::>().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(); + }); + } +} From 3efa5b4b83b3fe72570d50ce73b8382875a4bdfa Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Mon, 14 Jun 2021 22:43:19 -0400 Subject: [PATCH 18/25] Emit additional arguments in `future_prelude_collision` lint --- compiler/rustc_typeck/src/check/expr.rs | 2 +- compiler/rustc_typeck/src/check/method/mod.rs | 6 ++++-- .../src/check/method/prelude2021.rs | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index e6a2c5d5d14de..28ab6b1513310 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -941,7 +941,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // no need to check for bot/err -- callee does that let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t); - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr) { + let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { Ok(method) => { // We could add a "consider `foo::`" suggestion here, but I wasn't able to // trigger this codepath causing `structuraly_resolved_type` to emit an error. diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index e956637dec1d6..be6bc625d893f 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -174,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// # Arguments /// - /// Given a method call like `foo.bar::(...)`: + /// Given a method call like `foo.bar::(a, b + 1, ...)`: /// /// * `self`: the surrounding `FnCtxt` (!) /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) @@ -182,6 +182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// * `span`: the span for the method call /// * `call_expr`: the complete method call: (`foo.bar::(...)`) /// * `self_expr`: the self expression (`foo`) + /// * `args`: the expressions of the arguments (`a, b + 1, ...`) #[instrument(level = "debug", skip(self, call_expr, self_expr))] pub fn lookup_method( &self, @@ -190,6 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, + args: &'tcx [hir::Expr<'tcx>], ) -> Result, MethodError<'tcx>> { debug!( "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", @@ -199,7 +201,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; - self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick); + self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); for import_id in &pick.import_ids { debug!("used_trait_import: {:?}", import_id); diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index b141a4f6f89da..b7e41525bde47 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -20,6 +20,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, pick: &Pick<'tcx>, + args: &'tcx [hir::Expr<'tcx>], ) { debug!( "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", @@ -75,10 +76,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { format!("{}{}{}", autoref, derefs, self_expr) }; + let args = args + .iter() + .skip(1) + .map(|arg| { + format!( + ", {}", + self.sess().source_map().span_to_snippet(arg.span).unwrap() + ) + }) + .collect::(); + lint.span_suggestion( sp, "disambiguate the associated function", - format!("{}::{}({})", trait_name, segment.ident.name, self_adjusted,), + format!( + "{}::{}({}{})", + trait_name, segment.ident.name, self_adjusted, args + ), Applicability::MachineApplicable, ); } else { From 56108f67b13416636508fbfd15442a9e18a70e28 Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Tue, 15 Jun 2021 01:31:00 -0400 Subject: [PATCH 19/25] Add future_prelude_collision to 2021 compat group * Add to 2021 compatibility group * Set default to Allow --- compiler/rustc_lint_defs/src/builtin.rs | 6 ++- .../rust-2021/future-prelude-collision.fixed | 9 ++++ .../ui/rust-2021/future-prelude-collision.rs | 9 ++++ .../rust-2021/future-prelude-collision.stderr | 45 +++++++++++++++---- .../ui/rust-2021/generic-type-collision.fixed | 2 + .../ui/rust-2021/generic-type-collision.rs | 2 + .../rust-2021/generic-type-collision.stderr | 10 ++++- 7 files changed, 71 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index cfa7c160b0ff5..bef675563a45a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3282,7 +3282,11 @@ declare_lint! { /// /// [prelude changes]: https://blog.rust-lang.org/inside-rust/2021/03/04/planning-rust-2021.html#prelude-changes pub FUTURE_PRELUDE_COLLISION, - Warn, + Allow, "detects the usage of trait methods which are ambiguous with traits added to the \ prelude in future editions", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #85684 ", + edition: Some(Edition::Edition2021), + }; } diff --git a/src/test/ui/rust-2021/future-prelude-collision.fixed b/src/test/ui/rust-2021/future-prelude-collision.fixed index 922c0d54a0075..9ede9f3a2fb45 100644 --- a/src/test/ui/rust-2021/future-prelude-collision.fixed +++ b/src/test/ui/rust-2021/future-prelude-collision.fixed @@ -1,6 +1,7 @@ // run-rustfix // edition:2018 // check-pass +#![warn(future_prelude_collision)] trait TryIntoU32 { fn try_into(self) -> Result; @@ -52,14 +53,17 @@ fn main() { // test dot-call that will break in 2021 edition let _: u32 = TryIntoU32::try_into(3u8).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test associated function call that will break in 2021 edition let _ = ::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test reverse turbofish too let _ = as FromByteIterator>::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // negative testing lint (this line should *not* emit a warning) let _: u32 = TryFromU8::try_from(3u8).unwrap(); @@ -67,21 +71,26 @@ fn main() { // test type omission let _: u32 = <_ as TryFromU8>::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test autoderef let _: u32 = TryIntoU32::try_into(*(&3u8)).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test autoref let _: u32 = TryIntoU32::try_into(&3.0).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! let mut data = 3u16; let mut_ptr = std::ptr::addr_of_mut!(data); let _: u32 = TryIntoU32::try_into(mut_ptr as *const _).unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! type U32Alias = u32; let _ = ::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } diff --git a/src/test/ui/rust-2021/future-prelude-collision.rs b/src/test/ui/rust-2021/future-prelude-collision.rs index 154dde16d9e9b..914e910396a66 100644 --- a/src/test/ui/rust-2021/future-prelude-collision.rs +++ b/src/test/ui/rust-2021/future-prelude-collision.rs @@ -1,6 +1,7 @@ // run-rustfix // edition:2018 // check-pass +#![warn(future_prelude_collision)] trait TryIntoU32 { fn try_into(self) -> Result; @@ -52,14 +53,17 @@ fn main() { // test dot-call that will break in 2021 edition let _: u32 = 3u8.try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test associated function call that will break in 2021 edition let _ = u32::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test reverse turbofish too let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // negative testing lint (this line should *not* emit a warning) let _: u32 = TryFromU8::try_from(3u8).unwrap(); @@ -67,21 +71,26 @@ fn main() { // test type omission let _: u32 = <_>::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test autoderef let _: u32 = (&3u8).try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! // test autoref let _: u32 = 3.0.try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! let mut data = 3u16; let mut_ptr = std::ptr::addr_of_mut!(data); let _: u32 = mut_ptr.try_into().unwrap(); //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! type U32Alias = u32; let _ = U32Alias::try_from(3u8).unwrap(); //~^ WARNING trait-associated function `try_from` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } diff --git a/src/test/ui/rust-2021/future-prelude-collision.stderr b/src/test/ui/rust-2021/future-prelude-collision.stderr index 9c92074c36f58..190145ef4dbfc 100644 --- a/src/test/ui/rust-2021/future-prelude-collision.stderr +++ b/src/test/ui/rust-2021/future-prelude-collision.stderr @@ -1,52 +1,79 @@ warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:53:18 + --> $DIR/future-prelude-collision.rs:54:18 | LL | let _: u32 = 3u8.try_into().unwrap(); | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(3u8)` | - = note: `#[warn(future_prelude_collision)]` on by default +note: the lint level is defined here + --> $DIR/future-prelude-collision.rs:4:9 + | +LL | #![warn(future_prelude_collision)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:57:13 + --> $DIR/future-prelude-collision.rs:59:13 | LL | let _ = u32::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:61:13 + --> $DIR/future-prelude-collision.rs:64:13 | LL | let _ = >::from_iter(vec![1u8, 2, 3, 4, 5, 6].into_iter()); | ^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as FromByteIterator>::from_iter` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:68:18 + --> $DIR/future-prelude-collision.rs:72:18 | LL | let _: u32 = <_>::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^ help: disambiguate the associated function: `<_ as TryFromU8>::try_from` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:72:18 + --> $DIR/future-prelude-collision.rs:77:18 | LL | let _: u32 = (&3u8).try_into().unwrap(); | ^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(*(&3u8))` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:76:18 + --> $DIR/future-prelude-collision.rs:82:18 | LL | let _: u32 = 3.0.try_into().unwrap(); | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(&3.0)` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait method `try_into` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:81:18 + --> $DIR/future-prelude-collision.rs:88:18 | LL | let _: u32 = mut_ptr.try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(mut_ptr as *const _)` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: trait-associated function `try_from` will become ambiguous in Rust 2021 - --> $DIR/future-prelude-collision.rs:85:13 + --> $DIR/future-prelude-collision.rs:93:13 | LL | let _ = U32Alias::try_from(3u8).unwrap(); | ^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: `::try_from` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: 8 warnings emitted diff --git a/src/test/ui/rust-2021/generic-type-collision.fixed b/src/test/ui/rust-2021/generic-type-collision.fixed index 1ae2b95d515d8..00fb128a981e1 100644 --- a/src/test/ui/rust-2021/generic-type-collision.fixed +++ b/src/test/ui/rust-2021/generic-type-collision.fixed @@ -1,6 +1,7 @@ // check-pass // run-rustfix // edition 2018 +#![warn(future_prelude_collision)] trait MyTrait { fn from_iter(x: Option); @@ -13,4 +14,5 @@ impl MyTrait<()> for Vec { fn main() { as MyTrait<_>>::from_iter(None); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } diff --git a/src/test/ui/rust-2021/generic-type-collision.rs b/src/test/ui/rust-2021/generic-type-collision.rs index e203656163c61..406fba4d2479b 100644 --- a/src/test/ui/rust-2021/generic-type-collision.rs +++ b/src/test/ui/rust-2021/generic-type-collision.rs @@ -1,6 +1,7 @@ // check-pass // run-rustfix // edition 2018 +#![warn(future_prelude_collision)] trait MyTrait { fn from_iter(x: Option); @@ -13,4 +14,5 @@ impl MyTrait<()> for Vec { fn main() { >::from_iter(None); //~^ WARNING trait-associated function `from_iter` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } diff --git a/src/test/ui/rust-2021/generic-type-collision.stderr b/src/test/ui/rust-2021/generic-type-collision.stderr index 3acc6185a61b3..9374379d24763 100644 --- a/src/test/ui/rust-2021/generic-type-collision.stderr +++ b/src/test/ui/rust-2021/generic-type-collision.stderr @@ -1,10 +1,16 @@ warning: trait-associated function `from_iter` will become ambiguous in Rust 2021 - --> $DIR/generic-type-collision.rs:14:5 + --> $DIR/generic-type-collision.rs:15:5 | LL | >::from_iter(None); | ^^^^^^^^^^^^^^^^^^^^^ help: disambiguate the associated function: ` as MyTrait<_>>::from_iter` | - = note: `#[warn(future_prelude_collision)]` on by default +note: the lint level is defined here + --> $DIR/generic-type-collision.rs:4:9 + | +LL | #![warn(future_prelude_collision)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 warning: 1 warning emitted From dbc9da7962b7282c5752a7bc2c9907694b7c158c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 17 Jun 2021 06:10:38 -0400 Subject: [PATCH 20/25] WIP: Find the imports that were used to reach a method And add tests for some corner cases we have to consider. --- .../src/check/method/prelude2021.rs | 39 +++++++++++--- .../future-prelude-collision-imported.rs | 52 +++++++++++++++++++ .../future-prelude-collision-shadow.rs | 33 ++++++++++++ 3 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/rust-2021/future-prelude-collision-imported.rs create mode 100644 src/test/ui/rust-2021/future-prelude-collision-shadow.rs diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index b7e41525bde47..987ec032476d3 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -1,3 +1,5 @@ +use hir::def_id::DefId; +use hir::HirId; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir as hir; @@ -48,7 +50,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr.span, |lint| { let sp = call_expr.span; - let trait_name = self.tcx.def_path_str(pick.item.container.id()); + let trait_name = + self.trait_path_or_bare_name(call_expr.hir_id, pick.item.container.id()); let mut lint = lint.build(&format!( "trait method `{}` will become ambiguous in Rust 2021", @@ -144,16 +147,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 trait_path = self.trait_path_or_bare_name(expr_id, pick.item.container.id()); + let trait_generics = self.tcx.generics_of(pick.item.container.id()); - let parameter_count = type_generics.count() - (type_generics.has_self as usize); + let parameter_count = trait_generics.count() - (trait_generics.has_self as usize); let trait_name = if parameter_count == 0 { - type_name + trait_path } else { format!( "{}<{}>", - type_name, + trait_path, std::iter::repeat("_").take(parameter_count).collect::>().join(", ") ) }; @@ -179,4 +182,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint.emit(); }); } + + fn trait_path_or_bare_name(&self, expr_hir_id: HirId, trait_def_id: DefId) -> String { + self.trait_path(expr_hir_id, trait_def_id).unwrap_or_else(|| { + let key = self.tcx.def_key(trait_def_id); + format!("{}", key.disambiguated_data.data) + }) + } + + fn trait_path(&self, expr_hir_id: HirId, trait_def_id: DefId) -> Option { + let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?; + let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?; + if applicable_trait.import_ids.is_empty() { + // The trait was declared within the module, we only need to use its name. + return None; + } + + for &import_id in &applicable_trait.import_ids { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id); + let item = self.tcx.hir().expect_item(hir_id); + debug!(?item, ?import_id, "import_id"); + } + + return None; + } } diff --git a/src/test/ui/rust-2021/future-prelude-collision-imported.rs b/src/test/ui/rust-2021/future-prelude-collision-imported.rs new file mode 100644 index 0000000000000..e85a0bd725d42 --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-imported.rs @@ -0,0 +1,52 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(future_prelude_collision)] +#![allow(dead_code)] + +mod m { + pub trait TryIntoU32 { + fn try_into(self) -> Result; + } + + impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } + } + + pub trait AnotherTrick {} +} + +mod a { + use crate::m::TryIntoU32; + + fn main() { + // In this case, we can just use `TryIntoU32` + let _: u32 = 3u8.try_into().unwrap(); + } +} + +mod b { + use crate::m::AnotherTrick as TryIntoU32; + use crate::m::TryIntoU32 as _; + + fn main() { + // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use + // the path `crate::m::TryIntoU32` (with which it was imported). + let _: u32 = 3u8.try_into().unwrap(); + } +} + +mod c { + use super::m::TryIntoU32 as _; + use crate::m::AnotherTrick as TryIntoU32; + + fn main() { + // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use + // the path `super::m::TryIntoU32` (with which it was imported). + let _: u32 = 3u8.try_into().unwrap(); + } +} + +fn main() {} diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs new file mode 100644 index 0000000000000..ef19cf4d1e6ad --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs @@ -0,0 +1,33 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(future_prelude_collision)] +#![allow(dead_code)] + +mod m { + pub trait TryIntoU32 { + fn try_into(self) -> Result; + } + + impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } + } + + pub trait AnotherTrick {} +} + +mod d { + use crate::m::AnotherTrick as TryIntoU32; + use crate::m::*; + + fn main() { + // Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods + // to be available. + let _: u32 = 3u8.try_into().unwrap(); + //~^ ERROR no method name `try_into` found + } +} + +fn main() {} From 9bee7f0d0e1fee2a6a3ec2d04689d94ec238f034 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 18 Jun 2021 13:19:08 -0400 Subject: [PATCH 21/25] WIP: identify the case where we need to serialize path --- .../src/check/method/prelude2021.rs | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 987ec032476d3..5f6731b6c5219 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -1,10 +1,12 @@ use hir::def_id::DefId; use hir::HirId; +use hir::ItemKind; 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::kw::Underscore; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -51,7 +53,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |lint| { let sp = call_expr.span; let trait_name = - self.trait_path_or_bare_name(call_expr.hir_id, pick.item.container.id()); + self.trait_path_or_bare_name(span, call_expr.hir_id, pick.item.container.id()); let mut lint = lint.build(&format!( "trait method `{}` will become ambiguous in Rust 2021", @@ -147,7 +149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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 trait_path = self.trait_path_or_bare_name(expr_id, pick.item.container.id()); + let trait_path = self.trait_path_or_bare_name(span, expr_id, pick.item.container.id()); let trait_generics = self.tcx.generics_of(pick.item.container.id()); let parameter_count = trait_generics.count() - (trait_generics.has_self as usize); @@ -183,14 +185,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); } - fn trait_path_or_bare_name(&self, expr_hir_id: HirId, trait_def_id: DefId) -> String { - self.trait_path(expr_hir_id, trait_def_id).unwrap_or_else(|| { + fn trait_path_or_bare_name( + &self, + span: Span, + expr_hir_id: HirId, + trait_def_id: DefId, + ) -> String { + self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| { let key = self.tcx.def_key(trait_def_id); format!("{}", key.disambiguated_data.data) }) } - fn trait_path(&self, expr_hir_id: HirId, trait_def_id: DefId) -> Option { + fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option { let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?; let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?; if applicable_trait.import_ids.is_empty() { @@ -198,12 +205,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - for &import_id in &applicable_trait.import_ids { - let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id); - let item = self.tcx.hir().expect_item(hir_id); - debug!(?item, ?import_id, "import_id"); + let import_items: Vec<_> = applicable_trait + .import_ids + .iter() + .map(|&import_id| { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id); + self.tcx.hir().expect_item(hir_id) + }) + .collect(); + + // Find an identifier with which this trait was imported (note that `_` doesn't count). + let any_id = import_items + .iter() + .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None }) + .next(); + if let Some(any_id) = any_id { + return Some(format!("{}", any_id)); } - return None; + // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, + // so just take the first one. + match import_items[0].kind { + ItemKind::Use(path, _) => { + // FIXME: serialize path into something readable like a::b, there must be a fn for this + debug!("no name for trait, found import of path: {:?}", path); + return None; + } + _ => { + span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); + } + } } } From b18704dd58047e0ade59a425740b8a41ba1bd14b Mon Sep 17 00:00:00 2001 From: jam1garner <8260240+jam1garner@users.noreply.github.com> Date: Sat, 19 Jun 2021 18:42:24 -0400 Subject: [PATCH 22/25] Fix future_prelude_collision for object calls and use as _ --- .../src/check/method/prelude2021.rs | 195 +++++++++++++----- .../future-prelude-collision-imported.fixed | 59 ++++++ .../future-prelude-collision-imported.rs | 7 + .../future-prelude-collision-imported.stderr | 34 +++ .../future-prelude-collision-shadow.fixed | 33 +++ .../future-prelude-collision-shadow.rs | 4 +- .../future-prelude-collision-shadow.stderr | 40 ++++ 7 files changed, 313 insertions(+), 59 deletions(-) create mode 100644 src/test/ui/rust-2021/future-prelude-collision-imported.fixed create mode 100644 src/test/ui/rust-2021/future-prelude-collision-imported.stderr create mode 100644 src/test/ui/rust-2021/future-prelude-collision-shadow.fixed create mode 100644 src/test/ui/rust-2021/future-prelude-collision-shadow.stderr diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 5f6731b6c5219..0750567d3dd60 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -4,7 +4,7 @@ use hir::ItemKind; use rustc_ast::Mutability; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Ref, Ty}; use rustc_session::lint::builtin::FUTURE_PRELUDE_COLLISION; use rustc_span::symbol::kw::Underscore; use rustc_span::symbol::{sym, Ident}; @@ -46,21 +46,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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.trait_path_or_bare_name(span, call_expr.hir_id, 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) { + if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) { + // avoid repeatedly adding unneeded `&*`s + if pick.autoderefs == 1 + && matches!( + pick.autoref_or_ptr_adjustment, + Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) + ) + && matches!(self_ty.kind(), Ref(..)) + { + return; + } + // Inherent impls only require not relying on autoref and autoderef in order to + // ensure that the trait implementation won't be used + self.tcx.struct_span_lint_hir( + FUTURE_PRELUDE_COLLISION, + self_expr.hir_id, + self_expr.span, + |lint| { + let sp = self_expr.span; + + let mut lint = lint.build(&format!( + "trait method `{}` will become ambiguous in Rust 2021", + segment.ident.name + )); + let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { @@ -74,46 +84,115 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => "&", Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", }; - let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment + if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) { - format!("{}{} as *const _", derefs, self_expr) + 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 method call", + format!("({})", self_adjusted), + Applicability::MachineApplicable, + ); } else { - format!("{}{}{}", autoref, derefs, self_expr) - }; - let args = args - .iter() - .skip(1) - .map(|arg| { - format!( - ", {}", - self.sess().source_map().span_to_snippet(arg.span).unwrap() - ) - }) - .collect::(); - - lint.span_suggestion( - sp, - "disambiguate the associated function", - format!( - "{}::{}({}{})", - trait_name, segment.ident.name, self_adjusted, args - ), - Applicability::MachineApplicable, - ); - } else { - lint.span_help( - sp, - &format!( - "disambiguate the associated function with `{}::{}(...)`", - trait_name, segment.ident, - ), + let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{}(...) as *const _", derefs) + } else { + format!("{}{}...", autoref, derefs) + }; + lint.span_help( + sp, + &format!("disambiguate the method call with `({})`", self_adjusted,), + ); + } + + lint.emit(); + }, + ); + } else { + // trait implementations require full disambiguation to not clash with the new prelude + // additions (i.e. convert from dot-call to fully-qualified call) + 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.trait_path_or_bare_name( + span, + call_expr.hir_id, + pick.item.container.id(), ); - } - lint.emit(); - }, - ); + 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) + }; + let args = args + .iter() + .skip(1) + .map(|arg| { + format!( + ", {}", + self.sess().source_map().span_to_snippet(arg.span).unwrap() + ) + }) + .collect::(); + + lint.span_suggestion( + sp, + "disambiguate the associated function", + format!( + "{}::{}({}{})", + trait_name, segment.ident.name, self_adjusted, args + ), + 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( @@ -226,11 +305,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, // so just take the first one. match import_items[0].kind { - ItemKind::Use(path, _) => { - // FIXME: serialize path into something readable like a::b, there must be a fn for this - debug!("no name for trait, found import of path: {:?}", path); - return None; - } + ItemKind::Use(path, _) => Some( + path.segments + .iter() + .map(|segment| segment.ident.to_string()) + .collect::>() + .join("::"), + ), _ => { span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); } diff --git a/src/test/ui/rust-2021/future-prelude-collision-imported.fixed b/src/test/ui/rust-2021/future-prelude-collision-imported.fixed new file mode 100644 index 0000000000000..4f8fd9b345b28 --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-imported.fixed @@ -0,0 +1,59 @@ +// run-rustfix +// edition:2018 +// check-pass +#![warn(future_prelude_collision)] +#![allow(dead_code)] +#![allow(unused_imports)] + +mod m { + pub trait TryIntoU32 { + fn try_into(self) -> Result; + } + + impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } + } + + pub trait AnotherTrick {} +} + +mod a { + use crate::m::TryIntoU32; + + fn main() { + // In this case, we can just use `TryIntoU32` + let _: u32 = TryIntoU32::try_into(3u8).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + } +} + +mod b { + use crate::m::AnotherTrick as TryIntoU32; + use crate::m::TryIntoU32 as _; + + fn main() { + // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use + // the path `crate::m::TryIntoU32` (with which it was imported). + let _: u32 = crate::m::TryIntoU32::try_into(3u8).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + } +} + +mod c { + use super::m::TryIntoU32 as _; + use crate::m::AnotherTrick as TryIntoU32; + + fn main() { + // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use + // the path `super::m::TryIntoU32` (with which it was imported). + let _: u32 = super::m::TryIntoU32::try_into(3u8).unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + } +} + +fn main() {} diff --git a/src/test/ui/rust-2021/future-prelude-collision-imported.rs b/src/test/ui/rust-2021/future-prelude-collision-imported.rs index e85a0bd725d42..2ce1be6151b11 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-imported.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-imported.rs @@ -3,6 +3,7 @@ // check-pass #![warn(future_prelude_collision)] #![allow(dead_code)] +#![allow(unused_imports)] mod m { pub trait TryIntoU32 { @@ -24,6 +25,8 @@ mod a { fn main() { // In this case, we can just use `TryIntoU32` let _: u32 = 3u8.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } } @@ -35,6 +38,8 @@ mod b { // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use // the path `crate::m::TryIntoU32` (with which it was imported). let _: u32 = 3u8.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } } @@ -46,6 +51,8 @@ mod c { // In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use // the path `super::m::TryIntoU32` (with which it was imported). let _: u32 = 3u8.try_into().unwrap(); + //~^ WARNING trait method `try_into` will become ambiguous in Rust 2021 + //~^^ WARNING this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! } } diff --git a/src/test/ui/rust-2021/future-prelude-collision-imported.stderr b/src/test/ui/rust-2021/future-prelude-collision-imported.stderr new file mode 100644 index 0000000000000..3903cbfe82490 --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-imported.stderr @@ -0,0 +1,34 @@ +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision-imported.rs:27:22 + | +LL | let _: u32 = 3u8.try_into().unwrap(); + | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `TryIntoU32::try_into(3u8)` + | +note: the lint level is defined here + --> $DIR/future-prelude-collision-imported.rs:4:9 + | +LL | #![warn(future_prelude_collision)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 + +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision-imported.rs:40:22 + | +LL | let _: u32 = 3u8.try_into().unwrap(); + | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `crate::m::TryIntoU32::try_into(3u8)` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 + +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/future-prelude-collision-imported.rs:53:22 + | +LL | let _: u32 = 3u8.try_into().unwrap(); + | ^^^^^^^^^^^^^^ help: disambiguate the associated function: `super::m::TryIntoU32::try_into(3u8)` + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 + +warning: 3 warnings emitted + diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed b/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed new file mode 100644 index 0000000000000..588ab6255fa5c --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed @@ -0,0 +1,33 @@ +// run-rustfix +// edition:2018 +#![warn(future_prelude_collision)] +#![allow(dead_code)] +#![allow(unused_imports)] + +mod m { + pub trait TryIntoU32 { + fn try_into(self) -> Result; + } + + impl TryIntoU32 for u8 { + fn try_into(self) -> Result { + Ok(self as u32) + } + } + + pub trait AnotherTrick {} +} + +mod d { + use crate::m::AnotherTrick as TryIntoU32; + use crate::m::*; + + fn main() { + // Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods + // to be available. + let _: u32 = 3u8.try_into().unwrap(); + //~^ ERROR no method named `try_into` found for type `u8` in the current scope + } +} + +fn main() {} diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs index ef19cf4d1e6ad..588ab6255fa5c 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs @@ -1,8 +1,8 @@ // run-rustfix // edition:2018 -// check-pass #![warn(future_prelude_collision)] #![allow(dead_code)] +#![allow(unused_imports)] mod m { pub trait TryIntoU32 { @@ -26,7 +26,7 @@ mod d { // Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods // to be available. let _: u32 = 3u8.try_into().unwrap(); - //~^ ERROR no method name `try_into` found + //~^ ERROR no method named `try_into` found for type `u8` in the current scope } } diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr b/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr new file mode 100644 index 0000000000000..3019b2aa5e210 --- /dev/null +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -0,0 +1,40 @@ +error[E0599]: no method named `try_into` found for type `u8` in the current scope + --> $DIR/future-prelude-collision-shadow.rs:28:26 + | +LL | let _: u32 = 3u8.try_into().unwrap(); + | ^^^^^^^^ method not found in `u8` + | + ::: $SRC_DIR/core/src/convert/mod.rs:LL:COL + | +LL | fn try_into(self) -> Result; + | -------- + | | + | the method is available for `Box` here + | the method is available for `Pin` here + | the method is available for `Arc` here + | the method is available for `Rc` here + | + = help: items from traits can only be used if the trait is in scope + = note: the following traits are implemented but not in scope; perhaps add a `use` for one of them: + candidate #1: `use crate::m::TryIntoU32;` + candidate #2: `use std::convert::TryInto;` +help: consider wrapping the receiver expression with the appropriate type + | +LL | let _: u32 = Box::new(3u8).try_into().unwrap(); + | ^^^^^^^^^ ^ +help: consider wrapping the receiver expression with the appropriate type + | +LL | let _: u32 = Pin::new(3u8).try_into().unwrap(); + | ^^^^^^^^^ ^ +help: consider wrapping the receiver expression with the appropriate type + | +LL | let _: u32 = Arc::new(3u8).try_into().unwrap(); + | ^^^^^^^^^ ^ +help: consider wrapping the receiver expression with the appropriate type + | +LL | let _: u32 = Rc::new(3u8).try_into().unwrap(); + | ^^^^^^^^ ^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. From 3dc47e2c098e0b80019e2e1ca622d6af504f6494 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 21 Jun 2021 10:58:10 -0400 Subject: [PATCH 23/25] do not run rustfix for future-prelude-collision-shadow --- .../future-prelude-collision-shadow.fixed | 33 ------------------- .../future-prelude-collision-shadow.rs | 1 - .../future-prelude-collision-shadow.stderr | 2 +- 3 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 src/test/ui/rust-2021/future-prelude-collision-shadow.fixed diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed b/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed deleted file mode 100644 index 588ab6255fa5c..0000000000000 --- a/src/test/ui/rust-2021/future-prelude-collision-shadow.fixed +++ /dev/null @@ -1,33 +0,0 @@ -// run-rustfix -// edition:2018 -#![warn(future_prelude_collision)] -#![allow(dead_code)] -#![allow(unused_imports)] - -mod m { - pub trait TryIntoU32 { - fn try_into(self) -> Result; - } - - impl TryIntoU32 for u8 { - fn try_into(self) -> Result { - Ok(self as u32) - } - } - - pub trait AnotherTrick {} -} - -mod d { - use crate::m::AnotherTrick as TryIntoU32; - use crate::m::*; - - fn main() { - // Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods - // to be available. - let _: u32 = 3u8.try_into().unwrap(); - //~^ ERROR no method named `try_into` found for type `u8` in the current scope - } -} - -fn main() {} diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs index 588ab6255fa5c..c9d2529341f4f 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-shadow.rs +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.rs @@ -1,4 +1,3 @@ -// run-rustfix // edition:2018 #![warn(future_prelude_collision)] #![allow(dead_code)] diff --git a/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr b/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr index 3019b2aa5e210..ad9b8af00e467 100644 --- a/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr +++ b/src/test/ui/rust-2021/future-prelude-collision-shadow.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `try_into` found for type `u8` in the current scope - --> $DIR/future-prelude-collision-shadow.rs:28:26 + --> $DIR/future-prelude-collision-shadow.rs:27:26 | LL | let _: u32 = 3u8.try_into().unwrap(); | ^^^^^^^^ method not found in `u8` From 186c09ae8381e0c8fb43c1d47341649d214362ad Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 21 Jun 2021 11:55:07 -0400 Subject: [PATCH 24/25] add test for `dyn` collisions --- .../ui/rust-2021/inherent-dyn-collision.fixed | 53 +++++++++++++++++++ .../ui/rust-2021/inherent-dyn-collision.rs | 53 +++++++++++++++++++ .../rust-2021/inherent-dyn-collision.stderr | 16 ++++++ 3 files changed, 122 insertions(+) create mode 100644 src/test/ui/rust-2021/inherent-dyn-collision.fixed create mode 100644 src/test/ui/rust-2021/inherent-dyn-collision.rs create mode 100644 src/test/ui/rust-2021/inherent-dyn-collision.stderr diff --git a/src/test/ui/rust-2021/inherent-dyn-collision.fixed b/src/test/ui/rust-2021/inherent-dyn-collision.fixed new file mode 100644 index 0000000000000..cbb6e9659dff3 --- /dev/null +++ b/src/test/ui/rust-2021/inherent-dyn-collision.fixed @@ -0,0 +1,53 @@ +// Test case where the method we want is an inherent method on a +// dyn Trait. In that case, the fix is to insert `*` on the receiver. +// +// check-pass +// run-rustfix +// edition:2018 + +#![warn(future_prelude_collision)] + +trait TryIntoU32 { + fn try_into(&self) -> Result; +} + +impl TryIntoU32 for u8 { + // note: &self + fn try_into(&self) -> Result { + Ok(22) + } +} + +mod inner { + use super::get_dyn_trait; + + // note: this does nothing, but is copying from ffishim's problem of + // having a struct of the same name as the trait in-scope, while *also* + // implementing the trait for that struct but **without** importing the + // trait itself into scope + struct TryIntoU32; + + impl super::TryIntoU32 for TryIntoU32 { + fn try_into(&self) -> Result { + Ok(0) + } + } + + // this is where the gross part happens. since `get_dyn_trait` returns + // a Box, it can still call the method for `dyn Trait` without + // `Trait` being in-scope. it might even be possible to make the trait itself + // entirely unreference-able from the callsite? + pub fn test() -> u32 { + (&*get_dyn_trait()).try_into().unwrap() + //~^ WARNING trait method `try_into` will become ambiguous + //~| WARNING this was previously accepted + } +} + +fn get_dyn_trait() -> Box { + Box::new(3u8) as Box +} + +fn main() { + dbg!(inner::test()); +} diff --git a/src/test/ui/rust-2021/inherent-dyn-collision.rs b/src/test/ui/rust-2021/inherent-dyn-collision.rs new file mode 100644 index 0000000000000..1c9929eff91de --- /dev/null +++ b/src/test/ui/rust-2021/inherent-dyn-collision.rs @@ -0,0 +1,53 @@ +// Test case where the method we want is an inherent method on a +// dyn Trait. In that case, the fix is to insert `*` on the receiver. +// +// check-pass +// run-rustfix +// edition:2018 + +#![warn(future_prelude_collision)] + +trait TryIntoU32 { + fn try_into(&self) -> Result; +} + +impl TryIntoU32 for u8 { + // note: &self + fn try_into(&self) -> Result { + Ok(22) + } +} + +mod inner { + use super::get_dyn_trait; + + // note: this does nothing, but is copying from ffishim's problem of + // having a struct of the same name as the trait in-scope, while *also* + // implementing the trait for that struct but **without** importing the + // trait itself into scope + struct TryIntoU32; + + impl super::TryIntoU32 for TryIntoU32 { + fn try_into(&self) -> Result { + Ok(0) + } + } + + // this is where the gross part happens. since `get_dyn_trait` returns + // a Box, it can still call the method for `dyn Trait` without + // `Trait` being in-scope. it might even be possible to make the trait itself + // entirely unreference-able from the callsite? + pub fn test() -> u32 { + get_dyn_trait().try_into().unwrap() + //~^ WARNING trait method `try_into` will become ambiguous + //~| WARNING this was previously accepted + } +} + +fn get_dyn_trait() -> Box { + Box::new(3u8) as Box +} + +fn main() { + dbg!(inner::test()); +} diff --git a/src/test/ui/rust-2021/inherent-dyn-collision.stderr b/src/test/ui/rust-2021/inherent-dyn-collision.stderr new file mode 100644 index 0000000000000..3d7637100c2c9 --- /dev/null +++ b/src/test/ui/rust-2021/inherent-dyn-collision.stderr @@ -0,0 +1,16 @@ +warning: trait method `try_into` will become ambiguous in Rust 2021 + --> $DIR/inherent-dyn-collision.rs:41:9 + | +LL | get_dyn_trait().try_into().unwrap() + | ^^^^^^^^^^^^^^^ help: disambiguate the method call: `(&*get_dyn_trait())` + | +note: the lint level is defined here + --> $DIR/inherent-dyn-collision.rs:8:9 + | +LL | #![warn(future_prelude_collision)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2021 edition! + = note: for more information, see issue #85684 + +warning: 1 warning emitted + From aa3580baa6f5ef69aba41a63297fb659bcd4b793 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 21 Jun 2021 14:36:25 -0400 Subject: [PATCH 25/25] introduce helper function --- .../src/check/method/prelude2021.rs | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs index 0750567d3dd60..4c925a6f23707 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_typeck/src/check/method/prelude2021.rs @@ -137,28 +137,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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) - }; + let (self_adjusted, precise) = self.adjust_expr(pick, self_expr); + if precise { let args = args .iter() .skip(1) @@ -317,4 +297,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } + + /// Creates a string version of the `expr` that includes explicit adjustments. + /// Returns the string and also a bool indicating whther this is a *precise* + /// suggestion. + fn adjust_expr(&self, pick: &Pick<'tcx>, expr: &hir::Expr<'tcx>) -> (String, bool) { + 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 (expr_text, precise) = + if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { + (expr_text, true) + } else { + (format!("(..)"), false) + }; + + let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{}{} as *const _", derefs, expr_text) + } else { + format!("{}{}{}", autoref, derefs, expr_text) + }; + + (adjusted_text, precise) + } }