]>
Commit | Line | Data |
---|---|---|
54a0048b SL |
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 hir::map::DefPathData; | |
9e0c209e | 12 | use hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
54a0048b SL |
13 | use ty::{self, Ty, TyCtxt}; |
14 | use syntax::ast; | |
476ff2be | 15 | use syntax::symbol::Symbol; |
3157f602 XL |
16 | |
17 | use std::cell::Cell; | |
18 | ||
19 | thread_local! { | |
20 | static FORCE_ABSOLUTE: Cell<bool> = Cell::new(false) | |
21 | } | |
22 | ||
23 | /// Enforces that item_path_str always returns an absolute path. | |
24 | /// This is useful when building symbols that contain types, | |
25 | /// where we want the crate name to be part of the symbol. | |
26 | pub fn with_forced_absolute_paths<F: FnOnce() -> R, R>(f: F) -> R { | |
27 | FORCE_ABSOLUTE.with(|force| { | |
28 | let old = force.get(); | |
29 | force.set(true); | |
30 | let result = f(); | |
31 | force.set(old); | |
32 | result | |
33 | }) | |
34 | } | |
54a0048b | 35 | |
a7813a04 | 36 | impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { |
54a0048b SL |
37 | /// Returns a string identifying this def-id. This string is |
38 | /// suitable for user output. It is relative to the current crate | |
3157f602 | 39 | /// root, unless with_forced_absolute_paths was used. |
a7813a04 | 40 | pub fn item_path_str(self, def_id: DefId) -> String { |
3157f602 XL |
41 | let mode = FORCE_ABSOLUTE.with(|force| { |
42 | if force.get() { | |
43 | RootMode::Absolute | |
44 | } else { | |
45 | RootMode::Local | |
46 | } | |
47 | }); | |
48 | let mut buffer = LocalPathBuffer::new(mode); | |
54a0048b SL |
49 | self.push_item_path(&mut buffer, def_id); |
50 | buffer.into_string() | |
51 | } | |
52 | ||
53 | /// Returns a string identifying this local node-id. | |
a7813a04 | 54 | pub fn node_path_str(self, id: ast::NodeId) -> String { |
32a655c1 | 55 | self.item_path_str(self.hir.local_def_id(id)) |
54a0048b SL |
56 | } |
57 | ||
58 | /// Returns a string identifying this def-id. This string is | |
59 | /// suitable for user output. It always begins with a crate identifier. | |
a7813a04 | 60 | pub fn absolute_item_path_str(self, def_id: DefId) -> String { |
54a0048b SL |
61 | let mut buffer = LocalPathBuffer::new(RootMode::Absolute); |
62 | self.push_item_path(&mut buffer, def_id); | |
63 | buffer.into_string() | |
64 | } | |
65 | ||
66 | /// Returns the "path" to a particular crate. This can proceed in | |
67 | /// various ways, depending on the `root_mode` of the `buffer`. | |
68 | /// (See `RootMode` enum for more details.) | |
9e0c209e | 69 | pub fn push_krate_path<T>(self, buffer: &mut T, cnum: CrateNum) |
54a0048b SL |
70 | where T: ItemPathBuffer |
71 | { | |
72 | match *buffer.root_mode() { | |
73 | RootMode::Local => { | |
74 | // In local mode, when we encounter a crate other than | |
75 | // LOCAL_CRATE, execution proceeds in one of two ways: | |
76 | // | |
77 | // 1. for a direct dependency, where user added an | |
78 | // `extern crate` manually, we put the `extern | |
79 | // crate` as the parent. So you wind up with | |
80 | // something relative to the current crate. | |
81 | // 2. for an indirect crate, where there is no extern | |
82 | // crate, we just prepend the crate name. | |
83 | // | |
84 | // Returns `None` for the local crate. | |
85 | if cnum != LOCAL_CRATE { | |
86 | let opt_extern_crate = self.sess.cstore.extern_crate(cnum); | |
87 | let opt_extern_crate = opt_extern_crate.and_then(|extern_crate| { | |
88 | if extern_crate.direct { | |
89 | Some(extern_crate.def_id) | |
90 | } else { | |
91 | None | |
92 | } | |
93 | }); | |
94 | if let Some(extern_crate_def_id) = opt_extern_crate { | |
95 | self.push_item_path(buffer, extern_crate_def_id); | |
96 | } else { | |
476ff2be | 97 | buffer.push(&self.crate_name(cnum).as_str()); |
54a0048b SL |
98 | } |
99 | } | |
100 | } | |
101 | RootMode::Absolute => { | |
102 | // In absolute mode, just write the crate name | |
103 | // unconditionally. | |
476ff2be | 104 | buffer.push(&self.original_crate_name(cnum).as_str()); |
54a0048b SL |
105 | } |
106 | } | |
107 | } | |
108 | ||
109 | /// If possible, this pushes a global path resolving to `external_def_id` that is visible | |
110 | /// from at least one local module and returns true. If the crate defining `external_def_id` is | |
111 | /// declared with an `extern crate`, the path is guarenteed to use the `extern crate`. | |
a7813a04 | 112 | pub fn try_push_visible_item_path<T>(self, buffer: &mut T, external_def_id: DefId) -> bool |
54a0048b SL |
113 | where T: ItemPathBuffer |
114 | { | |
115 | let visible_parent_map = self.sess.cstore.visible_parent_map(); | |
116 | ||
117 | let (mut cur_def, mut cur_path) = (external_def_id, Vec::<ast::Name>::new()); | |
118 | loop { | |
119 | // If `cur_def` is a direct or injected extern crate, push the path to the crate | |
120 | // followed by the path to the item within the crate and return. | |
121 | if cur_def.index == CRATE_DEF_INDEX { | |
122 | match self.sess.cstore.extern_crate(cur_def.krate) { | |
123 | Some(extern_crate) if extern_crate.direct => { | |
124 | self.push_item_path(buffer, extern_crate.def_id); | |
125 | cur_path.iter().rev().map(|segment| buffer.push(&segment.as_str())).count(); | |
126 | return true; | |
127 | } | |
128 | None => { | |
476ff2be | 129 | buffer.push(&self.crate_name(cur_def.krate).as_str()); |
54a0048b SL |
130 | cur_path.iter().rev().map(|segment| buffer.push(&segment.as_str())).count(); |
131 | return true; | |
132 | } | |
133 | _ => {}, | |
134 | } | |
135 | } | |
136 | ||
9e0c209e SL |
137 | cur_path.push(self.sess.cstore.def_key(cur_def) |
138 | .disambiguated_data.data.get_opt_name().unwrap_or_else(|| | |
476ff2be | 139 | Symbol::intern("<unnamed>"))); |
54a0048b SL |
140 | match visible_parent_map.get(&cur_def) { |
141 | Some(&def) => cur_def = def, | |
142 | None => return false, | |
143 | }; | |
144 | } | |
145 | } | |
146 | ||
a7813a04 | 147 | pub fn push_item_path<T>(self, buffer: &mut T, def_id: DefId) |
54a0048b SL |
148 | where T: ItemPathBuffer |
149 | { | |
150 | match *buffer.root_mode() { | |
151 | RootMode::Local if !def_id.is_local() => | |
152 | if self.try_push_visible_item_path(buffer, def_id) { return }, | |
153 | _ => {} | |
154 | } | |
155 | ||
156 | let key = self.def_key(def_id); | |
157 | match key.disambiguated_data.data { | |
158 | DefPathData::CrateRoot => { | |
159 | assert!(key.parent.is_none()); | |
160 | self.push_krate_path(buffer, def_id.krate); | |
161 | } | |
162 | ||
54a0048b SL |
163 | DefPathData::Impl => { |
164 | self.push_impl_path(buffer, def_id); | |
165 | } | |
166 | ||
167 | // Unclear if there is any value in distinguishing these. | |
168 | // Probably eventually (and maybe we would even want | |
169 | // finer-grained distinctions, e.g. between enum/struct). | |
170 | data @ DefPathData::Misc | | |
171 | data @ DefPathData::TypeNs(..) | | |
172 | data @ DefPathData::ValueNs(..) | | |
a7813a04 | 173 | data @ DefPathData::Module(..) | |
54a0048b SL |
174 | data @ DefPathData::TypeParam(..) | |
175 | data @ DefPathData::LifetimeDef(..) | | |
176 | data @ DefPathData::EnumVariant(..) | | |
177 | data @ DefPathData::Field(..) | | |
178 | data @ DefPathData::StructCtor | | |
179 | data @ DefPathData::Initializer | | |
180 | data @ DefPathData::MacroDef(..) | | |
181 | data @ DefPathData::ClosureExpr | | |
5bcae85e | 182 | data @ DefPathData::Binding(..) | |
8bb4bdeb XL |
183 | data @ DefPathData::ImplTrait | |
184 | data @ DefPathData::Typeof => { | |
54a0048b SL |
185 | let parent_def_id = self.parent_def_id(def_id).unwrap(); |
186 | self.push_item_path(buffer, parent_def_id); | |
187 | buffer.push(&data.as_interned_str()); | |
188 | } | |
189 | } | |
190 | } | |
191 | ||
a7813a04 | 192 | fn push_impl_path<T>(self, |
54a0048b SL |
193 | buffer: &mut T, |
194 | impl_def_id: DefId) | |
195 | where T: ItemPathBuffer | |
196 | { | |
197 | let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); | |
198 | ||
199 | let use_types = if !impl_def_id.is_local() { | |
200 | // always have full types available for extern crates | |
201 | true | |
202 | } else { | |
203 | // for local crates, check whether type info is | |
204 | // available; typeck might not have completed yet | |
8bb4bdeb | 205 | self.maps.impl_trait_ref.borrow().contains_key(&impl_def_id) |
54a0048b SL |
206 | }; |
207 | ||
208 | if !use_types { | |
209 | return self.push_impl_path_fallback(buffer, impl_def_id); | |
210 | } | |
211 | ||
212 | // Decide whether to print the parent path for the impl. | |
213 | // Logically, since impls are global, it's never needed, but | |
214 | // users may find it useful. Currently, we omit the parent if | |
215 | // the impl is either in the same module as the self-type or | |
216 | // as the trait. | |
476ff2be | 217 | let self_ty = self.item_type(impl_def_id); |
a7813a04 | 218 | let in_self_mod = match characteristic_def_id_of_type(self_ty) { |
54a0048b SL |
219 | None => false, |
220 | Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id), | |
221 | }; | |
222 | ||
223 | let impl_trait_ref = self.impl_trait_ref(impl_def_id); | |
224 | let in_trait_mod = match impl_trait_ref { | |
225 | None => false, | |
226 | Some(trait_ref) => self.parent_def_id(trait_ref.def_id) == Some(parent_def_id), | |
227 | }; | |
228 | ||
229 | if !in_self_mod && !in_trait_mod { | |
230 | // If the impl is not co-located with either self-type or | |
231 | // trait-type, then fallback to a format that identifies | |
232 | // the module more clearly. | |
233 | self.push_item_path(buffer, parent_def_id); | |
234 | if let Some(trait_ref) = impl_trait_ref { | |
235 | buffer.push(&format!("<impl {} for {}>", trait_ref, self_ty)); | |
236 | } else { | |
237 | buffer.push(&format!("<impl {}>", self_ty)); | |
238 | } | |
239 | return; | |
240 | } | |
241 | ||
242 | // Otherwise, try to give a good form that would be valid language | |
243 | // syntax. Preferably using associated item notation. | |
244 | ||
245 | if let Some(trait_ref) = impl_trait_ref { | |
246 | // Trait impls. | |
247 | buffer.push(&format!("<{} as {}>", | |
248 | self_ty, | |
249 | trait_ref)); | |
250 | return; | |
251 | } | |
252 | ||
253 | // Inherent impls. Try to print `Foo::bar` for an inherent | |
254 | // impl on `Foo`, but fallback to `<Foo>::bar` if self-type is | |
255 | // anything other than a simple path. | |
256 | match self_ty.sty { | |
9e0c209e SL |
257 | ty::TyAdt(adt_def, substs) => { |
258 | if substs.types().next().is_none() { // ignore regions | |
54a0048b SL |
259 | self.push_item_path(buffer, adt_def.did); |
260 | } else { | |
261 | buffer.push(&format!("<{}>", self_ty)); | |
262 | } | |
263 | } | |
264 | ||
265 | ty::TyBool | | |
266 | ty::TyChar | | |
267 | ty::TyInt(_) | | |
268 | ty::TyUint(_) | | |
269 | ty::TyFloat(_) | | |
270 | ty::TyStr => { | |
271 | buffer.push(&format!("{}", self_ty)); | |
272 | } | |
273 | ||
274 | _ => { | |
275 | buffer.push(&format!("<{}>", self_ty)); | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
a7813a04 | 280 | fn push_impl_path_fallback<T>(self, |
54a0048b SL |
281 | buffer: &mut T, |
282 | impl_def_id: DefId) | |
283 | where T: ItemPathBuffer | |
284 | { | |
285 | // If no type info is available, fall back to | |
286 | // pretty printing some span information. This should | |
287 | // only occur very early in the compiler pipeline. | |
288 | let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); | |
289 | self.push_item_path(buffer, parent_def_id); | |
32a655c1 SL |
290 | let node_id = self.hir.as_local_node_id(impl_def_id).unwrap(); |
291 | let item = self.hir.expect_item(node_id); | |
54a0048b SL |
292 | let span_str = self.sess.codemap().span_to_string(item.span); |
293 | buffer.push(&format!("<impl at {}>", span_str)); | |
294 | } | |
295 | ||
54a0048b SL |
296 | /// Returns the def-id of `def_id`'s parent in the def tree. If |
297 | /// this returns `None`, then `def_id` represents a crate root or | |
298 | /// inlined root. | |
9e0c209e | 299 | pub fn parent_def_id(self, def_id: DefId) -> Option<DefId> { |
54a0048b SL |
300 | let key = self.def_key(def_id); |
301 | key.parent.map(|index| DefId { krate: def_id.krate, index: index }) | |
302 | } | |
303 | } | |
304 | ||
a7813a04 XL |
305 | /// As a heuristic, when we see an impl, if we see that the |
306 | /// 'self-type' is a type defined in the same module as the impl, | |
307 | /// we can omit including the path to the impl itself. This | |
308 | /// function tries to find a "characteristic def-id" for a | |
309 | /// type. It's just a heuristic so it makes some questionable | |
310 | /// decisions and we may want to adjust it later. | |
311 | pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> { | |
312 | match ty.sty { | |
9e0c209e | 313 | ty::TyAdt(adt_def, _) => Some(adt_def.did), |
a7813a04 | 314 | |
476ff2be | 315 | ty::TyDynamic(data, ..) => data.principal().map(|p| p.def_id()), |
a7813a04 XL |
316 | |
317 | ty::TyArray(subty, _) | | |
32a655c1 | 318 | ty::TySlice(subty) => characteristic_def_id_of_type(subty), |
a7813a04 XL |
319 | |
320 | ty::TyRawPtr(mt) | | |
321 | ty::TyRef(_, mt) => characteristic_def_id_of_type(mt.ty), | |
322 | ||
8bb4bdeb XL |
323 | ty::TyTuple(ref tys, _) => tys.iter() |
324 | .filter_map(|ty| characteristic_def_id_of_type(ty)) | |
325 | .next(), | |
a7813a04 | 326 | |
9e0c209e | 327 | ty::TyFnDef(def_id, ..) | |
a7813a04 XL |
328 | ty::TyClosure(def_id, _) => Some(def_id), |
329 | ||
330 | ty::TyBool | | |
331 | ty::TyChar | | |
332 | ty::TyInt(_) | | |
333 | ty::TyUint(_) | | |
334 | ty::TyStr | | |
335 | ty::TyFnPtr(_) | | |
336 | ty::TyProjection(_) | | |
337 | ty::TyParam(_) | | |
5bcae85e | 338 | ty::TyAnon(..) | |
a7813a04 XL |
339 | ty::TyInfer(_) | |
340 | ty::TyError | | |
5bcae85e | 341 | ty::TyNever | |
a7813a04 XL |
342 | ty::TyFloat(_) => None, |
343 | } | |
344 | } | |
345 | ||
54a0048b SL |
346 | /// Unifying Trait for different kinds of item paths we might |
347 | /// construct. The basic interface is that components get pushed: the | |
348 | /// instance can also customize how we handle the root of a crate. | |
349 | pub trait ItemPathBuffer { | |
350 | fn root_mode(&self) -> &RootMode; | |
351 | fn push(&mut self, text: &str); | |
352 | } | |
353 | ||
354 | #[derive(Debug)] | |
355 | pub enum RootMode { | |
356 | /// Try to make a path relative to the local crate. In | |
357 | /// particular, local paths have no prefix, and if the path comes | |
358 | /// from an extern crate, start with the path to the `extern | |
359 | /// crate` declaration. | |
360 | Local, | |
361 | ||
362 | /// Always prepend the crate name to the path, forming an absolute | |
363 | /// path from within a given set of crates. | |
364 | Absolute, | |
365 | } | |
366 | ||
367 | #[derive(Debug)] | |
368 | struct LocalPathBuffer { | |
369 | root_mode: RootMode, | |
370 | str: String, | |
371 | } | |
372 | ||
373 | impl LocalPathBuffer { | |
374 | fn new(root_mode: RootMode) -> LocalPathBuffer { | |
375 | LocalPathBuffer { | |
376 | root_mode: root_mode, | |
377 | str: String::new() | |
378 | } | |
379 | } | |
380 | ||
381 | fn into_string(self) -> String { | |
382 | self.str | |
383 | } | |
384 | ||
385 | } | |
386 | ||
387 | impl ItemPathBuffer for LocalPathBuffer { | |
388 | fn root_mode(&self) -> &RootMode { | |
389 | &self.root_mode | |
390 | } | |
391 | ||
392 | fn push(&mut self, text: &str) { | |
393 | if !self.str.is_empty() { | |
394 | self.str.push_str("::"); | |
395 | } | |
396 | self.str.push_str(text); | |
397 | } | |
398 | } |