Skip to content

Commit dbc9da7

Browse files
committed
WIP: Find the imports that were used to reach a method
And add tests for some corner cases we have to consider.
1 parent 56108f6 commit dbc9da7

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

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

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use hir::def_id::DefId;
2+
use hir::HirId;
13
use rustc_ast::Mutability;
24
use rustc_errors::Applicability;
35
use rustc_hir as hir;
@@ -48,7 +50,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4850
call_expr.span,
4951
|lint| {
5052
let sp = call_expr.span;
51-
let trait_name = self.tcx.def_path_str(pick.item.container.id());
53+
let trait_name =
54+
self.trait_path_or_bare_name(call_expr.hir_id, pick.item.container.id());
5255

5356
let mut lint = lint.build(&format!(
5457
"trait method `{}` will become ambiguous in Rust 2021",
@@ -144,16 +147,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
144147
self.tcx.struct_span_lint_hir(FUTURE_PRELUDE_COLLISION, expr_id, span, |lint| {
145148
// "type" refers to either a type or, more likely, a trait from which
146149
// the associated function or method is from.
147-
let type_name = self.tcx.def_path_str(pick.item.container.id());
148-
let type_generics = self.tcx.generics_of(pick.item.container.id());
150+
let trait_path = self.trait_path_or_bare_name(expr_id, pick.item.container.id());
151+
let trait_generics = self.tcx.generics_of(pick.item.container.id());
149152

150-
let parameter_count = type_generics.count() - (type_generics.has_self as usize);
153+
let parameter_count = trait_generics.count() - (trait_generics.has_self as usize);
151154
let trait_name = if parameter_count == 0 {
152-
type_name
155+
trait_path
153156
} else {
154157
format!(
155158
"{}<{}>",
156-
type_name,
159+
trait_path,
157160
std::iter::repeat("_").take(parameter_count).collect::<Vec<_>>().join(", ")
158161
)
159162
};
@@ -179,4 +182,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
179182
lint.emit();
180183
});
181184
}
185+
186+
fn trait_path_or_bare_name(&self, expr_hir_id: HirId, trait_def_id: DefId) -> String {
187+
self.trait_path(expr_hir_id, trait_def_id).unwrap_or_else(|| {
188+
let key = self.tcx.def_key(trait_def_id);
189+
format!("{}", key.disambiguated_data.data)
190+
})
191+
}
192+
193+
fn trait_path(&self, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> {
194+
let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?;
195+
let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?;
196+
if applicable_trait.import_ids.is_empty() {
197+
// The trait was declared within the module, we only need to use its name.
198+
return None;
199+
}
200+
201+
for &import_id in &applicable_trait.import_ids {
202+
let hir_id = self.tcx.hir().local_def_id_to_hir_id(import_id);
203+
let item = self.tcx.hir().expect_item(hir_id);
204+
debug!(?item, ?import_id, "import_id");
205+
}
206+
207+
return None;
208+
}
182209
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// run-rustfix
2+
// edition:2018
3+
// check-pass
4+
#![warn(future_prelude_collision)]
5+
#![allow(dead_code)]
6+
7+
mod m {
8+
pub trait TryIntoU32 {
9+
fn try_into(self) -> Result<u32, ()>;
10+
}
11+
12+
impl TryIntoU32 for u8 {
13+
fn try_into(self) -> Result<u32, ()> {
14+
Ok(self as u32)
15+
}
16+
}
17+
18+
pub trait AnotherTrick {}
19+
}
20+
21+
mod a {
22+
use crate::m::TryIntoU32;
23+
24+
fn main() {
25+
// In this case, we can just use `TryIntoU32`
26+
let _: u32 = 3u8.try_into().unwrap();
27+
}
28+
}
29+
30+
mod b {
31+
use crate::m::AnotherTrick as TryIntoU32;
32+
use crate::m::TryIntoU32 as _;
33+
34+
fn main() {
35+
// In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use
36+
// the path `crate::m::TryIntoU32` (with which it was imported).
37+
let _: u32 = 3u8.try_into().unwrap();
38+
}
39+
}
40+
41+
mod c {
42+
use super::m::TryIntoU32 as _;
43+
use crate::m::AnotherTrick as TryIntoU32;
44+
45+
fn main() {
46+
// In this case, a `TryIntoU32::try_into` rewrite will not work, and we need to use
47+
// the path `super::m::TryIntoU32` (with which it was imported).
48+
let _: u32 = 3u8.try_into().unwrap();
49+
}
50+
}
51+
52+
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-rustfix
2+
// edition:2018
3+
// check-pass
4+
#![warn(future_prelude_collision)]
5+
#![allow(dead_code)]
6+
7+
mod m {
8+
pub trait TryIntoU32 {
9+
fn try_into(self) -> Result<u32, ()>;
10+
}
11+
12+
impl TryIntoU32 for u8 {
13+
fn try_into(self) -> Result<u32, ()> {
14+
Ok(self as u32)
15+
}
16+
}
17+
18+
pub trait AnotherTrick {}
19+
}
20+
21+
mod d {
22+
use crate::m::AnotherTrick as TryIntoU32;
23+
use crate::m::*;
24+
25+
fn main() {
26+
// Here, `TryIntoU32` is imported but shadowed, but in that case we don't permit its methods
27+
// to be available.
28+
let _: u32 = 3u8.try_into().unwrap();
29+
//~^ ERROR no method name `try_into` found
30+
}
31+
}
32+
33+
fn main() {}

0 commit comments

Comments
 (0)