blob: 92d5b734f17d2b133a5bd87f9b02b634236d7532 [file] [log] [blame]
// 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 hir::map::DefPathData;
use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use ty::{self, Ty, TyCtxt};
use syntax::ast;
use syntax::symbol::Symbol;
use std::cell::Cell;
thread_local! {
static FORCE_ABSOLUTE: Cell<bool> = Cell::new(false)
}
/// Enforces that item_path_str always returns an absolute path.
/// This is useful when building symbols that contain types,
/// where we want the crate name to be part of the symbol.
pub fn with_forced_absolute_paths<F: FnOnce() -> R, R>(f: F) -> R {
FORCE_ABSOLUTE.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.map.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<T>(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 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.original_crate_name(cnum));
}
}
}
/// 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 guarenteed to use the `extern crate`.
pub fn try_push_visible_item_path<T>(self, buffer: &mut T, external_def_id: DefId) -> bool
where T: ItemPathBuffer
{
let visible_parent_map = self.sess.cstore.visible_parent_map();
let (mut cur_def, mut cur_path) = (external_def_id, Vec::<ast::Name>::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.sess.cstore.extern_crate(cur_def.krate) {
Some(extern_crate) if extern_crate.direct => {
self.push_item_path(buffer, extern_crate.def_id);
cur_path.iter().rev().map(|segment| buffer.push(&segment.as_str())).count();
return true;
}
None => {
buffer.push(&self.crate_name(cur_def.krate));
cur_path.iter().rev().map(|segment| buffer.push(&segment.as_str())).count();
return true;
}
_ => {},
}
}
cur_path.push(self.sess.cstore.def_key(cur_def)
.disambiguated_data.data.get_opt_name().unwrap_or_else(||
Symbol::intern("<unnamed>")));
match visible_parent_map.get(&cur_def) {
Some(&def) => cur_def = def,
None => return false,
};
}
}
pub fn push_item_path<T>(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::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::Module(..) |
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(..) |
data @ DefPathData::ImplTrait => {
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.item_type(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!("<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::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::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));
}
/// 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<DefId> {
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<DefId> {
match ty.sty {
ty::TyAdt(adt_def, _) => Some(adt_def.did),
ty::TyTrait(ref data) => Some(data.principal.def_id()),
ty::TyArray(subty, _) |
ty::TySlice(subty) |
ty::TyBox(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, _) => 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::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: 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);
}
}