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.
This commit is contained in:
David Wood 2018-09-27 01:17:54 +02:00
parent de3d640f59
commit 9e2d6e1e62
No known key found for this signature in database
GPG Key ID: 01760B4F9F53F154
7 changed files with 192 additions and 17 deletions
src
librustc/ty
librustc_codegen_utils
librustc_typeck/check/method
librustdoc/clean
test/ui/rust-2018

@ -13,14 +13,16 @@ use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use ty::{self, Ty, TyCtxt};
use middle::cstore::{ExternCrate, ExternCrateSource};
use syntax::ast;
use syntax::symbol::Symbol;
use syntax::symbol::LocalInternedString;
use syntax::symbol::{keywords, LocalInternedString, Symbol};
use syntax_pos::edition::Edition;
use std::cell::Cell;
use std::fmt::Debug;
thread_local! {
static FORCE_ABSOLUTE: Cell<bool> = Cell::new(false);
static FORCE_IMPL_FILENAME_LINE: Cell<bool> = Cell::new(false);
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = Cell::new(false);
}
/// 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 {
})
}
/// Add the `crate::` prefix to paths where appropriate.
pub fn with_crate_prefix<F: FnOnce() -> R, R>(f: F) -> R {
SHOULD_PREFIX_WITH_CRATE.with(|flag| {
let old = flag.get();
flag.set(true);
let result = f();
flag.set(old);
result
})
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Returns a string identifying this def-id. This string is
/// suitable for user output. It is relative to the current crate
@ -64,6 +77,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
});
let mut buffer = LocalPathBuffer::new(mode);
debug!("item_path_str: buffer={:?} def_id={:?}", buffer, def_id);
self.push_item_path(&mut buffer, def_id);
buffer.into_string()
}
@ -77,6 +91,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// suitable for user output. It always begins with a crate identifier.
pub fn absolute_item_path_str(self, def_id: DefId) -> String {
let mut buffer = LocalPathBuffer::new(RootMode::Absolute);
debug!("absolute_item_path_str: buffer={:?} def_id={:?}", buffer, def_id);
self.push_item_path(&mut buffer, def_id);
buffer.into_string()
}
@ -85,8 +100,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// various ways, depending on the `root_mode` of the `buffer`.
/// (See `RootMode` enum for more details.)
pub fn push_krate_path<T>(self, buffer: &mut T, cnum: CrateNum)
where T: ItemPathBuffer
where T: ItemPathBuffer + Debug
{
debug!(
"push_krate_path: buffer={:?} cnum={:?} LOCAL_CRATE={:?}",
buffer, cnum, LOCAL_CRATE
);
match *buffer.root_mode() {
RootMode::Local => {
// In local mode, when we encounter a crate other than
@ -109,16 +128,32 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
..
}) = *opt_extern_crate
{
debug!("push_krate_path: def_id={:?}", def_id);
self.push_item_path(buffer, def_id);
} else {
buffer.push(&self.crate_name(cnum).as_str());
let name = self.crate_name(cnum).as_str();
debug!("push_krate_path: name={:?}", name);
buffer.push(&name);
}
} else if self.sess.edition() == Edition::Edition2018 {
SHOULD_PREFIX_WITH_CRATE.with(|flag| {
// We only add the `crate::` keyword where appropriate. This
// is only possible because of the invariant in `push_item_path`
// that this function will not be called after printing the path
// to an item in the standard library. Without this invariant,
// we would print `crate::std::..` here.
if flag.get() {
buffer.push(&keywords::Crate.name().as_str())
}
})
}
}
RootMode::Absolute => {
// In absolute mode, just write the crate name
// unconditionally.
buffer.push(&self.original_crate_name(cnum).as_str());
let name = self.original_crate_name(cnum).as_str();
debug!("push_krate_path: original_name={:?}", name);
buffer.push(&name);
}
}
}
@ -127,12 +162,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// from at least one local module and returns true. If the crate defining `external_def_id` is
/// declared with an `extern crate`, the path is guaranteed to use the `extern crate`.
pub fn try_push_visible_item_path<T>(self, buffer: &mut T, external_def_id: DefId) -> bool
where T: ItemPathBuffer
where T: ItemPathBuffer + Debug
{
debug!(
"try_push_visible_item_path: buffer={:?} external_def_id={:?}",
buffer, external_def_id
);
let visible_parent_map = self.visible_parent_map(LOCAL_CRATE);
let (mut cur_def, mut cur_path) = (external_def_id, Vec::<LocalInternedString>::new());
loop {
debug!(
"try_push_visible_item_path: cur_def={:?} cur_path={:?} CRATE_DEF_INDEX={:?}",
cur_def, cur_path, CRATE_DEF_INDEX,
);
// If `cur_def` is a direct or injected extern crate, push the path to the crate
// followed by the path to the item within the crate and return.
if cur_def.index == CRATE_DEF_INDEX {
@ -142,6 +185,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
direct: true,
..
}) => {
debug!("try_push_visible_item_path: def_id={:?}", def_id);
self.push_item_path(buffer, def_id);
cur_path.iter().rev().for_each(|segment| buffer.push(&segment));
return true;
@ -156,6 +200,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
let mut cur_def_key = self.def_key(cur_def);
debug!("try_push_visible_item_path: cur_def_key={:?}", cur_def_key);
// For a UnitStruct or TupleStruct we want the name of its parent rather than <unnamed>.
if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data {
@ -175,6 +220,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Symbol::intern("<unnamed>").as_str()
}
});
debug!("try_push_visible_item_path: symbol={:?}", symbol);
cur_path.push(symbol);
match visible_parent_map.get(&cur_def) {
@ -185,8 +231,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
pub fn push_item_path<T>(self, buffer: &mut T, def_id: DefId)
where T: ItemPathBuffer
where T: ItemPathBuffer + Debug
{
debug!("push_item_path: buffer={:?} def_id={:?}", buffer, def_id);
match *buffer.root_mode() {
RootMode::Local if !def_id.is_local() =>
if self.try_push_visible_item_path(buffer, def_id) { return },
@ -194,6 +241,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
}
let key = self.def_key(def_id);
debug!("push_item_path: key={:?}", key);
match key.disambiguated_data.data {
DefPathData::CrateRoot => {
assert!(key.parent.is_none());
@ -225,9 +273,21 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
data @ DefPathData::ImplTrait |
data @ DefPathData::GlobalMetaData(..) => {
let parent_def_id = self.parent_def_id(def_id).unwrap();
self.push_item_path(buffer, parent_def_id);
match self.def_key(parent_def_id).disambiguated_data.data {
// Skip recursing to print the crate root depending on the
// current name.
//
// In particular, don't recurse to print the crate root if we
// just printed `std`. In doing this, we are able to add
// `crate::` to trait import suggestions.
DefPathData::CrateRoot if data.as_interned_str() == "std" => {},
_ => self.push_item_path(buffer, parent_def_id),
}
buffer.push(&data.as_interned_str().as_symbol().as_str());
}
},
DefPathData::StructCtor => { // present `X` instead of `X::{{constructor}}`
let parent_def_id = self.parent_def_id(def_id).unwrap();
self.push_item_path(buffer, parent_def_id);
@ -238,8 +298,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn push_impl_path<T>(self,
buffer: &mut T,
impl_def_id: DefId)
where T: ItemPathBuffer
where T: ItemPathBuffer + Debug
{
debug!("push_impl_path: buffer={:?} impl_def_id={:?}", buffer, impl_def_id);
let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
// Always use types for non-local impls, where types are always
@ -327,7 +388,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
fn push_impl_path_fallback<T>(self,
buffer: &mut T,
impl_def_id: DefId)
where T: ItemPathBuffer
where T: ItemPathBuffer + Debug
{
// If no type info is available, fall back to
// pretty printing some span information. This should

@ -338,6 +338,7 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance
//
// To be able to work on all platforms and get *some* reasonable output, we
// use C++ name-mangling.
#[derive(Debug)]
struct SymbolPathBuffer {
result: String,
temp_buf: String,

@ -16,6 +16,7 @@ use rustc::hir::map as hir_map;
use hir::Node;
use rustc_data_structures::sync::Lrc;
use rustc::ty::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable};
use rustc::ty::item_path::with_crate_prefix;
use hir::def::Def;
use hir::def_id::{CRATE_DEF_INDEX, DefId};
use middle::lang_items::FnOnceTraitLangItem;
@ -515,7 +516,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} else {
"\n"
};
format!("use {};\n{}", self.tcx.item_path_str(*did), additional_newline)
format!(
"use {};\n{}",
with_crate_prefix(|| self.tcx.item_path_str(*did)),
additional_newline
)
}).collect();
err.span_suggestions_with_applicability(
@ -528,12 +533,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let limit = if candidates.len() == 5 { 5 } else { 4 };
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
if candidates.len() > 1 {
msg.push_str(&format!("\ncandidate #{}: `use {};`",
i + 1,
self.tcx.item_path_str(*trait_did)));
msg.push_str(
&format!(
"\ncandidate #{}: `use {};`",
i + 1,
with_crate_prefix(|| self.tcx.item_path_str(*trait_did))
)
);
} else {
msg.push_str(&format!("\n`use {};`",
self.tcx.item_path_str(*trait_did)));
msg.push_str(
&format!(
"\n`use {};`",
with_crate_prefix(|| self.tcx.item_path_str(*trait_did))
)
);
}
}
if candidates.len() > limit {

@ -3990,6 +3990,7 @@ pub fn path_to_def(tcx: &TyCtxt, path: &[&str]) -> Option<DefId> {
pub fn get_path_for_type<F>(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path
where F: Fn(DefId) -> Def {
#[derive(Debug)]
struct AbsolutePathBuffer {
names: Vec<String>,
}

@ -0,0 +1,15 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub trait Baz {
fn baz(&self) { }
}
impl Baz for u32 { }

@ -0,0 +1,41 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// edition:2018
// aux-build:trait-import-suggestions.rs
// compile-flags:--extern trait-import-suggestions
mod foo {
mod foobar {
pub(crate) trait Foobar {
fn foobar(&self) { }
}
impl Foobar for u32 { }
}
pub(crate) trait Bar {
fn bar(&self) { }
}
impl Bar for u32 { }
fn in_foo() {
let x: u32 = 22;
x.foobar();
}
}
fn main() {
let x: u32 = 22;
x.bar();
x.baz();
let y = u32::from_str("33");
}

@ -0,0 +1,43 @@
error[E0599]: no method named `foobar` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:32:11
|
LL | x.foobar();
| ^^^^^^
|
= help: items from traits can only be used if the trait is in scope
= note: the following trait is implemented but not in scope, perhaps add a `use` for it:
`use crate::foo::foobar::Foobar;`
error[E0599]: no method named `bar` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:38:7
|
LL | x.bar();
| ^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
LL | use crate::foo::Bar;
|
error[E0599]: no method named `baz` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:39:7
|
LL | x.baz();
| ^^^
error[E0599]: no function or associated item named `from_str` found for type `u32` in the current scope
--> $DIR/trait-import-suggestions.rs:40:13
|
LL | let y = u32::from_str("33");
| ^^^^^^^^^^^^^ function or associated item not found in `u32`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
LL | use std::str::FromStr;
|
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0599`.