Skip to content

Commit 9e2d6e1

Browse files
committed
Add crate:: to trait suggestions in Rust 2018.
In the 2018 edition, when suggesting traits to import that implement a given method that is being invoked, suggestions will now include the `crate::` prefix if the suggested trait is local to the current crate.
1 parent de3d640 commit 9e2d6e1

File tree

7 files changed

+192
-17
lines changed

7 files changed

+192
-17
lines changed

src/librustc/ty/item_path.rs

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
1313
use ty::{self, Ty, TyCtxt};
1414
use middle::cstore::{ExternCrate, ExternCrateSource};
1515
use syntax::ast;
16-
use syntax::symbol::Symbol;
17-
use syntax::symbol::LocalInternedString;
16+
use syntax::symbol::{keywords, LocalInternedString, Symbol};
17+
use syntax_pos::edition::Edition;
1818

1919
use std::cell::Cell;
20+
use std::fmt::Debug;
2021

2122
thread_local! {
2223
static FORCE_ABSOLUTE: Cell<bool> = Cell::new(false);
2324
static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false);
25+
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false);
2426
}
2527

2628
/// Enforces that item_path_str always returns an absolute path and
@@ -51,6 +53,17 @@ pub fn with_forced_impl_filename_line<F: FnOnce() -> R, R>(f: F) -> R {
5153
})
5254
}
5355

56+
/// Add the `crate::` prefix to paths where appropriate.
57+
pub fn with_crate_prefix<F: FnOnce() -> R, R>(f: F) -> R {
58+
SHOULD_PREFIX_WITH_CRATE.with(|flag| {
59+
let old = flag.get();
60+
flag.set(true);
61+
let result = f();
62+
flag.set(old);
63+
result
64+
})
65+
}
66+
5467
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
5568
/// Returns a string identifying this def-id. This string is
5669
/// suitable for user output. It is relative to the current crate
@@ -64,6 +77,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
6477
}
6578
});
6679
let mut buffer = LocalPathBuffer::new(mode);
80+
debug!("item_path_str: buffer={:?} def_id={:?}", buffer, def_id);
6781
self.push_item_path(&mut buffer, def_id);
6882
buffer.into_string()
6983
}
@@ -77,6 +91,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
7791
/// suitable for user output. It always begins with a crate identifier.
7892
pub fn absolute_item_path_str(self, def_id: DefId) -> String {
7993
let mut buffer = LocalPathBuffer::new(RootMode::Absolute);
94+
debug!("absolute_item_path_str: buffer={:?} def_id={:?}", buffer, def_id);
8095
self.push_item_path(&mut buffer, def_id);
8196
buffer.into_string()
8297
}
@@ -85,8 +100,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
85100
/// various ways, depending on the `root_mode` of the `buffer`.
86101
/// (See `RootMode` enum for more details.)
87102
pub fn push_krate_path<T>(self, buffer: &mut T, cnum: CrateNum)
88-
where T: ItemPathBuffer
103+
where T: ItemPathBuffer + Debug
89104
{
105+
debug!(
106+
"push_krate_path: buffer={:?} cnum={:?} LOCAL_CRATE={:?}",
107+
buffer, cnum, LOCAL_CRATE
108+
);
90109
match *buffer.root_mode() {
91110
RootMode::Local => {
92111
// In local mode, when we encounter a crate other than
@@ -109,16 +128,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
109128
..
110129
}) = *opt_extern_crate
111130
{
131+
debug!("push_krate_path: def_id={:?}", def_id);
112132
self.push_item_path(buffer, def_id);
113133
} else {
114-
buffer.push(&self.crate_name(cnum).as_str());
134+
let name = self.crate_name(cnum).as_str();
135+
debug!("push_krate_path: name={:?}", name);
136+
buffer.push(&name);
115137
}
138+
} else if self.sess.edition() == Edition::Edition2018 {
139+
SHOULD_PREFIX_WITH_CRATE.with(|flag| {
140+
// We only add the `crate::` keyword where appropriate. This
141+
// is only possible because of the invariant in `push_item_path`
142+
// that this function will not be called after printing the path
143+
// to an item in the standard library. Without this invariant,
144+
// we would print `crate::std::..` here.
145+
if flag.get() {
146+
buffer.push(&keywords::Crate.name().as_str())
147+
}
148+
})
116149
}
117150
}
118151
RootMode::Absolute => {
119152
// In absolute mode, just write the crate name
120153
// unconditionally.
121-
buffer.push(&self.original_crate_name(cnum).as_str());
154+
let name = self.original_crate_name(cnum).as_str();
155+
debug!("push_krate_path: original_name={:?}", name);
156+
buffer.push(&name);
122157
}
123158
}
124159
}
@@ -127,12 +162,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
127162
/// from at least one local module and returns true. If the crate defining `external_def_id` is
128163
/// declared with an `extern crate`, the path is guaranteed to use the `extern crate`.
129164
pub fn try_push_visible_item_path<T>(self, buffer: &mut T, external_def_id: DefId) -> bool
130-
where T: ItemPathBuffer
165+
where T: ItemPathBuffer + Debug
131166
{
167+
debug!(
168+
"try_push_visible_item_path: buffer={:?} external_def_id={:?}",
169+
buffer, external_def_id
170+
);
132171
let visible_parent_map = self.visible_parent_map(LOCAL_CRATE);
133172

134173
let (mut cur_def, mut cur_path) = (external_def_id, Vec::<LocalInternedString>::new());
135174
loop {
175+
debug!(
176+
"try_push_visible_item_path: cur_def={:?} cur_path={:?} CRATE_DEF_INDEX={:?}",
177+
cur_def, cur_path, CRATE_DEF_INDEX,
178+
);
136179
// If `cur_def` is a direct or injected extern crate, push the path to the crate
137180
// followed by the path to the item within the crate and return.
138181
if cur_def.index == CRATE_DEF_INDEX {
@@ -142,6 +185,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
142185
direct: true,
143186
..
144187
}) => {
188+
debug!("try_push_visible_item_path: def_id={:?}", def_id);
145189
self.push_item_path(buffer, def_id);
146190
cur_path.iter().rev().for_each(|segment| buffer.push(&segment));
147191
return true;
@@ -156,6 +200,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
156200
}
157201

158202
let mut cur_def_key = self.def_key(cur_def);
203+
debug!("try_push_visible_item_path: cur_def_key={:?}", cur_def_key);
159204

160205
// For a UnitStruct or TupleStruct we want the name of its parent rather than <unnamed>.
161206
if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data {
@@ -175,6 +220,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
175220
Symbol::intern("<unnamed>").as_str()
176221
}
177222
});
223+
debug!("try_push_visible_item_path: symbol={:?}", symbol);
178224
cur_path.push(symbol);
179225

180226
match visible_parent_map.get(&cur_def) {
@@ -185,15 +231,17 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
185231
}
186232

187233
pub fn push_item_path<T>(self, buffer: &mut T, def_id: DefId)
188-
where T: ItemPathBuffer
234+
where T: ItemPathBuffer + Debug
189235
{
236+
debug!("push_item_path: buffer={:?} def_id={:?}", buffer, def_id);
190237
match *buffer.root_mode() {
191238
RootMode::Local if !def_id.is_local() =>
192239
if self.try_push_visible_item_path(buffer, def_id) { return },
193240
_ => {}
194241
}
195242

196243
let key = self.def_key(def_id);
244+
debug!("push_item_path: key={:?}", key);
197245
match key.disambiguated_data.data {
198246
DefPathData::CrateRoot => {
199247
assert!(key.parent.is_none());
@@ -225,9 +273,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
225273
data @ DefPathData::ImplTrait |
226274
data @ DefPathData::GlobalMetaData(..) => {
227275
let parent_def_id = self.parent_def_id(def_id).unwrap();
228-
self.push_item_path(buffer, parent_def_id);
276+
277+
match self.def_key(parent_def_id).disambiguated_data.data {
278+
// Skip recursing to print the crate root depending on the
279+
// current name.
280+
//
281+
// In particular, don't recurse to print the crate root if we
282+
// just printed `std`. In doing this, we are able to add
283+
// `crate::` to trait import suggestions.
284+
DefPathData::CrateRoot if data.as_interned_str() == "std" => {},
285+
_ => self.push_item_path(buffer, parent_def_id),
286+
}
287+
229288
buffer.push(&data.as_interned_str().as_symbol().as_str());
230-
}
289+
},
290+
231291
DefPathData::StructCtor => { // present `X` instead of `X::{{constructor}}`
232292
let parent_def_id = self.parent_def_id(def_id).unwrap();
233293
self.push_item_path(buffer, parent_def_id);
@@ -238,8 +298,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
238298
fn push_impl_path<T>(self,
239299
buffer: &mut T,
240300
impl_def_id: DefId)
241-
where T: ItemPathBuffer
301+
where T: ItemPathBuffer + Debug
242302
{
303+
debug!("push_impl_path: buffer={:?} impl_def_id={:?}", buffer, impl_def_id);
243304
let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
244305

245306
// Always use types for non-local impls, where types are always
@@ -327,7 +388,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
327388
fn push_impl_path_fallback<T>(self,
328389
buffer: &mut T,
329390
impl_def_id: DefId)
330-
where T: ItemPathBuffer
391+
where T: ItemPathBuffer + Debug
331392
{
332393
// If no type info is available, fall back to
333394
// pretty printing some span information. This should

src/librustc_codegen_utils/symbol_names.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
338338
//
339339
// To be able to work on all platforms and get *some* reasonable output, we
340340
// use C++ name-mangling.
341+
#[derive(Debug)]
341342
struct SymbolPathBuffer {
342343
result: String,
343344
temp_buf: String,

src/librustc_typeck/check/method/suggest.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rustc::hir::map as hir_map;
1616
use hir::Node;
1717
use rustc_data_structures::sync::Lrc;
1818
use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable};
19+
use rustc::ty::item_path::with_crate_prefix;
1920
use hir::def::Def;
2021
use hir::def_id::{CRATE_DEF_INDEX, DefId};
2122
use middle::lang_items::FnOnceTraitLangItem;
@@ -515,7 +516,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
515516
} else {
516517
"\n"
517518
};
518-
format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline)
519+
format!(
520+
"use {};\n{}",
521+
with_crate_prefix(|| self.tcx.item_path_str(*did)),
522+
additional_newline
523+
)
519524
}).collect();
520525

521526
err.span_suggestions_with_applicability(
@@ -528,12 +533,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
528533
let limit = if candidates.len() == 5 { 5 } else { 4 };
529534
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
530535
if candidates.len() > 1 {
531-
msg.push_str(&format!("\ncandidate #{}: `use {};`",
532-
i + 1,
533-
self.tcx.item_path_str(*trait_did)));
536+
msg.push_str(
537+
&format!(
538+
"\ncandidate #{}: `use {};`",
539+
i + 1,
540+
with_crate_prefix(|| self.tcx.item_path_str(*trait_did))
541+
)
542+
);
534543
} else {
535-
msg.push_str(&format!("\n`use {};`",
536-
self.tcx.item_path_str(*trait_did)));
544+
msg.push_str(
545+
&format!(
546+
"\n`use {};`",
547+
with_crate_prefix(|| self.tcx.item_path_str(*trait_did))
548+
)
549+
);
537550
}
538551
}
539552
if candidates.len() > limit {

src/librustdoc/clean/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3990,6 +3990,7 @@ pub fn path_to_def(tcx: &TyCtxt, path: &[&str]) -> Option<DefId> {
39903990

39913991
pub fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path
39923992
where F: Fn(DefId) -> Def {
3993+
#[derive(Debug)]
39933994
struct AbsolutePathBuffer {
39943995
names: Vec<String>,
39953996
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub trait Baz {
12+
fn baz(&self) { }
13+
}
14+
15+
impl Baz for u32 { }
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// edition:2018
12+
// aux-build:trait-import-suggestions.rs
13+
// compile-flags:--extern trait-import-suggestions
14+
15+
mod foo {
16+
mod foobar {
17+
pub(crate) trait Foobar {
18+
fn foobar(&self) { }
19+
}
20+
21+
impl Foobar for u32 { }
22+
}
23+
24+
pub(crate) trait Bar {
25+
fn bar(&self) { }
26+
}
27+
28+
impl Bar for u32 { }
29+
30+
fn in_foo() {
31+
let x: u32 = 22;
32+
x.foobar();
33+
}
34+
}
35+
36+
fn main() {
37+
let x: u32 = 22;
38+
x.bar();
39+
x.baz();
40+
let y = u32::from_str("33");
41+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0599]: no method named `foobar` found for type `u32` in the current scope
2+
--> $DIR/trait-import-suggestions.rs:32:11
3+
|
4+
LL | x.foobar();
5+
| ^^^^^^
6+
|
7+
= help: items from traits can only be used if the trait is in scope
8+
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
9+
`use crate::foo::foobar::Foobar;`
10+
11+
error[E0599]: no method named `bar` found for type `u32` in the current scope
12+
--> $DIR/trait-import-suggestions.rs:38:7
13+
|
14+
LL | x.bar();
15+
| ^^^
16+
|
17+
= help: items from traits can only be used if the trait is in scope
18+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
19+
|
20+
LL | use crate::foo::Bar;
21+
|
22+
23+
error[E0599]: no method named `baz` found for type `u32` in the current scope
24+
--> $DIR/trait-import-suggestions.rs:39:7
25+
|
26+
LL | x.baz();
27+
| ^^^
28+
29+
error[E0599]: no function or associated item named `from_str` found for type `u32` in the current scope
30+
--> $DIR/trait-import-suggestions.rs:40:13
31+
|
32+
LL | let y = u32::from_str("33");
33+
| ^^^^^^^^^^^^^ function or associated item not found in `u32`
34+
|
35+
= help: items from traits can only be used if the trait is in scope
36+
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
37+
|
38+
LL | use std::str::FromStr;
39+
|
40+
41+
error: aborting due to 4 previous errors
42+
43+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)