|
| 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 | +} |
0 commit comments