From 9e2d6e1e62802f7a42c900438b97fd3c5316dcb3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 27 Sep 2018 01:17:54 +0200 Subject: [PATCH 1/3] 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. --- src/librustc/ty/item_path.rs | 83 ++++++++++++++++--- src/librustc_codegen_utils/symbol_names.rs | 1 + src/librustc_typeck/check/method/suggest.rs | 25 ++++-- src/librustdoc/clean/mod.rs | 1 + .../auxiliary/trait-import-suggestions.rs | 15 ++++ .../ui/rust-2018/trait-import-suggestions.rs | 41 +++++++++ .../rust-2018/trait-import-suggestions.stderr | 43 ++++++++++ 7 files changed, 192 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/rust-2018/auxiliary/trait-import-suggestions.rs create mode 100644 src/test/ui/rust-2018/trait-import-suggestions.rs create mode 100644 src/test/ui/rust-2018/trait-import-suggestions.stderr diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 1b4ff18f6b8d8..41f908b4be6b5 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,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(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(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::::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 . if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data { @@ -175,6 +220,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) { @@ -185,8 +231,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn push_item_path(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(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(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 diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs index 39b88b225edc7..7651dfe4f34e9 100644 --- a/src/librustc_codegen_utils/symbol_names.rs +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -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..c0a8009e53688 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, } 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`. From 4cbd397ce861ae0ee823ab56d1f797dee67e0b64 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 27 Sep 2018 21:38:11 +0200 Subject: [PATCH 2/3] Move prelude crate names into `Session`. Avoid hardcoding and special-casing the `std` crate name in the item path logic by moving the prelude crate name logic into the `Session` type so it can be reused in the item path logic and resolve module. --- src/librustc/session/mod.rs | 13 ++++++------- src/librustc/ty/item_path.rs | 4 +++- 2 files changed, 9 insertions(+), 8 deletions(-) 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 41f908b4be6b5..4e7783a00cb86 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -281,7 +281,9 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // 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" => {}, + DefPathData::CrateRoot if self.sess.extern_prelude.contains( + &data.as_interned_str().as_symbol() + ) => {}, _ => self.push_item_path(buffer, parent_def_id), } From 02357e459be7fb2014f57f1a86b38897c2c0816b Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 28 Sep 2018 00:56:41 +0200 Subject: [PATCH 3/3] Attempt to resolve linking issues. This commit takes a different approach to add the `crate::` prefix to item paths than previous commits. Previously, recursion was stopped after a prelude crate name was pushed to the path. It is theorized that this was the cause of the linking issues since the same path logic is used for symbol names and that not recursing meant that details were being missed that affect symbol names. As of this commit, instead of ceasing recursion, a flag is passed through to any subsequent recursive calls so that the same effect can be achieved by checking that flag. --- src/librustc/ty/item_path.rs | 100 ++++++++++++--------- src/librustc_codegen_utils/symbol_names.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- 3 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 4e7783a00cb86..c8d104e6c321f 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -78,7 +78,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); + self.push_item_path(&mut buffer, def_id, false); buffer.into_string() } @@ -92,14 +92,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { 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); + 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) + /// + /// `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!( @@ -129,19 +134,16 @@ 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); + self.push_item_path(buffer, def_id, pushed_prelude_crate); } else { let name = self.crate_name(cnum).as_str(); debug!("push_krate_path: name={:?}", name); buffer.push(&name); } - } else if self.sess.edition() == Edition::Edition2018 { + } else if self.sess.edition() == Edition::Edition2018 && !pushed_prelude_crate { 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. + // 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()) } @@ -161,7 +163,12 @@ 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 + 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!( @@ -186,7 +193,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { .. }) => { debug!("try_push_visible_item_path: def_id={:?}", def_id); - self.push_item_path(buffer, def_id); + self.push_item_path(buffer, def_id, pushed_prelude_crate); cur_path.iter().rev().for_each(|segment| buffer.push(&segment)); return true; } @@ -230,13 +237,16 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - pub fn push_item_path(self, buffer: &mut T, def_id: DefId) + 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={:?}", buffer, def_id); + 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 }, _ => {} } @@ -245,11 +255,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { 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. @@ -272,34 +282,37 @@ 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(); - - 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 self.sess.extern_prelude.contains( - &data.as_interned_str().as_symbol() - ) => {}, - _ => 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) + 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); @@ -314,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. @@ -338,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 { @@ -364,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 | @@ -387,16 +400,19 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - fn push_impl_path_fallback(self, - buffer: &mut T, - impl_def_id: DefId) + 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 7651dfe4f34e9..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() } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c0a8009e53688..371b631723a39 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -4008,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,