Skip to content

Commit 767c789

Browse files
committed
refactor item-paths in diagnostics, symbol names
This change has a few parts. We introduce a new `item_path` module for constructing item paths. The job of this module is basically to make nice, user-readable paths -- but these paths are not necessarily 100% unique. They meant to help a *human* find code, but not necessarily a compute. These paths are used to drive `item_path_str` but also symbol names. Because the paths are not unique, we also modify the symbol name hash to include the full `DefPath`, whereas before it included only those aspects of the def-path that were not included in the "informative" symbol name. Eventually, I'd like to make the item-path infrastructure a bit more declarative. Right now it's based purely on strings. In particular, for impls, we should supply the raw types to the `ItemPathBuffer`, so that symbol names can be encoded using the C++ encoding scheme for better integration with tooling.
1 parent 6ff88f9 commit 767c789

File tree

3 files changed

+362
-26
lines changed

3 files changed

+362
-26
lines changed

src/librustc/middle/ty/item_path.rs

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// Copyright 2012-2015 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+
use front::map::DefPathData;
12+
use middle::cstore::LOCAL_CRATE;
13+
use middle::def_id::DefId;
14+
use middle::ty::{self, Ty, TyCtxt};
15+
use syntax::ast;
16+
17+
impl<'tcx> TyCtxt<'tcx> {
18+
/// Returns a string identifying this def-id. This string is
19+
/// suitable for user output. It is relative to the current crate
20+
/// root.
21+
pub fn item_path_str(&self, def_id: DefId) -> String {
22+
let mut buffer = LocalPathBuffer::new(RootMode::Local);
23+
self.push_item_path(&mut buffer, def_id);
24+
buffer.into_string()
25+
}
26+
27+
/// Returns a string identifying this def-id. This string is
28+
/// suitable for user output. It always begins with a crate identifier.
29+
pub fn absolute_item_path_str(&self, def_id: DefId) -> String {
30+
let mut buffer = LocalPathBuffer::new(RootMode::Absolute);
31+
self.push_item_path(&mut buffer, def_id);
32+
buffer.into_string()
33+
}
34+
35+
/// Returns the "path" to a particular crate. This can proceed in
36+
/// various ways, depending on the `root_mode` of the `buffer`.
37+
/// (See `RootMode` enum for more details.)
38+
pub fn push_krate_path<T>(&self, buffer: &mut T, cnum: ast::CrateNum)
39+
where T: ItemPathBuffer
40+
{
41+
match *buffer.root_mode() {
42+
RootMode::Local => {
43+
// In local mode, when we encounter a crate other than
44+
// LOCAL_CRATE, execution proceeds in one of two ways:
45+
//
46+
// 1. for a direct dependency, where user added an
47+
// `extern crate` manually, we put the `extern
48+
// crate` as the parent. So you wind up with
49+
// something relative to the current crate.
50+
// 2. for an indirect crate, where there is no extern
51+
// crate, we just prepend the crate name.
52+
//
53+
// Returns `None` for the local crate.
54+
if cnum != LOCAL_CRATE {
55+
let opt_extern_crate = self.sess.cstore.extern_crate(cnum);
56+
let opt_extern_crate = opt_extern_crate.and_then(|extern_crate| {
57+
if extern_crate.direct {
58+
Some(extern_crate.def_id)
59+
} else {
60+
None
61+
}
62+
});
63+
if let Some(extern_crate_def_id) = opt_extern_crate {
64+
self.push_item_path(buffer, extern_crate_def_id);
65+
} else {
66+
buffer.push(&self.crate_name(cnum));
67+
}
68+
}
69+
}
70+
RootMode::Absolute => {
71+
// In absolute mode, just write the crate name
72+
// unconditionally.
73+
buffer.push(&self.crate_name(cnum));
74+
}
75+
}
76+
}
77+
78+
pub fn push_item_path<T>(&self, buffer: &mut T, def_id: DefId)
79+
where T: ItemPathBuffer
80+
{
81+
let key = self.def_key(def_id);
82+
match key.disambiguated_data.data {
83+
DefPathData::CrateRoot => {
84+
assert!(key.parent.is_none());
85+
self.push_krate_path(buffer, def_id.krate);
86+
}
87+
88+
DefPathData::InlinedRoot(ref root_path) => {
89+
assert!(key.parent.is_none());
90+
self.push_item_path(buffer, root_path.def_id);
91+
}
92+
93+
DefPathData::Impl => {
94+
self.push_impl_path(buffer, def_id);
95+
}
96+
97+
// Unclear if there is any value in distinguishing these.
98+
// Probably eventually (and maybe we would even want
99+
// finer-grained distinctions, e.g. between enum/struct).
100+
data @ DefPathData::Misc |
101+
data @ DefPathData::TypeNs(..) |
102+
data @ DefPathData::ValueNs(..) |
103+
data @ DefPathData::TypeParam(..) |
104+
data @ DefPathData::LifetimeDef(..) |
105+
data @ DefPathData::EnumVariant(..) |
106+
data @ DefPathData::Field(..) |
107+
data @ DefPathData::StructCtor |
108+
data @ DefPathData::Initializer |
109+
data @ DefPathData::MacroDef(..) |
110+
data @ DefPathData::ClosureExpr |
111+
data @ DefPathData::Binding(..) => {
112+
let parent_def_id = self.parent_def_id(def_id).unwrap();
113+
self.push_item_path(buffer, parent_def_id);
114+
buffer.push(&data.as_interned_str());
115+
}
116+
}
117+
}
118+
119+
fn push_impl_path<T>(&self,
120+
buffer: &mut T,
121+
impl_def_id: DefId)
122+
where T: ItemPathBuffer
123+
{
124+
let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
125+
126+
let use_types = if !impl_def_id.is_local() {
127+
// always have full types available for extern crates
128+
true
129+
} else {
130+
// for local crates, check whether type info is
131+
// available; typeck might not have completed yet
132+
self.impl_trait_refs.borrow().contains_key(&impl_def_id)
133+
};
134+
135+
if !use_types {
136+
return self.push_impl_path_fallback(buffer, impl_def_id);
137+
}
138+
139+
// Decide whether to print the parent path for the impl.
140+
// Logically, since impls are global, it's never needed, but
141+
// users may find it useful. Currently, we omit the parent if
142+
// the impl is either in the same module as the self-type or
143+
// as the trait.
144+
let self_ty = self.lookup_item_type(impl_def_id).ty;
145+
let in_self_mod = match self.characteristic_def_id_of_type(self_ty) {
146+
None => false,
147+
Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id),
148+
};
149+
150+
let impl_trait_ref = self.impl_trait_ref(impl_def_id);
151+
let in_trait_mod = match impl_trait_ref {
152+
None => false,
153+
Some(trait_ref) => self.parent_def_id(trait_ref.def_id) == Some(parent_def_id),
154+
};
155+
156+
if !in_self_mod && !in_trait_mod {
157+
// If the impl is not co-located with either self-type or
158+
// trait-type, then fallback to a format that identifies
159+
// the module more clearly.
160+
self.push_item_path(buffer, parent_def_id);
161+
if let Some(trait_ref) = impl_trait_ref {
162+
buffer.push(&format!("<impl {} for {}>", trait_ref, self_ty));
163+
} else {
164+
buffer.push(&format!("<impl {}>", self_ty));
165+
}
166+
return;
167+
}
168+
169+
// Otherwise, try to give a good form that would be valid language
170+
// syntax. Preferably using associated item notation.
171+
172+
if let Some(trait_ref) = impl_trait_ref {
173+
// Trait impls.
174+
buffer.push(&format!("<{} as {}>",
175+
self_ty,
176+
trait_ref));
177+
return;
178+
}
179+
180+
// Inherent impls. Try to print `Foo::bar` for an inherent
181+
// impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
182+
// anything other than a simple path.
183+
match self_ty.sty {
184+
ty::TyStruct(adt_def, substs) |
185+
ty::TyEnum(adt_def, substs) => {
186+
if substs.types.is_empty() { // ignore regions
187+
self.push_item_path(buffer, adt_def.did);
188+
} else {
189+
buffer.push(&format!("<{}>", self_ty));
190+
}
191+
}
192+
193+
ty::TyBool |
194+
ty::TyChar |
195+
ty::TyInt(_) |
196+
ty::TyUint(_) |
197+
ty::TyFloat(_) |
198+
ty::TyStr => {
199+
buffer.push(&format!("{}", self_ty));
200+
}
201+
202+
_ => {
203+
buffer.push(&format!("<{}>", self_ty));
204+
}
205+
}
206+
}
207+
208+
fn push_impl_path_fallback<T>(&self,
209+
buffer: &mut T,
210+
impl_def_id: DefId)
211+
where T: ItemPathBuffer
212+
{
213+
// If no type info is available, fall back to
214+
// pretty printing some span information. This should
215+
// only occur very early in the compiler pipeline.
216+
let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
217+
self.push_item_path(buffer, parent_def_id);
218+
let node_id = self.map.as_local_node_id(impl_def_id).unwrap();
219+
let item = self.map.expect_item(node_id);
220+
let span_str = self.sess.codemap().span_to_string(item.span);
221+
buffer.push(&format!("<impl at {}>", span_str));
222+
}
223+
224+
/// As a heuristic, when we see an impl, if we see that the
225+
/// 'self-type' is a type defined in the same module as the impl,
226+
/// we can omit including the path to the impl itself. This
227+
/// function tries to find a "characteristic def-id" for a
228+
/// type. It's just a heuristic so it makes some questionable
229+
/// decisions and we may want to adjust it later.
230+
fn characteristic_def_id_of_type(&self, ty: Ty<'tcx>) -> Option<DefId> {
231+
match ty.sty {
232+
ty::TyStruct(adt_def, _) |
233+
ty::TyEnum(adt_def, _) =>
234+
Some(adt_def.did),
235+
236+
ty::TyTrait(ref data) =>
237+
Some(data.principal_def_id()),
238+
239+
ty::TyBox(subty) =>
240+
self.characteristic_def_id_of_type(subty),
241+
242+
ty::TyRawPtr(mt) |
243+
ty::TyRef(_, mt) =>
244+
self.characteristic_def_id_of_type(mt.ty),
245+
246+
ty::TyTuple(ref tys) =>
247+
tys.iter()
248+
.filter_map(|ty| self.characteristic_def_id_of_type(ty))
249+
.next(),
250+
251+
_ =>
252+
None
253+
}
254+
}
255+
256+
/// Returns the def-id of `def_id`'s parent in the def tree. If
257+
/// this returns `None`, then `def_id` represents a crate root or
258+
/// inlined root.
259+
fn parent_def_id(&self, def_id: DefId) -> Option<DefId> {
260+
let key = self.def_key(def_id);
261+
key.parent.map(|index| DefId { krate: def_id.krate, index: index })
262+
}
263+
}
264+
265+
/// Unifying Trait for different kinds of item paths we might
266+
/// construct. The basic interface is that components get pushed: the
267+
/// instance can also customize how we handle the root of a crate.
268+
pub trait ItemPathBuffer {
269+
fn root_mode(&self) -> &RootMode;
270+
fn push(&mut self, text: &str);
271+
}
272+
273+
#[derive(Debug)]
274+
pub enum RootMode {
275+
/// Try to make a path relative to the local crate. In
276+
/// particular, local paths have no prefix, and if the path comes
277+
/// from an extern crate, start with the path to the `extern
278+
/// crate` declaration.
279+
Local,
280+
281+
/// Always prepend the crate name to the path, forming an absolute
282+
/// path from within a given set of crates.
283+
Absolute,
284+
}
285+
286+
#[derive(Debug)]
287+
struct LocalPathBuffer {
288+
root_mode: RootMode,
289+
str: String,
290+
}
291+
292+
impl LocalPathBuffer {
293+
fn new(root_mode: RootMode) -> LocalPathBuffer {
294+
LocalPathBuffer {
295+
root_mode: root_mode,
296+
str: String::new()
297+
}
298+
}
299+
300+
fn into_string(self) -> String {
301+
self.str
302+
}
303+
304+
}
305+
306+
impl ItemPathBuffer for LocalPathBuffer {
307+
fn root_mode(&self) -> &RootMode {
308+
&self.root_mode
309+
}
310+
311+
fn push(&mut self, text: &str) {
312+
if !self.str.is_empty() {
313+
self.str.push_str("::");
314+
}
315+
self.str.push_str(text);
316+
}
317+
}

src/librustc/middle/ty/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub mod cast;
8686
pub mod error;
8787
pub mod fast_reject;
8888
pub mod fold;
89+
pub mod item_path;
8990
pub mod _match;
9091
pub mod maps;
9192
pub mod outlives;
@@ -2218,8 +2219,12 @@ impl<'tcx> TyCtxt<'tcx> {
22182219
self.def_map.borrow().get(&tr.ref_id).expect("no def-map entry for trait").def_id()
22192220
}
22202221

2221-
pub fn item_path_str(&self, id: DefId) -> String {
2222-
self.with_path(id, |path| ast_map::path_to_string(path))
2222+
pub fn def_key(&self, id: DefId) -> ast_map::DefKey {
2223+
if id.is_local() {
2224+
self.map.def_key(id)
2225+
} else {
2226+
self.sess.cstore.def_key(id)
2227+
}
22232228
}
22242229

22252230
/// Returns the `DefPath` of an item. Note that if `id` is not

0 commit comments

Comments
 (0)