1
Fork 0

WIP: Find the imports that were used to reach a method

And add tests for some corner cases we have to consider.
This commit is contained in:
Niko Matsakis 2021-06-17 06:10:38 -04:00
parent 56108f67b1
commit dbc9da7962
3 changed files with 118 additions and 6 deletions

View file

@ -1,3 +1,5 @@
use hir::def_id::DefId;
use hir::HirId;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -48,7 +50,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr.span, call_expr.span,
|lint| { |lint| {
let sp = call_expr.span; 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!( let mut lint = lint.build(&format!(
"trait method `{}` will become ambiguous in Rust 2021", "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| { 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 // "type" refers to either a type or, more likely, a trait from which
// the associated function or method is from. // the associated function or method is from.
let type_name = self.tcx.def_path_str(pick.item.container.id()); let trait_path = self.trait_path_or_bare_name(expr_id, pick.item.container.id());
let type_generics = self.tcx.generics_of(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 { let trait_name = if parameter_count == 0 {
type_name trait_path
} else { } else {
format!( format!(
"{}<{}>", "{}<{}>",
type_name, trait_path,
std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ") std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
) )
}; };
@ -179,4 +182,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lint.emit(); 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<String> {
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;
}
} }

View file

@ -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<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 = 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() {}

View file

@ -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<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 name `try_into` found
}
}
fn main() {}