Fix future_prelude_collision for object calls and use as _

This commit is contained in:
jam1garner 2021-06-19 18:42:24 -04:00
parent 9bee7f0d0e
commit b18704dd58
7 changed files with 311 additions and 57 deletions

View File

@ -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());
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 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 {
@ -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| {
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(),
);
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::<String>();
lint.span_suggestion(
sp,
"disambiguate the associated function",
format!(
", {}",
self.sess().source_map().span_to_snippet(arg.span).unwrap()
)
})
.collect::<String>();
"{}::{}({}{})",
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.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();
},
);
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::<Vec<_>>()
.join("::"),
),
_ => {
span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind);
}

View File

@ -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<u32, ()>;
}
impl TryIntoU32 for u8 {
fn try_into(self) -> Result<u32, ()> {
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() {}

View File

@ -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!
}
}

View File

@ -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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/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 <https://github.com/rust-lang/rust/issues/85684>
warning: 3 warnings emitted

View File

@ -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<u32, ()>;
}
impl TryIntoU32 for u8 {
fn try_into(self) -> Result<u32, ()> {
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() {}

View File

@ -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
}
}

View File

@ -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<T, Self::Error>;
| --------
| |
| the method is available for `Box<u8>` here
| the method is available for `Pin<u8>` here
| the method is available for `Arc<u8>` here
| the method is available for `Rc<u8>` 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`.