rustc_middle/ty/print/
mod.rs

1use std::path::PathBuf;
2
3use hir::def::Namespace;
4use rustc_data_structures::fx::FxHashSet;
5use rustc_data_structures::sso::SsoHashSet;
6use rustc_hir as hir;
7use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
8use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
9use tracing::{debug, instrument, trace};
10
11use crate::ty::{self, GenericArg, ShortInstance, Ty, TyCtxt};
12
13// `pretty` is a separate module only for organization.
14mod pretty;
15pub use self::pretty::*;
16use super::Lift;
17
18pub type PrintError = std::fmt::Error;
19
20pub 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".
33pub 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                            // We need to additionally print the `kind` field of a coroutine if
143                            // it is desugared from a coroutine-closure.
144                            if let Some(hir::CoroutineKind::Desugared(
145                                _,
146                                hir::CoroutineSource::Closure,
147                            )) = self.tcx().coroutine_kind(def_id)
148                                && args.len() > parent_args.len()
149                            {
150                                return self.path_generic_args(
151                                    |cx| cx.print_def_path(def_id, parent_args),
152                                    &args[..parent_args.len() + 1][..1],
153                                );
154                            } else {
155                                // Closures' own generics are only captures, don't print them.
156                            }
157                        }
158                        DefPathData::SyntheticCoroutineBody => {
159                            // Synthetic coroutine bodies have no distinct generics, since like
160                            // closures they're all just internal state of the coroutine.
161                        }
162                        // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
163                        // Anon consts doesn't have their own generics, and inline consts' own
164                        // generics are their inferred types, so don't print them.
165                        DefPathData::AnonConst => {}
166
167                        // If we have any generic arguments to print, we do that
168                        // on top of the same path, but without its own generics.
169                        _ => {
170                            if !generics.is_own_empty() && args.len() >= generics.count() {
171                                let args = generics.own_args_no_defaults(self.tcx(), args);
172                                return self.path_generic_args(
173                                    |cx| cx.print_def_path(def_id, parent_args),
174                                    args,
175                                );
176                            }
177                        }
178                    }
179
180                    // FIXME(eddyb) try to move this into the parent's printing
181                    // logic, instead of doing it when printing the child.
182                    trait_qualify_parent = generics.has_self
183                        && generics.parent == Some(parent_def_id)
184                        && parent_args.len() == generics.parent_count
185                        && self.tcx().generics_of(parent_def_id).parent_count == 0;
186                }
187
188                self.path_append(
189                    |cx: &mut Self| {
190                        if trait_qualify_parent {
191                            let trait_ref = ty::TraitRef::new(
192                                cx.tcx(),
193                                parent_def_id,
194                                parent_args.iter().copied(),
195                            );
196                            cx.path_qualified(trait_ref.self_ty(), Some(trait_ref))
197                        } else {
198                            cx.print_def_path(parent_def_id, parent_args)
199                        }
200                    },
201                    &key.disambiguated_data,
202                )
203            }
204        }
205    }
206
207    fn default_print_impl_path(
208        &mut self,
209        impl_def_id: DefId,
210        self_ty: Ty<'tcx>,
211        impl_trait_ref: Option<ty::TraitRef<'tcx>>,
212    ) -> Result<(), PrintError> {
213        debug!(
214            "default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}",
215            impl_def_id, self_ty, impl_trait_ref
216        );
217
218        let key = self.tcx().def_key(impl_def_id);
219        let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id };
220
221        // Decide whether to print the parent path for the impl.
222        // Logically, since impls are global, it's never needed, but
223        // users may find it useful. Currently, we omit the parent if
224        // the impl is either in the same module as the self-type or
225        // as the trait.
226        let in_self_mod = match characteristic_def_id_of_type(self_ty) {
227            None => false,
228            Some(ty_def_id) => self.tcx().parent(ty_def_id) == parent_def_id,
229        };
230        let in_trait_mod = match impl_trait_ref {
231            None => false,
232            Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == parent_def_id,
233        };
234
235        if !in_self_mod && !in_trait_mod {
236            // If the impl is not co-located with either self-type or
237            // trait-type, then fallback to a format that identifies
238            // the module more clearly.
239            self.path_append_impl(
240                |cx| cx.print_def_path(parent_def_id, &[]),
241                &key.disambiguated_data,
242                self_ty,
243                impl_trait_ref,
244            )
245        } else {
246            // Otherwise, try to give a good form that would be valid language
247            // syntax. Preferably using associated item notation.
248            self.path_qualified(self_ty, impl_trait_ref)
249        }
250    }
251}
252
253/// As a heuristic, when we see an impl, if we see that the
254/// 'self type' is a type defined in the same module as the impl,
255/// we can omit including the path to the impl itself. This
256/// function tries to find a "characteristic `DefId`" for a
257/// type. It's just a heuristic so it makes some questionable
258/// decisions and we may want to adjust it later.
259///
260/// Visited set is needed to avoid full iteration over
261/// deeply nested tuples that have no DefId.
262fn characteristic_def_id_of_type_cached<'a>(
263    ty: Ty<'a>,
264    visited: &mut SsoHashSet<Ty<'a>>,
265) -> Option<DefId> {
266    match *ty.kind() {
267        ty::Adt(adt_def, _) => Some(adt_def.did()),
268
269        ty::Dynamic(data, ..) => data.principal_def_id(),
270
271        ty::Pat(subty, _) | ty::Array(subty, _) | ty::Slice(subty) => {
272            characteristic_def_id_of_type_cached(subty, visited)
273        }
274
275        ty::RawPtr(ty, _) => characteristic_def_id_of_type_cached(ty, visited),
276
277        ty::Ref(_, ty, _) => characteristic_def_id_of_type_cached(ty, visited),
278
279        ty::Tuple(tys) => tys.iter().find_map(|ty| {
280            if visited.insert(ty) {
281                return characteristic_def_id_of_type_cached(ty, visited);
282            }
283            return None;
284        }),
285
286        ty::FnDef(def_id, _)
287        | ty::Closure(def_id, _)
288        | ty::CoroutineClosure(def_id, _)
289        | ty::Coroutine(def_id, _)
290        | ty::CoroutineWitness(def_id, _)
291        | ty::Foreign(def_id) => Some(def_id),
292
293        ty::Bool
294        | ty::Char
295        | ty::Int(_)
296        | ty::Uint(_)
297        | ty::Str
298        | ty::FnPtr(..)
299        | ty::UnsafeBinder(_)
300        | ty::Alias(..)
301        | ty::Placeholder(..)
302        | ty::Param(_)
303        | ty::Infer(_)
304        | ty::Bound(..)
305        | ty::Error(_)
306        | ty::Never
307        | ty::Float(_) => None,
308    }
309}
310pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
311    characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new())
312}
313
314impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> {
315    fn print(&self, cx: &mut P) -> Result<(), PrintError> {
316        cx.print_region(*self)
317    }
318}
319
320impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> {
321    fn print(&self, cx: &mut P) -> Result<(), PrintError> {
322        cx.print_type(*self)
323    }
324}
325
326impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
327    fn print(&self, cx: &mut P) -> Result<(), PrintError> {
328        cx.print_dyn_existential(self)
329    }
330}
331
332impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> {
333    fn print(&self, cx: &mut P) -> Result<(), PrintError> {
334        cx.print_const(*self)
335    }
336}
337
338// This is only used by query descriptions
339pub fn describe_as_module(def_id: impl Into<LocalDefId>, tcx: TyCtxt<'_>) -> String {
340    let def_id = def_id.into();
341    if def_id.is_top_level_module() {
342        "top-level module".to_string()
343    } else {
344        format!("module `{}`", tcx.def_path_str(def_id))
345    }
346}
347
348impl<T> rustc_type_ir::ir_print::IrPrint<T> for TyCtxt<'_>
349where
350    T: Copy + for<'a, 'tcx> Lift<TyCtxt<'tcx>, Lifted: Print<'tcx, FmtPrinter<'a, 'tcx>>>,
351{
352    fn print(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353        ty::tls::with(|tcx| {
354            let mut cx = FmtPrinter::new(tcx, Namespace::TypeNS);
355            tcx.lift(*t).expect("could not lift for printing").print(&mut cx)?;
356            fmt.write_str(&cx.into_buffer())?;
357            Ok(())
358        })
359    }
360
361    fn print_debug(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362        with_no_trimmed_paths!(Self::print(t, fmt))
363    }
364}
365
366/// Format instance name that is already known to be too long for rustc.
367/// Show only the first 2 types if it is longer than 32 characters to avoid blasting
368/// the user's terminal with thousands of lines of type-name.
369///
370/// If the type name is longer than before+after, it will be written to a file.
371pub fn shrunk_instance_name<'tcx>(
372    tcx: TyCtxt<'tcx>,
373    instance: ty::Instance<'tcx>,
374) -> (String, Option<PathBuf>) {
375    let s = instance.to_string();
376
377    // Only use the shrunk version if it's really shorter.
378    // This also avoids the case where before and after slices overlap.
379    if s.chars().nth(33).is_some() {
380        let shrunk = format!("{}", ShortInstance(instance, 4));
381        if shrunk == s {
382            return (s, None);
383        }
384
385        let path = tcx.output_filenames(()).temp_path_for_diagnostic("long-type.txt");
386        let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
387
388        (shrunk, written_to_path)
389    } else {
390        (s, None)
391    }
392}