]> git.lizzy.rs Git - rust.git/commitdiff
refactor item-paths in diagnostics, symbol names
authorNiko Matsakis <niko@alum.mit.edu>
Wed, 16 Mar 2016 09:57:03 +0000 (05:57 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Fri, 25 Mar 2016 18:07:19 +0000 (14:07 -0400)
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.

src/librustc/middle/ty/item_path.rs [new file with mode: 0644]
src/librustc/middle/ty/mod.rs
src/librustc_trans/back/symbol_names.rs

diff --git a/src/librustc/middle/ty/item_path.rs b/src/librustc/middle/ty/item_path.rs
new file mode 100644 (file)
index 0000000..147230f
--- /dev/null
@@ -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);
+    }
+}
index 61e591e2fcea63645f64775805c24e4882b80b6b..a4c3e82b6335db7472435cff21dda19edff0f1bd 100644 (file)
@@ -86,6 +86,7 @@
 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 @@ pub fn trait_ref_to_def_id(&self, tr: &hir::TraitRef) -> DefId {
         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
index a67b6841f02a187d9b1c723a0575d75fdb69223c..81b00c3827f3c1c69f8cdfeb0fdd2815adfed6c1 100644 (file)
 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);
+    let mut buffer = SymbolPathBuffer {
+        names: Vec::with_capacity(def_path.data.len())
+    };
+    ccx.tcx().push_item_path(&mut buffer, def_id);
 
-    if def_id.is_local() {
-        path.push(ccx.tcx().crate_name.clone());
+    if let Some(suffix) = suffix {
+        buffer.push(suffix);
     }
 
-    path.extend(def_path.into_iter().map(|e| e.data.as_interned_str()));
+    mangle(buffer.names.into_iter(), Some(&hash[..]))
+}
 
-    if let Some(suffix) = suffix {
-        path.push(token::intern_and_get_ident(suffix));
+struct SymbolPathBuffer {
+    names: Vec<InternedString>,
+}
+
+impl ItemPathBuffer for SymbolPathBuffer {
+    fn root_mode(&self) -> &RootMode {
+        const ABSOLUTE: &'static RootMode = &RootMode::Absolute;
+        ABSOLUTE
     }
 
-    mangle(path.into_iter(), Some(&hash[..]))
+    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[..]))
 }