1 | use std::path::PathBuf; |
---|---|
2 | |
3 | use hir::def::Namespace; |
4 | use rustc_data_structures::fx::FxHashSet; |
5 | use rustc_data_structures::sso::SsoHashSet; |
6 | use rustc_hir as hir; |
7 | use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; |
8 | use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; |
9 | use tracing::{debug, instrument, trace}; |
10 | |
11 | use crate::ty::{self, GenericArg, ShortInstance, Ty, TyCtxt}; |
12 | |
13 | // `pretty` is a separate module only for organization. |
14 | mod pretty; |
15 | pub use self::pretty::*; |
16 | use super::Lift; |
17 | |
18 | pub type PrintError = std::fmt::Error; |
19 | |
20 | pub trait Print<'tcx, P> { |
21 | fn print(&self, cx: &mut P) -> Result<(), PrintError>; |
22 | } |
23 | |
24 | /// Interface for outputting user-facing "type-system entities" |
25 | /// (paths, types, lifetimes, constants, etc.) as a side-effect |
26 | /// (e.g. formatting, like `PrettyPrinter` implementors do) or by |
27 | /// constructing some alternative representation (e.g. an AST), |
28 | /// which the associated types allow passing through the methods. |
29 | /// |
30 | /// For pretty-printing/formatting in particular, see `PrettyPrinter`. |
31 | // |
32 | // FIXME(eddyb) find a better name; this is more general than "printing". |
33 | pub trait Printer<'tcx>: Sized { |
34 | fn tcx<'a>(&'a self) -> TyCtxt<'tcx>; |
35 | |
36 | fn print_def_path( |
37 | &mut self, |
38 | def_id: DefId, |
39 | args: &'tcx [GenericArg<'tcx>], |
40 | ) -> Result<(), PrintError> { |
41 | self.default_print_def_path(def_id, args) |
42 | } |
43 | |
44 | fn print_impl_path( |
45 | &mut self, |
46 | impl_def_id: DefId, |
47 | args: &'tcx [GenericArg<'tcx>], |
48 | ) -> Result<(), PrintError> { |
49 | let tcx = self.tcx(); |
50 | let self_ty = tcx.type_of(impl_def_id); |
51 | let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); |
52 | let (self_ty, impl_trait_ref) = if tcx.generics_of(impl_def_id).count() <= args.len() { |
53 | ( |
54 | self_ty.instantiate(tcx, args), |
55 | impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(tcx, args)), |
56 | ) |
57 | } else { |
58 | // We are probably printing a nested item inside of an impl. |
59 | // Use the identity substitutions for the impl. |
60 | ( |
61 | self_ty.instantiate_identity(), |
62 | impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()), |
63 | ) |
64 | }; |
65 | |
66 | self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref) |
67 | } |
68 | |
69 | fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>; |
70 | |
71 | fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>; |
72 | |
73 | fn print_dyn_existential( |
74 | &mut self, |
75 | predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
76 | ) -> Result<(), PrintError>; |
77 | |
78 | fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>; |
79 | |
80 | fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>; |
81 | |
82 | fn path_qualified( |
83 | &mut self, |
84 | self_ty: Ty<'tcx>, |
85 | trait_ref: Option<ty::TraitRef<'tcx>>, |
86 | ) -> Result<(), PrintError>; |
87 | |
88 | fn path_append_impl( |
89 | &mut self, |
90 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
91 | disambiguated_data: &DisambiguatedDefPathData, |
92 | self_ty: Ty<'tcx>, |
93 | trait_ref: Option<ty::TraitRef<'tcx>>, |
94 | ) -> Result<(), PrintError>; |
95 | |
96 | fn path_append( |
97 | &mut self, |
98 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
99 | disambiguated_data: &DisambiguatedDefPathData, |
100 | ) -> Result<(), PrintError>; |
101 | |
102 | fn path_generic_args( |
103 | &mut self, |
104 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
105 | args: &[GenericArg<'tcx>], |
106 | ) -> Result<(), PrintError>; |
107 | |
108 | fn should_truncate(&mut self) -> bool { |
109 | false |
110 | } |
111 | |
112 | // Defaults (should not be overridden): |
113 | |
114 | #[instrument(skip(self), level = "debug")] |
115 | fn default_print_def_path( |
116 | &mut self, |
117 | def_id: DefId, |
118 | args: &'tcx [GenericArg<'tcx>], |
119 | ) -> Result<(), PrintError> { |
120 | let key = self.tcx().def_key(def_id); |
121 | debug!(?key); |
122 | |
123 | match key.disambiguated_data.data { |
124 | DefPathData::CrateRoot => { |
125 | assert!(key.parent.is_none()); |
126 | self.path_crate(def_id.krate) |
127 | } |
128 | |
129 | DefPathData::Impl => self.print_impl_path(def_id, args), |
130 | |
131 | _ => { |
132 | let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id }; |
133 | |
134 | let mut parent_args = args; |
135 | let mut trait_qualify_parent = false; |
136 | if !args.is_empty() { |
137 | let generics = self.tcx().generics_of(def_id); |
138 | parent_args = &args[..generics.parent_count.min(args.len())]; |
139 | |
140 | match key.disambiguated_data.data { |
141 | DefPathData::Closure => { |
142 | // FIXME(async_closures): This is somewhat ugly. |
143 | // We need to additionally print the `kind` field of a closure if |
144 | // it is desugared from a coroutine-closure. |
145 | if let Some(hir::CoroutineKind::Desugared( |
146 | _, |
147 | hir::CoroutineSource::Closure, |
148 | )) = self.tcx().coroutine_kind(def_id) |
149 | && args.len() > parent_args.len() |
150 | { |
151 | return self.path_generic_args( |
152 | |cx| cx.print_def_path(def_id, parent_args), |
153 | &args[..parent_args.len() + 1][..1], |
154 | ); |
155 | } else { |
156 | // Closures' own generics are only captures, don't print them. |
157 | } |
158 | } |
159 | // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`. |
160 | // Anon consts doesn't have their own generics, and inline consts' own |
161 | // generics are their inferred types, so don't print them. |
162 | DefPathData::AnonConst => {} |
163 | |
164 | // If we have any generic arguments to print, we do that |
165 | // on top of the same path, but without its own generics. |
166 | _ => { |
167 | if !generics.is_own_empty() && args.len() >= generics.count() { |
168 | let args = generics.own_args_no_defaults(self.tcx(), args); |
169 | return self.path_generic_args( |
170 | |cx| cx.print_def_path(def_id, parent_args), |
171 | args, |
172 | ); |
173 | } |
174 | } |
175 | } |
176 | |
177 | // FIXME(eddyb) try to move this into the parent's printing |
178 | // logic, instead of doing it when printing the child. |
179 | trait_qualify_parent = generics.has_self |
180 | && generics.parent == Some(parent_def_id) |
181 | && parent_args.len() == generics.parent_count |
182 | && self.tcx().generics_of(parent_def_id).parent_count == 0; |
183 | } |
184 | |
185 | self.path_append( |
186 | |cx: &mut Self| { |
187 | if trait_qualify_parent { |
188 | let trait_ref = ty::TraitRef::new( |
189 | cx.tcx(), |
190 | parent_def_id, |
191 | parent_args.iter().copied(), |
192 | ); |
193 | cx.path_qualified(trait_ref.self_ty(), Some(trait_ref)) |
194 | } else { |
195 | cx.print_def_path(parent_def_id, parent_args) |
196 | } |
197 | }, |
198 | &key.disambiguated_data, |
199 | ) |
200 | } |
201 | } |
202 | } |
203 | |
204 | fn default_print_impl_path( |
205 | &mut self, |
206 | impl_def_id: DefId, |
207 | self_ty: Ty<'tcx>, |
208 | impl_trait_ref: Option<ty::TraitRef<'tcx>>, |
209 | ) -> Result<(), PrintError> { |
210 | debug!( |
211 | "default_print_impl_path: impl_def_id={:?} , self_ty={} , impl_trait_ref={:?} ", |
212 | impl_def_id, self_ty, impl_trait_ref |
213 | ); |
214 | |
215 | let key = self.tcx().def_key(impl_def_id); |
216 | let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id }; |
217 | |
218 | // Decide whether to print the parent path for the impl. |
219 | // Logically, since impls are global, it's never needed, but |
220 | // users may find it useful. Currently, we omit the parent if |
221 | // the impl is either in the same module as the self-type or |
222 | // as the trait. |
223 | let in_self_mod = match characteristic_def_id_of_type(self_ty) { |
224 | None => false, |
225 | Some(ty_def_id) => self.tcx().parent(ty_def_id) == parent_def_id, |
226 | }; |
227 | let in_trait_mod = match impl_trait_ref { |
228 | None => false, |
229 | Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == parent_def_id, |
230 | }; |
231 | |
232 | if !in_self_mod && !in_trait_mod { |
233 | // If the impl is not co-located with either self-type or |
234 | // trait-type, then fallback to a format that identifies |
235 | // the module more clearly. |
236 | self.path_append_impl( |
237 | |cx| cx.print_def_path(parent_def_id, &[]), |
238 | &key.disambiguated_data, |
239 | self_ty, |
240 | impl_trait_ref, |
241 | ) |
242 | } else { |
243 | // Otherwise, try to give a good form that would be valid language |
244 | // syntax. Preferably using associated item notation. |
245 | self.path_qualified(self_ty, impl_trait_ref) |
246 | } |
247 | } |
248 | } |
249 | |
250 | /// As a heuristic, when we see an impl, if we see that the |
251 | /// 'self type' is a type defined in the same module as the impl, |
252 | /// we can omit including the path to the impl itself. This |
253 | /// function tries to find a "characteristic `DefId`" for a |
254 | /// type. It's just a heuristic so it makes some questionable |
255 | /// decisions and we may want to adjust it later. |
256 | /// |
257 | /// Visited set is needed to avoid full iteration over |
258 | /// deeply nested tuples that have no DefId. |
259 | fn characteristic_def_id_of_type_cached<'a>( |
260 | ty: Ty<'a>, |
261 | visited: &mut SsoHashSet<Ty<'a>>, |
262 | ) -> Option<DefId> { |
263 | match *ty.kind() { |
264 | ty::Adt(adt_def, _) => Some(adt_def.did()), |
265 | |
266 | ty::Dynamic(data, ..) => data.principal_def_id(), |
267 | |
268 | ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => { |
269 | characteristic_def_id_of_type_cached(subty, visited) |
270 | } |
271 | |
272 | ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited), |
273 | |
274 | ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited), |
275 | |
276 | ty::Tuple(tys) => tys.iter().find_map(|ty| { |
277 | if visited.insert(ty) { |
278 | return characteristic_def_id_of_type_cached(ty, visited); |
279 | } |
280 | return None; |
281 | }), |
282 | |
283 | ty::FnDef(def_id, _) |
284 | | ty::Closure(def_id, _) |
285 | | ty::CoroutineClosure(def_id, _) |
286 | | ty::Coroutine(def_id, _) |
287 | | ty::CoroutineWitness(def_id, _) |
288 | | ty::Foreign(def_id) => Some(def_id), |
289 | |
290 | ty::Bool |
291 | | ty::Char |
292 | | ty::Int(_) |
293 | | ty::Uint(_) |
294 | | ty::Str |
295 | | ty::FnPtr(..) |
296 | | ty::UnsafeBinder(_) |
297 | | ty::Alias(..) |
298 | | ty::Placeholder(..) |
299 | | ty::Param(_) |
300 | | ty::Infer(_) |
301 | | ty::Bound(..) |
302 | | ty::Error(_) |
303 | | ty::Never |
304 | | ty::Float(_) => None, |
305 | } |
306 | } |
307 | pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> { |
308 | characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new()) |
309 | } |
310 | |
311 | impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> { |
312 | fn print(&self, cx: &mut P) -> Result<(), PrintError> { |
313 | cx.print_region(*self) |
314 | } |
315 | } |
316 | |
317 | impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> { |
318 | fn print(&self, cx: &mut P) -> Result<(), PrintError> { |
319 | cx.print_type(*self) |
320 | } |
321 | } |
322 | |
323 | impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> { |
324 | fn print(&self, cx: &mut P) -> Result<(), PrintError> { |
325 | cx.print_dyn_existential(self) |
326 | } |
327 | } |
328 | |
329 | impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> { |
330 | fn print(&self, cx: &mut P) -> Result<(), PrintError> { |
331 | cx.print_const(*self) |
332 | } |
333 | } |
334 | |
335 | // This is only used by query descriptions |
336 | pub fn describe_as_module(def_id: impl Into<LocalDefId>, tcx: TyCtxt<'_>) -> String { |
337 | let def_id = def_id.into(); |
338 | if def_id.is_top_level_module() { |
339 | "top-level module".to_string() |
340 | } else { |
341 | format!("module `{} `", tcx.def_path_str(def_id)) |
342 | } |
343 | } |
344 | |
345 | impl<T> rustc_type_ir::ir_print::IrPrint<T> for TyCtxt<'_> |
346 | where |
347 | T: Copy + for<'a, 'tcx> dynLift<TyCtxt<'tcx>, Lifted: Print<'tcx, FmtPrinter<'a, 'tcx>>>, |
348 | { |
349 | fn print(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
350 | ty::tls::with(|tcx: TyCtxt<'_>| { |
351 | let mut cx: FmtPrinter<'_, '_> = FmtPrinter::new(tcx, ns:Namespace::TypeNS); |
352 | tcx.lift(*t).expect(msg:"could not lift for printing").print(&mut cx)?; |
353 | fmt.write_str(&cx.into_buffer())?; |
354 | Ok(()) |
355 | }) |
356 | } |
357 | |
358 | fn print_debug(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
359 | with_no_trimmed_paths!(Self::print(t, fmt)) |
360 | } |
361 | } |
362 | |
363 | /// Format instance name that is already known to be too long for rustc. |
364 | /// Show only the first 2 types if it is longer than 32 characters to avoid blasting |
365 | /// the user's terminal with thousands of lines of type-name. |
366 | /// |
367 | /// If the type name is longer than before+after, it will be written to a file. |
368 | pub fn shrunk_instance_name<'tcx>( |
369 | tcx: TyCtxt<'tcx>, |
370 | instance: ty::Instance<'tcx>, |
371 | ) -> (String, Option<PathBuf>) { |
372 | let s: String = instance.to_string(); |
373 | |
374 | // Only use the shrunk version if it's really shorter. |
375 | // This also avoids the case where before and after slices overlap. |
376 | if s.chars().nth(33).is_some() { |
377 | let shrunk: String = format!("{} ", ShortInstance(instance, 4)); |
378 | if shrunk == s { |
379 | return (s, None); |
380 | } |
381 | |
382 | let path: PathBuf = tcx.output_filenames(()).temp_path_ext("long-type.txt", None); |
383 | let written_to_path: Option |
384 | |
385 | (shrunk, written_to_path) |
386 | } else { |
387 | (s, None) |
388 | } |
389 | } |
390 |
Definitions
- PrintError
- Printer
- tcx
- print_def_path
- print_impl_path
- print_region
- print_type
- print_dyn_existential
- print_const
- path_crate
- path_qualified
- path_append_impl
- path_append
- path_generic_args
- should_truncate
- default_print_def_path
- default_print_impl_path
- characteristic_def_id_of_type_cached
- characteristic_def_id_of_type
- describe_as_module
- print_debug
Learn Rust with the experts
Find out more