]> git.lizzy.rs Git - rust.git/blob - src/librustc/ty/print/mod.rs
ccd2a702c9f262d3e46de137887b2f54bfcb1307
[rust.git] / src / librustc / ty / print / mod.rs
1 use crate::hir::map::DefPathData;
2 use crate::hir::def_id::{CrateNum, DefId};
3 use crate::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable};
4 use crate::ty::subst::{Subst, SubstsRef};
5
6 use rustc_data_structures::fx::FxHashSet;
7 use syntax::symbol::InternedString;
8
9 use std::iter;
10 use std::ops::Deref;
11
12 // `pretty` is a separate module only for organization.
13 mod pretty;
14 pub use self::pretty::*;
15
16 // FIXME(eddyb) this module uses `pub(crate)` for things used only
17 // from `ppaux` - when that is removed, they can be re-privatized.
18
19 struct LateBoundRegionNameCollector(FxHashSet<InternedString>);
20 impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector {
21     fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool {
22         match *r {
23             ty::ReLateBound(_, ty::BrNamed(_, name)) => {
24                 self.0.insert(name);
25             },
26             _ => {},
27         }
28         r.super_visit_with(self)
29     }
30 }
31
32 #[derive(Default)]
33 pub(crate) struct PrintConfig {
34     pub(crate) is_debug: bool,
35     used_region_names: Option<FxHashSet<InternedString>>,
36     region_index: usize,
37     binder_depth: usize,
38 }
39
40 pub struct PrintCx<'a, 'gcx, 'tcx, P> {
41     pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
42     pub printer: P,
43     pub(crate) config: &'a mut PrintConfig,
44 }
45
46 // HACK(eddyb) this is solely for `self: PrintCx<Self>`, e.g. to
47 // implement traits on the printer and call the methods on the context.
48 impl<P> Deref for PrintCx<'_, '_, '_, P> {
49     type Target = P;
50     fn deref(&self) -> &P {
51         &self.printer
52     }
53 }
54
55 impl<'a, 'gcx, 'tcx, P> PrintCx<'a, 'gcx, 'tcx, P> {
56     pub fn with<R>(
57         tcx: TyCtxt<'a, 'gcx, 'tcx>,
58         printer: P,
59         f: impl FnOnce(PrintCx<'_, 'gcx, 'tcx, P>) -> R,
60     ) -> R {
61         f(PrintCx {
62             tcx,
63             printer,
64             config: &mut PrintConfig::default(),
65         })
66     }
67
68     pub(crate) fn with_tls_tcx<R>(printer: P, f: impl FnOnce(PrintCx<'_, '_, '_, P>) -> R) -> R {
69         ty::tls::with(|tcx| PrintCx::with(tcx, printer, f))
70     }
71     fn prepare_late_bound_region_info<T>(&mut self, value: &ty::Binder<T>)
72     where T: TypeFoldable<'tcx>
73     {
74         let mut collector = LateBoundRegionNameCollector(Default::default());
75         value.visit_with(&mut collector);
76         self.config.used_region_names = Some(collector.0);
77         self.config.region_index = 0;
78     }
79 }
80
81 pub trait Print<'tcx, P> {
82     type Output;
83     type Error;
84
85     fn print(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error>;
86     fn print_display(
87         &self,
88         cx: PrintCx<'_, '_, 'tcx, P>,
89     ) -> Result<Self::Output, Self::Error> {
90         let old_debug = cx.config.is_debug;
91         cx.config.is_debug = false;
92         let result = self.print(PrintCx {
93             tcx: cx.tcx,
94             printer: cx.printer,
95             config: cx.config,
96         });
97         cx.config.is_debug = old_debug;
98         result
99     }
100     fn print_debug(&self, cx: PrintCx<'_, '_, 'tcx, P>) -> Result<Self::Output, Self::Error> {
101         let old_debug = cx.config.is_debug;
102         cx.config.is_debug = true;
103         let result = self.print(PrintCx {
104             tcx: cx.tcx,
105             printer: cx.printer,
106             config: cx.config,
107         });
108         cx.config.is_debug = old_debug;
109         result
110     }
111 }
112
113 pub trait Printer: Sized {
114     type Error;
115
116     type Path;
117     type Region;
118     type Type;
119
120     fn print_def_path(
121         self: PrintCx<'_, '_, 'tcx, Self>,
122         def_id: DefId,
123         substs: Option<SubstsRef<'tcx>>,
124         projections: impl Iterator<Item = ty::ExistentialProjection<'tcx>>,
125     ) -> Result<Self::Path, Self::Error> {
126         self.default_print_def_path(def_id, substs, projections)
127     }
128     fn print_impl_path(
129         self: PrintCx<'_, '_, 'tcx, Self>,
130         impl_def_id: DefId,
131         substs: Option<SubstsRef<'tcx>>,
132         self_ty: Ty<'tcx>,
133         trait_ref: Option<ty::TraitRef<'tcx>>,
134     ) -> Result<Self::Path, Self::Error> {
135         self.default_print_impl_path(impl_def_id, substs, self_ty, trait_ref)
136     }
137
138     fn print_region(
139         self: PrintCx<'_, '_, '_, Self>,
140         region: ty::Region<'_>,
141     ) -> Result<Self::Region, Self::Error>;
142
143     fn print_type(
144         self: PrintCx<'_, '_, 'tcx, Self>,
145         ty: Ty<'tcx>,
146     ) -> Result<Self::Type, Self::Error>;
147
148     fn path_crate(
149         self: PrintCx<'_, '_, '_, Self>,
150         cnum: CrateNum,
151     ) -> Result<Self::Path, Self::Error>;
152     fn path_qualified(
153         self: PrintCx<'_, '_, 'tcx, Self>,
154         self_ty: Ty<'tcx>,
155         trait_ref: Option<ty::TraitRef<'tcx>>,
156     ) -> Result<Self::Path, Self::Error>;
157
158     fn path_append_impl<'gcx, 'tcx>(
159         self: PrintCx<'_, 'gcx, 'tcx, Self>,
160         print_prefix: impl FnOnce(
161             PrintCx<'_, 'gcx, 'tcx, Self>,
162         ) -> Result<Self::Path, Self::Error>,
163         self_ty: Ty<'tcx>,
164         trait_ref: Option<ty::TraitRef<'tcx>>,
165     ) -> Result<Self::Path, Self::Error>;
166     fn path_append<'gcx, 'tcx>(
167         self: PrintCx<'_, 'gcx, 'tcx, Self>,
168         print_prefix: impl FnOnce(
169             PrintCx<'_, 'gcx, 'tcx, Self>,
170         ) -> Result<Self::Path, Self::Error>,
171         text: &str,
172     ) -> Result<Self::Path, Self::Error>;
173     fn path_generic_args<'gcx, 'tcx>(
174         self: PrintCx<'_, 'gcx, 'tcx, Self>,
175         print_prefix: impl FnOnce(
176             PrintCx<'_, 'gcx, 'tcx, Self>,
177         ) -> Result<Self::Path, Self::Error>,
178         params: &[ty::GenericParamDef],
179         substs: SubstsRef<'tcx>,
180         projections: impl Iterator<Item = ty::ExistentialProjection<'tcx>>,
181     ) -> Result<Self::Path, Self::Error>;
182 }
183
184 impl<P: Printer> PrintCx<'a, 'gcx, 'tcx, P> {
185     pub fn default_print_def_path(
186         self,
187         def_id: DefId,
188         substs: Option<SubstsRef<'tcx>>,
189         projections: impl Iterator<Item = ty::ExistentialProjection<'tcx>>,
190     ) -> Result<P::Path, P::Error> {
191         debug!("default_print_def_path: def_id={:?}, substs={:?}", def_id, substs);
192         let key = self.tcx.def_key(def_id);
193         debug!("default_print_def_path: key={:?}", key);
194
195         match key.disambiguated_data.data {
196             DefPathData::CrateRoot => {
197                 assert!(key.parent.is_none());
198                 self.path_crate(def_id.krate)
199             }
200
201             DefPathData::Impl => {
202                 let mut self_ty = self.tcx.type_of(def_id);
203                 if let Some(substs) = substs {
204                     self_ty = self_ty.subst(self.tcx, substs);
205                 }
206
207                 let mut impl_trait_ref = self.tcx.impl_trait_ref(def_id);
208                 if let Some(substs) = substs {
209                     impl_trait_ref = impl_trait_ref.subst(self.tcx, substs);
210                 }
211                 self.print_impl_path(def_id, substs, self_ty, impl_trait_ref)
212             }
213
214             _ => {
215                 let generics = substs.map(|_| self.tcx.generics_of(def_id));
216                 let generics_parent = generics.as_ref().and_then(|g| g.parent);
217                 let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
218                 let print_parent_path = |cx: PrintCx<'_, 'gcx, 'tcx, P>| {
219                     if let Some(generics_parent_def_id) = generics_parent {
220                         assert_eq!(parent_def_id, generics_parent_def_id);
221
222                         // FIXME(eddyb) try to move this into the parent's printing
223                         // logic, instead of doing it when printing the child.
224                         let parent_generics = cx.tcx.generics_of(parent_def_id);
225                         let parent_has_own_self =
226                             parent_generics.has_self && parent_generics.parent_count == 0;
227                         if let (Some(substs), true) = (substs, parent_has_own_self) {
228                             let trait_ref = ty::TraitRef::new(parent_def_id, substs);
229                             cx.path_qualified(trait_ref.self_ty(), Some(trait_ref))
230                         } else {
231                             cx.print_def_path(parent_def_id, substs, iter::empty())
232                         }
233                     } else {
234                         cx.print_def_path(parent_def_id, None, iter::empty())
235                     }
236                 };
237                 let print_path = |cx: PrintCx<'_, 'gcx, 'tcx, P>| {
238                     match key.disambiguated_data.data {
239                         // Skip `::{{constructor}}` on tuple/unit structs.
240                         DefPathData::StructCtor => print_parent_path(cx),
241
242                         _ => {
243                             cx.path_append(
244                                 print_parent_path,
245                                 &key.disambiguated_data.data.as_interned_str().as_str(),
246                             )
247                         }
248                     }
249                 };
250
251                 if let (Some(generics), Some(substs)) = (generics, substs) {
252                     let has_own_self = generics.has_self && generics.parent_count == 0;
253                     let params = &generics.params[has_own_self as usize..];
254                     self.path_generic_args(print_path, params, substs, projections)
255                 } else {
256                     print_path(self)
257                 }
258             }
259         }
260     }
261
262     fn default_print_impl_path(
263         self,
264         impl_def_id: DefId,
265         _substs: Option<SubstsRef<'tcx>>,
266         self_ty: Ty<'tcx>,
267         impl_trait_ref: Option<ty::TraitRef<'tcx>>,
268     ) -> Result<P::Path, P::Error> {
269         debug!("default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}",
270                impl_def_id, self_ty, impl_trait_ref);
271
272         // Decide whether to print the parent path for the impl.
273         // Logically, since impls are global, it's never needed, but
274         // users may find it useful. Currently, we omit the parent if
275         // the impl is either in the same module as the self-type or
276         // as the trait.
277         let parent_def_id = self.tcx.parent(impl_def_id).unwrap();
278         let in_self_mod = match characteristic_def_id_of_type(self_ty) {
279             None => false,
280             Some(ty_def_id) => self.tcx.parent(ty_def_id) == Some(parent_def_id),
281         };
282         let in_trait_mod = match impl_trait_ref {
283             None => false,
284             Some(trait_ref) => self.tcx.parent(trait_ref.def_id) == Some(parent_def_id),
285         };
286
287         if !in_self_mod && !in_trait_mod {
288             // If the impl is not co-located with either self-type or
289             // trait-type, then fallback to a format that identifies
290             // the module more clearly.
291             self.path_append_impl(
292                 |cx| cx.print_def_path(parent_def_id, None, iter::empty()),
293                 self_ty,
294                 impl_trait_ref,
295             )
296         } else {
297             // Otherwise, try to give a good form that would be valid language
298             // syntax. Preferably using associated item notation.
299             self.path_qualified(self_ty, impl_trait_ref)
300         }
301     }
302 }
303
304 /// As a heuristic, when we see an impl, if we see that the
305 /// 'self type' is a type defined in the same module as the impl,
306 /// we can omit including the path to the impl itself. This
307 /// function tries to find a "characteristic `DefId`" for a
308 /// type. It's just a heuristic so it makes some questionable
309 /// decisions and we may want to adjust it later.
310 pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
311     match ty.sty {
312         ty::Adt(adt_def, _) => Some(adt_def.did),
313
314         ty::Dynamic(data, ..) => data.principal_def_id(),
315
316         ty::Array(subty, _) |
317         ty::Slice(subty) => characteristic_def_id_of_type(subty),
318
319         ty::RawPtr(mt) => characteristic_def_id_of_type(mt.ty),
320
321         ty::Ref(_, ty, _) => characteristic_def_id_of_type(ty),
322
323         ty::Tuple(ref tys) => tys.iter()
324                                    .filter_map(|ty| characteristic_def_id_of_type(ty))
325                                    .next(),
326
327         ty::FnDef(def_id, _) |
328         ty::Closure(def_id, _) |
329         ty::Generator(def_id, _, _) |
330         ty::Foreign(def_id) => Some(def_id),
331
332         ty::Bool |
333         ty::Char |
334         ty::Int(_) |
335         ty::Uint(_) |
336         ty::Str |
337         ty::FnPtr(_) |
338         ty::Projection(_) |
339         ty::Placeholder(..) |
340         ty::UnnormalizedProjection(..) |
341         ty::Param(_) |
342         ty::Opaque(..) |
343         ty::Infer(_) |
344         ty::Bound(..) |
345         ty::Error |
346         ty::GeneratorWitness(..) |
347         ty::Never |
348         ty::Float(_) => None,
349     }
350 }