diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 79d06cd7f1254..3c209a4324675 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -28,17 +28,16 @@ use util::common::ProfileQueriesMsg; use rustc_data_structures::base_n; use rustc_data_structures::sync::{self, Lrc, Lock, LockCell, OneThread, Once, RwLock}; -use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder, DiagnosticId, Applicability}; use errors::emitter::{Emitter, EmitterWriter}; +use syntax::ast::{self, NodeId}; use syntax::edition::Edition; +use syntax::feature_gate::{self, AttributeType}; use syntax::json::JsonEmitter; -use syntax::feature_gate; -use syntax::parse; -use syntax::parse::ParseSess; -use syntax::{ast, source_map}; -use syntax::feature_gate::AttributeType; -use syntax_pos::{MultiSpan, Span, symbol::Symbol}; +use syntax::source_map; +use syntax::symbol::Symbol; +use syntax::parse::{self, ParseSess}; +use syntax_pos::{MultiSpan, Span}; use util::profiling::SelfProfiler; use rustc_target::spec::PanicStrategy; diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 1b4ff18f6b8d8..c8d104e6c321f 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -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 = Cell::new(false); static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); + static SHOULD_PREFIX_WITH_CRATE: Cell = 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 R, R>(f: F) -> R { }) } +/// Add the `crate::` prefix to paths where appropriate. +pub fn with_crate_prefix 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,7 +77,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } }); let mut buffer = LocalPathBuffer::new(mode); - self.push_item_path(&mut buffer, def_id); + debug!("item_path_str: buffer={:?} def_id={:?}", buffer, def_id); + self.push_item_path(&mut buffer, def_id, false); buffer.into_string() } @@ -77,16 +91,26 @@ 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); - self.push_item_path(&mut buffer, def_id); + debug!("absolute_item_path_str: buffer={:?} def_id={:?}", buffer, def_id); + self.push_item_path(&mut buffer, def_id, false); buffer.into_string() } /// Returns the "path" to a particular crate. This can proceed in /// various ways, depending on the `root_mode` of the `buffer`. /// (See `RootMode` enum for more details.) - pub fn push_krate_path(self, buffer: &mut T, cnum: CrateNum) - where T: ItemPathBuffer + /// + /// `pushed_prelude_crate` argument should be `true` when the buffer + /// has had a prelude crate pushed to it. If this is the case, then + /// we do not want to prepend `crate::` (as that would not be a valid + /// path). + pub fn push_krate_path(self, buffer: &mut T, cnum: CrateNum, pushed_prelude_crate: bool) + 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 +133,29 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { .. }) = *opt_extern_crate { - self.push_item_path(buffer, def_id); + debug!("push_krate_path: def_id={:?}", def_id); + self.push_item_path(buffer, def_id, pushed_prelude_crate); } 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 && !pushed_prelude_crate { + SHOULD_PREFIX_WITH_CRATE.with(|flag| { + // We only add the `crate::` keyword where appropriate. In particular, + // when we've not previously pushed a prelude crate to this path. + 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); } } } @@ -126,13 +163,26 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// If possible, this pushes a global path resolving to `external_def_id` that is visible /// 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(self, buffer: &mut T, external_def_id: DefId) -> bool - where T: ItemPathBuffer + pub fn try_push_visible_item_path( + self, + buffer: &mut T, + external_def_id: DefId, + pushed_prelude_crate: bool, + ) -> bool + 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::::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,7 +192,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { direct: true, .. }) => { - self.push_item_path(buffer, def_id); + debug!("try_push_visible_item_path: def_id={:?}", def_id); + self.push_item_path(buffer, def_id, pushed_prelude_crate); cur_path.iter().rev().for_each(|segment| buffer.push(&segment)); return true; } @@ -156,6 +207,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 . if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data { @@ -175,6 +227,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Symbol::intern("").as_str() } }); + debug!("try_push_visible_item_path: symbol={:?}", symbol); cur_path.push(symbol); match visible_parent_map.get(&cur_def) { @@ -184,24 +237,29 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - pub fn push_item_path(self, buffer: &mut T, def_id: DefId) - where T: ItemPathBuffer + pub fn push_item_path(self, buffer: &mut T, def_id: DefId, pushed_prelude_crate: bool) + where T: ItemPathBuffer + Debug { + debug!( + "push_item_path: buffer={:?} def_id={:?} pushed_prelude_crate={:?}", + buffer, def_id, pushed_prelude_crate + ); match *buffer.root_mode() { RootMode::Local if !def_id.is_local() => - if self.try_push_visible_item_path(buffer, def_id) { return }, + if self.try_push_visible_item_path(buffer, def_id, pushed_prelude_crate) { return }, _ => {} } 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()); - self.push_krate_path(buffer, def_id.krate); + self.push_krate_path(buffer, def_id.krate, pushed_prelude_crate); } DefPathData::Impl => { - self.push_impl_path(buffer, def_id); + self.push_impl_path(buffer, def_id, pushed_prelude_crate); } // Unclear if there is any value in distinguishing these. @@ -224,22 +282,40 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { data @ DefPathData::ClosureExpr | 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); + let parent_did = self.parent_def_id(def_id).unwrap(); + + // Keep track of whether we are one recursion away from the `CrateRoot` and + // pushing the name of a prelude crate. If we are, we'll want to know this when + // printing the `CrateRoot` so we don't prepend a `crate::` to paths. + let mut is_prelude_crate = false; + if let DefPathData::CrateRoot = self.def_key(parent_did).disambiguated_data.data { + if self.sess.extern_prelude.contains(&data.as_interned_str().as_symbol()) { + is_prelude_crate = true; + } + } + + self.push_item_path( + buffer, parent_did, pushed_prelude_crate || is_prelude_crate + ); 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); + self.push_item_path(buffer, parent_def_id, pushed_prelude_crate); } } } - fn push_impl_path(self, - buffer: &mut T, - impl_def_id: DefId) - where T: ItemPathBuffer + fn push_impl_path( + self, + buffer: &mut T, + impl_def_id: DefId, + pushed_prelude_crate: bool, + ) + 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 @@ -251,7 +327,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { }; if !use_types { - return self.push_impl_path_fallback(buffer, impl_def_id); + return self.push_impl_path_fallback(buffer, impl_def_id, pushed_prelude_crate); } // Decide whether to print the parent path for the impl. @@ -275,7 +351,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // If the impl is not co-located with either self-type or // trait-type, then fallback to a format that identifies // the module more clearly. - self.push_item_path(buffer, parent_def_id); + self.push_item_path(buffer, parent_def_id, pushed_prelude_crate); if let Some(trait_ref) = impl_trait_ref { buffer.push(&format!("", trait_ref, self_ty)); } else { @@ -301,13 +377,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { match self_ty.sty { ty::Adt(adt_def, substs) => { if substs.types().next().is_none() { // ignore regions - self.push_item_path(buffer, adt_def.did); + self.push_item_path(buffer, adt_def.did, pushed_prelude_crate); } else { buffer.push(&format!("<{}>", self_ty)); } } - ty::Foreign(did) => self.push_item_path(buffer, did), + ty::Foreign(did) => self.push_item_path(buffer, did, pushed_prelude_crate), ty::Bool | ty::Char | @@ -324,16 +400,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - fn push_impl_path_fallback(self, - buffer: &mut T, - impl_def_id: DefId) - where T: ItemPathBuffer + fn push_impl_path_fallback( + self, + buffer: &mut T, + impl_def_id: DefId, + pushed_prelude_crate: bool, + ) + where T: ItemPathBuffer + Debug { // If no type info is available, fall back to // pretty printing some span information. This should // only occur very early in the compiler pipeline. let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); - self.push_item_path(buffer, parent_def_id); + self.push_item_path(buffer, parent_def_id, pushed_prelude_crate); let node_id = self.hir.as_local_node_id(impl_def_id).unwrap(); let item = self.hir.expect_item(node_id); let span_str = self.sess.source_map().span_to_string(item.span); diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 39b88b225edc7..c1e80234a7750 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -228,7 +228,7 @@ fn get_symbol_hash<'a, 'tcx>( fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> ty::SymbolName { let mut buffer = SymbolPathBuffer::new(); item_path::with_forced_absolute_paths(|| { - tcx.push_item_path(&mut buffer, def_id); + tcx.push_item_path(&mut buffer, def_id, false); }); buffer.into_interned() } @@ -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, diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index f5d332521ff0b..28b9dcb9bfdd1 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -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 { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a91f2fd7474f5..371b631723a39 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3990,6 +3990,7 @@ pub fn path_to_def(tcx: &TyCtxt, path: &[&str]) -> Option { pub fn get_path_for_type(tcx: TyCtxt, def_id: DefId, def_ctor: F) -> hir::Path where F: Fn(DefId) -> Def { + #[derive(Debug)] struct AbsolutePathBuffer { names: Vec, } @@ -4007,7 +4008,7 @@ where F: Fn(DefId) -> Def { let mut apb = AbsolutePathBuffer { names: vec![] }; - tcx.push_item_path(&mut apb, def_id); + tcx.push_item_path(&mut apb, def_id, false); hir::Path { span: DUMMY_SP, diff --git a/src/test/ui/rust-2018/auxiliary/trait-import-suggestions.rs b/src/test/ui/rust-2018/auxiliary/trait-import-suggestions.rs new file mode 100644 index 0000000000000..611fa83854b9b --- /dev/null +++ b/src/test/ui/rust-2018/auxiliary/trait-import-suggestions.rs @@ -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 or the MIT license +// , 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 { } diff --git a/src/test/ui/rust-2018/trait-import-suggestions.rs b/src/test/ui/rust-2018/trait-import-suggestions.rs new file mode 100644 index 0000000000000..d603d8212ed37 --- /dev/null +++ b/src/test/ui/rust-2018/trait-import-suggestions.rs @@ -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 or the MIT license +// , 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"); +} diff --git a/src/test/ui/rust-2018/trait-import-suggestions.stderr b/src/test/ui/rust-2018/trait-import-suggestions.stderr new file mode 100644 index 0000000000000..59fe7b958e345 --- /dev/null +++ b/src/test/ui/rust-2018/trait-import-suggestions.stderr @@ -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`.