]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/ty/item_path.rs
refactor item-paths in diagnostics, symbol names
[rust.git] / src / librustc / middle / 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 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 }