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