// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use hir::map::DefPathData; 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 std::cell::Cell; thread_local! { static FORCE_ABSOLUTE: Cell = Cell::new(false); static FORCE_IMPL_FILENAME_LINE: Cell = Cell::new(false); } /// Enforces that item_path_str always returns an absolute path and /// also enables "type-based" impl paths. This is used when building /// symbols that contain types, where we want the crate name to be /// part of the symbol. pub fn with_forced_absolute_paths R, R>(f: F) -> R { FORCE_ABSOLUTE.with(|force| { let old = force.get(); force.set(true); let result = f(); force.set(old); result }) } /// Force us to name impls with just the filename/line number. We /// normally try to use types. But at some points, notably while printing /// cycle errors, this can result in extra or suboptimal error output, /// so this variable disables that check. pub fn with_forced_impl_filename_line R, R>(f: F) -> R { FORCE_IMPL_FILENAME_LINE.with(|force| { let old = force.get(); force.set(true); let result = f(); force.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 /// root, unless with_forced_absolute_paths was used. pub fn item_path_str(self, def_id: DefId) -> String { let mode = FORCE_ABSOLUTE.with(|force| { if force.get() { RootMode::Absolute } else { RootMode::Local } }); let mut buffer = LocalPathBuffer::new(mode); self.push_item_path(&mut buffer, def_id); buffer.into_string() } /// Returns a string identifying this local node-id. pub fn node_path_str(self, id: ast::NodeId) -> String { self.item_path_str(self.hir.local_def_id(id)) } /// 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(self, buffer: &mut T, cnum: 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 extern inferred from a path or an indirect crate, // where there is no explicit `extern crate`, we just prepend // the crate name. // // Returns `None` for the local crate. if cnum != LOCAL_CRATE { let opt_extern_crate = self.extern_crate(cnum.as_def_id()); if let Some(ExternCrate { src: ExternCrateSource::Extern(def_id), direct: true, .. }) = *opt_extern_crate { self.push_item_path(buffer, def_id); } else { buffer.push(&self.crate_name(cnum).as_str()); } } } RootMode::Absolute => { // In absolute mode, just write the crate name // unconditionally. buffer.push(&self.original_crate_name(cnum).as_str()); } } } /// 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 { let visible_parent_map = self.visible_parent_map(LOCAL_CRATE); let (mut cur_def, mut cur_path) = (external_def_id, Vec::::new()); loop { // 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 { match *self.extern_crate(cur_def) { Some(ExternCrate { src: ExternCrateSource::Extern(def_id), direct: true, .. }) => { self.push_item_path(buffer, def_id); cur_path.iter().rev().for_each(|segment| buffer.push(&segment)); return true; } None => { buffer.push(&self.crate_name(cur_def.krate).as_str()); cur_path.iter().rev().for_each(|segment| buffer.push(&segment)); return true; } _ => {}, } } let mut cur_def_key = self.def_key(cur_def); // For a UnitStruct or TupleStruct we want the name of its parent rather than . if let DefPathData::StructCtor = cur_def_key.disambiguated_data.data { let parent = DefId { krate: cur_def.krate, index: cur_def_key.parent.expect("DefPathData::StructCtor missing a parent"), }; cur_def_key = self.def_key(parent); } let data = cur_def_key.disambiguated_data.data; let symbol = data.get_opt_name().map(|n| n.as_str()).unwrap_or_else(|| { Symbol::intern("").as_str() }); cur_path.push(symbol); match visible_parent_map.get(&cur_def) { Some(&def) => cur_def = def, None => return false, }; } } pub fn push_item_path(self, buffer: &mut T, def_id: DefId) where T: ItemPathBuffer { match *buffer.root_mode() { RootMode::Local if !def_id.is_local() => if self.try_push_visible_item_path(buffer, def_id) { return }, _ => {} } 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::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::Trait(..) | data @ DefPathData::AssocTypeInTrait(..) | data @ DefPathData::AssocTypeInImpl(..) | data @ DefPathData::ValueNs(..) | data @ DefPathData::Module(..) | data @ DefPathData::TypeParam(..) | data @ DefPathData::LifetimeDef(..) | data @ DefPathData::EnumVariant(..) | data @ DefPathData::Field(..) | data @ DefPathData::Initializer | data @ DefPathData::MacroDef(..) | data @ DefPathData::ClosureExpr | data @ DefPathData::ImplTrait | data @ DefPathData::Typeof | data @ DefPathData::GlobalMetaData(..) => { 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().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); } } } fn push_impl_path(self, buffer: &mut T, impl_def_id: DefId) where T: ItemPathBuffer { let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); // Always use types for non-local impls, where types are always // available, and filename/line-number is mostly uninteresting. let use_types = !impl_def_id.is_local() || { // Otherwise, use filename/line-number if forced. let force_no_types = FORCE_IMPL_FILENAME_LINE.with(|f| f.get()); !force_no_types }; 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.type_of(impl_def_id); let in_self_mod = match 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!("", trait_ref, self_ty)); } else { buffer.push(&format!("", 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 `::bar` if self-type is // anything other than a simple path. match self_ty.sty { ty::TyAdt(adt_def, substs) => { if substs.types().next().is_none() { // ignore regions self.push_item_path(buffer, adt_def.did); } else { buffer.push(&format!("<{}>", self_ty)); } } ty::TyForeign(did) => self.push_item_path(buffer, did), 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(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.hir.as_local_node_id(impl_def_id).unwrap(); let item = self.hir.expect_item(node_id); let span_str = self.sess.codemap().span_to_string(item.span); buffer.push(&format!("", span_str)); } /// 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. pub fn parent_def_id(self, def_id: DefId) -> Option { let key = self.def_key(def_id); key.parent.map(|index| DefId { krate: def_id.krate, index: index }) } } /// 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. pub fn characteristic_def_id_of_type(ty: Ty) -> Option { match ty.sty { ty::TyAdt(adt_def, _) => Some(adt_def.did), ty::TyDynamic(data, ..) => data.principal().map(|p| p.def_id()), ty::TyArray(subty, _) | ty::TySlice(subty) => characteristic_def_id_of_type(subty), ty::TyRawPtr(mt) | ty::TyRef(_, mt) => characteristic_def_id_of_type(mt.ty), ty::TyTuple(ref tys) => tys.iter() .filter_map(|ty| characteristic_def_id_of_type(ty)) .next(), ty::TyFnDef(def_id, _) | ty::TyClosure(def_id, _) | ty::TyGenerator(def_id, _, _) | ty::TyForeign(def_id) => Some(def_id), ty::TyBool | ty::TyChar | ty::TyInt(_) | ty::TyUint(_) | ty::TyStr | ty::TyFnPtr(_) | ty::TyProjection(_) | ty::TyParam(_) | ty::TyAnon(..) | ty::TyInfer(_) | ty::TyError | ty::TyGeneratorWitness(..) | ty::TyNever | ty::TyFloat(_) => None, } } /// 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, 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); } }