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.
This commit is contained in:
parent
cd5cf09635
commit
2291abf313
src
317
src/librustc/middle/ty/item_path.rs
Normal file
317
src/librustc/middle/ty/item_path.rs
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright 2012-2015 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.
|
||||
|
||||
use front::map::DefPathData;
|
||||
use middle::cstore::LOCAL_CRATE;
|
||||
use middle::def_id::DefId;
|
||||
use middle::ty::{self, Ty, TyCtxt};
|
||||
use syntax::ast;
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// Returns a string identifying this def-id. This string is
|
||||
/// suitable for user output. It is relative to the current crate
|
||||
/// root.
|
||||
pub fn item_path_str(&self, def_id: DefId) -> String {
|
||||
let mut buffer = LocalPathBuffer::new(RootMode::Local);
|
||||
self.push_item_path(&mut buffer, def_id);
|
||||
buffer.into_string()
|
||||
}
|
||||
|
||||
/// Returns a string identifying this def-id. This string is
|
||||
/// 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);
|
||||
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<T>(&self, buffer: &mut T, cnum: ast::CrateNum)
|
||||
where T: ItemPathBuffer
|
||||
{
|
||||
match *buffer.root_mode() {
|
||||
RootMode::Local => {
|
||||
// In local mode, when we encounter a crate other than
|
||||
// LOCAL_CRATE, execution proceeds in one of two ways:
|
||||
//
|
||||
// 1. for a direct dependency, where user added an
|
||||
// `extern crate` manually, we put the `extern
|
||||
// crate` as the parent. So you wind up with
|
||||
// something relative to the current crate.
|
||||
// 2. for an indirect crate, where there is no extern
|
||||
// crate, we just prepend the crate name.
|
||||
//
|
||||
// Returns `None` for the local crate.
|
||||
if cnum != LOCAL_CRATE {
|
||||
let opt_extern_crate = self.sess.cstore.extern_crate(cnum);
|
||||
let opt_extern_crate = opt_extern_crate.and_then(|extern_crate| {
|
||||
if extern_crate.direct {
|
||||
Some(extern_crate.def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(extern_crate_def_id) = opt_extern_crate {
|
||||
self.push_item_path(buffer, extern_crate_def_id);
|
||||
} else {
|
||||
buffer.push(&self.crate_name(cnum));
|
||||
}
|
||||
}
|
||||
}
|
||||
RootMode::Absolute => {
|
||||
// In absolute mode, just write the crate name
|
||||
// unconditionally.
|
||||
buffer.push(&self.crate_name(cnum));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_item_path<T>(&self, buffer: &mut T, def_id: DefId)
|
||||
where T: ItemPathBuffer
|
||||
{
|
||||
let key = self.def_key(def_id);
|
||||
match key.disambiguated_data.data {
|
||||
DefPathData::CrateRoot => {
|
||||
assert!(key.parent.is_none());
|
||||
self.push_krate_path(buffer, def_id.krate);
|
||||
}
|
||||
|
||||
DefPathData::InlinedRoot(ref root_path) => {
|
||||
assert!(key.parent.is_none());
|
||||
self.push_item_path(buffer, root_path.def_id);
|
||||
}
|
||||
|
||||
DefPathData::Impl => {
|
||||
self.push_impl_path(buffer, def_id);
|
||||
}
|
||||
|
||||
// Unclear if there is any value in distinguishing these.
|
||||
// Probably eventually (and maybe we would even want
|
||||
// finer-grained distinctions, e.g. between enum/struct).
|
||||
data @ DefPathData::Misc |
|
||||
data @ DefPathData::TypeNs(..) |
|
||||
data @ DefPathData::ValueNs(..) |
|
||||
data @ DefPathData::TypeParam(..) |
|
||||
data @ DefPathData::LifetimeDef(..) |
|
||||
data @ DefPathData::EnumVariant(..) |
|
||||
data @ DefPathData::Field(..) |
|
||||
data @ DefPathData::StructCtor |
|
||||
data @ DefPathData::Initializer |
|
||||
data @ DefPathData::MacroDef(..) |
|
||||
data @ DefPathData::ClosureExpr |
|
||||
data @ DefPathData::Binding(..) => {
|
||||
let parent_def_id = self.parent_def_id(def_id).unwrap();
|
||||
self.push_item_path(buffer, parent_def_id);
|
||||
buffer.push(&data.as_interned_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_impl_path<T>(&self,
|
||||
buffer: &mut T,
|
||||
impl_def_id: DefId)
|
||||
where T: ItemPathBuffer
|
||||
{
|
||||
let parent_def_id = self.parent_def_id(impl_def_id).unwrap();
|
||||
|
||||
let use_types = if !impl_def_id.is_local() {
|
||||
// always have full types available for extern crates
|
||||
true
|
||||
} else {
|
||||
// for local crates, check whether type info is
|
||||
// available; typeck might not have completed yet
|
||||
self.impl_trait_refs.borrow().contains_key(&impl_def_id)
|
||||
};
|
||||
|
||||
if !use_types {
|
||||
return self.push_impl_path_fallback(buffer, impl_def_id);
|
||||
}
|
||||
|
||||
// Decide whether to print the parent path for the impl.
|
||||
// Logically, since impls are global, it's never needed, but
|
||||
// users may find it useful. Currently, we omit the parent if
|
||||
// the impl is either in the same module as the self-type or
|
||||
// as the trait.
|
||||
let self_ty = self.lookup_item_type(impl_def_id).ty;
|
||||
let in_self_mod = match self.characteristic_def_id_of_type(self_ty) {
|
||||
None => false,
|
||||
Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id),
|
||||
};
|
||||
|
||||
let impl_trait_ref = self.impl_trait_ref(impl_def_id);
|
||||
let in_trait_mod = match impl_trait_ref {
|
||||
None => false,
|
||||
Some(trait_ref) => self.parent_def_id(trait_ref.def_id) == Some(parent_def_id),
|
||||
};
|
||||
|
||||
if !in_self_mod && !in_trait_mod {
|
||||
// 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);
|
||||
if let Some(trait_ref) = impl_trait_ref {
|
||||
buffer.push(&format!("<impl {} for {}>", trait_ref, self_ty));
|
||||
} else {
|
||||
buffer.push(&format!("<impl {}>", self_ty));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, try to give a good form that would be valid language
|
||||
// syntax. Preferably using associated item notation.
|
||||
|
||||
if let Some(trait_ref) = impl_trait_ref {
|
||||
// Trait impls.
|
||||
buffer.push(&format!("<{} as {}>",
|
||||
self_ty,
|
||||
trait_ref));
|
||||
return;
|
||||
}
|
||||
|
||||
// Inherent impls. Try to print `Foo::bar` for an inherent
|
||||
// impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
|
||||
// anything other than a simple path.
|
||||
match self_ty.sty {
|
||||
ty::TyStruct(adt_def, substs) |
|
||||
ty::TyEnum(adt_def, substs) => {
|
||||
if substs.types.is_empty() { // ignore regions
|
||||
self.push_item_path(buffer, adt_def.did);
|
||||
} else {
|
||||
buffer.push(&format!("<{}>", self_ty));
|
||||
}
|
||||
}
|
||||
|
||||
ty::TyBool |
|
||||
ty::TyChar |
|
||||
ty::TyInt(_) |
|
||||
ty::TyUint(_) |
|
||||
ty::TyFloat(_) |
|
||||
ty::TyStr => {
|
||||
buffer.push(&format!("{}", self_ty));
|
||||
}
|
||||
|
||||
_ => {
|
||||
buffer.push(&format!("<{}>", self_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_impl_path_fallback<T>(&self,
|
||||
buffer: &mut T,
|
||||
impl_def_id: DefId)
|
||||
where T: ItemPathBuffer
|
||||
{
|
||||
// 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);
|
||||
let node_id = self.map.as_local_node_id(impl_def_id).unwrap();
|
||||
let item = self.map.expect_item(node_id);
|
||||
let span_str = self.sess.codemap().span_to_string(item.span);
|
||||
buffer.push(&format!("<impl at {}>", span_str));
|
||||
}
|
||||
|
||||
/// As a heuristic, when we see an impl, if we see that the
|
||||
/// 'self-type' is a type defined in the same module as the impl,
|
||||
/// we can omit including the path to the impl itself. This
|
||||
/// function tries to find a "characteristic def-id" for a
|
||||
/// type. It's just a heuristic so it makes some questionable
|
||||
/// decisions and we may want to adjust it later.
|
||||
fn characteristic_def_id_of_type(&self, ty: Ty<'tcx>) -> Option<DefId> {
|
||||
match ty.sty {
|
||||
ty::TyStruct(adt_def, _) |
|
||||
ty::TyEnum(adt_def, _) =>
|
||||
Some(adt_def.did),
|
||||
|
||||
ty::TyTrait(ref data) =>
|
||||
Some(data.principal_def_id()),
|
||||
|
||||
ty::TyBox(subty) =>
|
||||
self.characteristic_def_id_of_type(subty),
|
||||
|
||||
ty::TyRawPtr(mt) |
|
||||
ty::TyRef(_, mt) =>
|
||||
self.characteristic_def_id_of_type(mt.ty),
|
||||
|
||||
ty::TyTuple(ref tys) =>
|
||||
tys.iter()
|
||||
.filter_map(|ty| self.characteristic_def_id_of_type(ty))
|
||||
.next(),
|
||||
|
||||
_ =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the def-id of `def_id`'s parent in the def tree. If
|
||||
/// this returns `None`, then `def_id` represents a crate root or
|
||||
/// inlined root.
|
||||
fn parent_def_id(&self, def_id: DefId) -> Option<DefId> {
|
||||
let key = self.def_key(def_id);
|
||||
key.parent.map(|index| DefId { krate: def_id.krate, index: index })
|
||||
}
|
||||
}
|
||||
|
||||
/// Unifying Trait for different kinds of item paths we might
|
||||
/// construct. The basic interface is that components get pushed: the
|
||||
/// instance can also customize how we handle the root of a crate.
|
||||
pub trait ItemPathBuffer {
|
||||
fn root_mode(&self) -> &RootMode;
|
||||
fn push(&mut self, text: &str);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RootMode {
|
||||
/// Try to make a path relative to the local crate. In
|
||||
/// particular, local paths have no prefix, and if the path comes
|
||||
/// from an extern crate, start with the path to the `extern
|
||||
/// crate` declaration.
|
||||
Local,
|
||||
|
||||
/// Always prepend the crate name to the path, forming an absolute
|
||||
/// path from within a given set of crates.
|
||||
Absolute,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LocalPathBuffer {
|
||||
root_mode: RootMode,
|
||||
str: String,
|
||||
}
|
||||
|
||||
impl LocalPathBuffer {
|
||||
fn new(root_mode: RootMode) -> LocalPathBuffer {
|
||||
LocalPathBuffer {
|
||||
root_mode: root_mode,
|
||||
str: String::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn into_string(self) -> String {
|
||||
self.str
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ItemPathBuffer for LocalPathBuffer {
|
||||
fn root_mode(&self) -> &RootMode {
|
||||
&self.root_mode
|
||||
}
|
||||
|
||||
fn push(&mut self, text: &str) {
|
||||
if !self.str.is_empty() {
|
||||
self.str.push_str("::");
|
||||
}
|
||||
self.str.push_str(text);
|
||||
}
|
||||
}
|
@ -86,6 +86,7 @@ pub mod cast;
|
||||
pub mod error;
|
||||
pub mod fast_reject;
|
||||
pub mod fold;
|
||||
pub mod item_path;
|
||||
pub mod _match;
|
||||
pub mod maps;
|
||||
pub mod outlives;
|
||||
@ -2218,8 +2219,12 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
self.def_map.borrow().get(&tr.ref_id).expect("no def-map entry for trait").def_id()
|
||||
}
|
||||
|
||||
pub fn item_path_str(&self, id: DefId) -> String {
|
||||
self.with_path(id, |path| ast_map::path_to_string(path))
|
||||
pub fn def_key(&self, id: DefId) -> ast_map::DefKey {
|
||||
if id.is_local() {
|
||||
self.map.def_key(id)
|
||||
} else {
|
||||
self.sess.cstore.def_key(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `DefPath` of an item. Note that if `id` is not
|
||||
|
@ -103,10 +103,10 @@ use util::sha2::{Digest, Sha256};
|
||||
use rustc::middle::cstore;
|
||||
use rustc::middle::def_id::DefId;
|
||||
use rustc::middle::ty::{self, TypeFoldable};
|
||||
use rustc::middle::ty::item_path::{ItemPathBuffer, RootMode};
|
||||
use rustc::front::map::definitions::DefPath;
|
||||
|
||||
use std::fmt::Write;
|
||||
use syntax::ast;
|
||||
use syntax::parse::token::{self, InternedString};
|
||||
use serialize::hex::ToHex;
|
||||
|
||||
@ -135,29 +135,23 @@ pub fn def_path_to_string<'tcx>(tcx: &ty::TyCtxt<'tcx>, def_path: &DefPath) -> S
|
||||
|
||||
fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
def_path: &DefPath,
|
||||
originating_crate: ast::CrateNum,
|
||||
parameters: &[ty::Ty<'tcx>])
|
||||
-> String {
|
||||
debug!("get_symbol_hash(def_path={:?}, parameters={:?})",
|
||||
def_path, parameters);
|
||||
|
||||
let tcx = ccx.tcx();
|
||||
|
||||
let mut hash_state = ccx.symbol_hasher().borrow_mut();
|
||||
|
||||
hash_state.reset();
|
||||
|
||||
if originating_crate == cstore::LOCAL_CRATE {
|
||||
hash_state.input_str(&tcx.sess.crate_disambiguator.borrow()[..]);
|
||||
} else {
|
||||
hash_state.input_str(&tcx.sess.cstore.crate_disambiguator(originating_crate));
|
||||
}
|
||||
|
||||
for component in def_path {
|
||||
let disambiguator_bytes = [(component.disambiguator >> 0) as u8,
|
||||
(component.disambiguator >> 8) as u8,
|
||||
(component.disambiguator >> 16) as u8,
|
||||
(component.disambiguator >> 24) as u8];
|
||||
hash_state.input(&disambiguator_bytes);
|
||||
}
|
||||
// the main symbol name is not necessarily unique; hash in the
|
||||
// compiler's internal def-path, guaranteeing each symbol has a
|
||||
// truly unique path
|
||||
hash_state.input_str(&def_path_to_string(tcx, def_path));
|
||||
|
||||
// also include any type parameters (for generic items)
|
||||
for t in parameters {
|
||||
assert!(!t.has_erasable_regions());
|
||||
assert!(!t.needs_subst());
|
||||
@ -180,6 +174,9 @@ fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
-> String {
|
||||
let &Instance { def: mut def_id, params: parameters } = instance;
|
||||
|
||||
debug!("exported_name_with_opt_suffix(def_id={:?}, parameters={:?}, suffix={:?})",
|
||||
def_id, parameters, suffix);
|
||||
|
||||
if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) {
|
||||
if let Some(&src_def_id) = ccx.external_srcs().borrow().get(&node_id) {
|
||||
def_id = src_def_id;
|
||||
@ -187,21 +184,34 @@ fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
let def_path = ccx.tcx().def_path(def_id);
|
||||
let hash = get_symbol_hash(ccx, &def_path, def_id.krate, parameters.as_slice());
|
||||
assert_eq!(def_path.krate, def_id.krate);
|
||||
let hash = get_symbol_hash(ccx, &def_path, parameters.as_slice());
|
||||
|
||||
let mut path = Vec::with_capacity(16);
|
||||
|
||||
if def_id.is_local() {
|
||||
path.push(ccx.tcx().crate_name.clone());
|
||||
}
|
||||
|
||||
path.extend(def_path.into_iter().map(|e| e.data.as_interned_str()));
|
||||
let mut buffer = SymbolPathBuffer {
|
||||
names: Vec::with_capacity(def_path.data.len())
|
||||
};
|
||||
ccx.tcx().push_item_path(&mut buffer, def_id);
|
||||
|
||||
if let Some(suffix) = suffix {
|
||||
path.push(token::intern_and_get_ident(suffix));
|
||||
buffer.push(suffix);
|
||||
}
|
||||
|
||||
mangle(path.into_iter(), Some(&hash[..]))
|
||||
mangle(buffer.names.into_iter(), Some(&hash[..]))
|
||||
}
|
||||
|
||||
struct SymbolPathBuffer {
|
||||
names: Vec<InternedString>,
|
||||
}
|
||||
|
||||
impl ItemPathBuffer for SymbolPathBuffer {
|
||||
fn root_mode(&self) -> &RootMode {
|
||||
const ABSOLUTE: &'static RootMode = &RootMode::Absolute;
|
||||
ABSOLUTE
|
||||
}
|
||||
|
||||
fn push(&mut self, text: &str) {
|
||||
self.names.push(token::intern(text).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
@ -225,7 +235,11 @@ pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>
|
||||
-> String {
|
||||
let path = [token::intern(&t.to_string()).as_str(),
|
||||
gensym_name(suffix).as_str()];
|
||||
let hash = get_symbol_hash(ccx, &Vec::new(), cstore::LOCAL_CRATE, &[t]);
|
||||
let def_path = DefPath {
|
||||
data: vec![],
|
||||
krate: cstore::LOCAL_CRATE,
|
||||
};
|
||||
let hash = get_symbol_hash(ccx, &def_path, &[t]);
|
||||
mangle(path.iter().cloned(), Some(&hash[..]))
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user