]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_driver/src/pretty.rs
Group some commonly passed together values into a struct
[rust.git] / compiler / rustc_driver / src / pretty.rs
1 //! The various pretty-printing routines.
2
3 use crate::session_diagnostics::UnprettyDumpFail;
4 use rustc_ast as ast;
5 use rustc_ast_pretty::pprust;
6 use rustc_errors::ErrorGuaranteed;
7 use rustc_hir as hir;
8 use rustc_hir_pretty as pprust_hir;
9 use rustc_interface::interface::CompilerIO;
10 use rustc_middle::hir::map as hir_map;
11 use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
12 use rustc_middle::ty::{self, TyCtxt};
13 use rustc_session::config::{Input, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
14 use rustc_session::Session;
15 use rustc_span::symbol::Ident;
16 use rustc_span::FileName;
17
18 use std::cell::Cell;
19 use std::fmt::Write;
20 use std::path::PathBuf;
21
22 pub use self::PpMode::*;
23 pub use self::PpSourceMode::*;
24 use crate::abort_on_err;
25
26 // This slightly awkward construction is to allow for each PpMode to
27 // choose whether it needs to do analyses (which can consume the
28 // Session) and then pass through the session (now attached to the
29 // analysis results) on to the chosen pretty-printer, along with the
30 // `&PpAnn` object.
31 //
32 // Note that since the `&PrinterSupport` is freshly constructed on each
33 // call, it would not make sense to try to attach the lifetime of `self`
34 // to the lifetime of the `&PrinterObject`.
35
36 /// Constructs a `PrinterSupport` object and passes it to `f`.
37 fn call_with_pp_support<'tcx, A, F>(
38     ppmode: &PpSourceMode,
39     sess: &'tcx Session,
40     tcx: Option<TyCtxt<'tcx>>,
41     f: F,
42 ) -> A
43 where
44     F: FnOnce(&dyn PrinterSupport) -> A,
45 {
46     match *ppmode {
47         Normal | Expanded => {
48             let annotation = NoAnn { sess, tcx };
49             f(&annotation)
50         }
51
52         Identified | ExpandedIdentified => {
53             let annotation = IdentifiedAnnotation { sess, tcx };
54             f(&annotation)
55         }
56         ExpandedHygiene => {
57             let annotation = HygieneAnnotation { sess };
58             f(&annotation)
59         }
60     }
61 }
62 fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
63 where
64     F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A,
65 {
66     match *ppmode {
67         PpHirMode::Normal => {
68             let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
69             f(&annotation, tcx.hir())
70         }
71
72         PpHirMode::Identified => {
73             let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
74             f(&annotation, tcx.hir())
75         }
76         PpHirMode::Typed => {
77             abort_on_err(tcx.analysis(()), tcx.sess);
78
79             let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
80             tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir()))
81         }
82     }
83 }
84
85 trait PrinterSupport: pprust::PpAnn {
86     /// Provides a uniform interface for re-extracting a reference to a
87     /// `Session` from a value that now owns it.
88     fn sess(&self) -> &Session;
89
90     /// Produces the pretty-print annotation object.
91     ///
92     /// (Rust does not yet support upcasting from a trait object to
93     /// an object for one of its supertraits.)
94     fn pp_ann(&self) -> &dyn pprust::PpAnn;
95 }
96
97 trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
98     /// Provides a uniform interface for re-extracting a reference to a
99     /// `Session` from a value that now owns it.
100     fn sess(&self) -> &Session;
101
102     /// Provides a uniform interface for re-extracting a reference to an
103     /// `hir_map::Map` from a value that now owns it.
104     fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
105
106     /// Produces the pretty-print annotation object.
107     ///
108     /// (Rust does not yet support upcasting from a trait object to
109     /// an object for one of its supertraits.)
110     fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
111 }
112
113 struct NoAnn<'hir> {
114     sess: &'hir Session,
115     tcx: Option<TyCtxt<'hir>>,
116 }
117
118 impl<'hir> PrinterSupport for NoAnn<'hir> {
119     fn sess(&self) -> &Session {
120         self.sess
121     }
122
123     fn pp_ann(&self) -> &dyn pprust::PpAnn {
124         self
125     }
126 }
127
128 impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
129     fn sess(&self) -> &Session {
130         self.sess
131     }
132
133     fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
134         self.tcx.map(|tcx| tcx.hir())
135     }
136
137     fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
138         self
139     }
140 }
141
142 impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
143 impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
144     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
145         if let Some(tcx) = self.tcx {
146             pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
147         }
148     }
149 }
150
151 struct IdentifiedAnnotation<'hir> {
152     sess: &'hir Session,
153     tcx: Option<TyCtxt<'hir>>,
154 }
155
156 impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
157     fn sess(&self) -> &Session {
158         self.sess
159     }
160
161     fn pp_ann(&self) -> &dyn pprust::PpAnn {
162         self
163     }
164 }
165
166 impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
167     fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
168         if let pprust::AnnNode::Expr(_) = node {
169             s.popen();
170         }
171     }
172     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
173         match node {
174             pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
175
176             pprust::AnnNode::Item(item) => {
177                 s.s.space();
178                 s.synth_comment(item.id.to_string())
179             }
180             pprust::AnnNode::SubItem(id) => {
181                 s.s.space();
182                 s.synth_comment(id.to_string())
183             }
184             pprust::AnnNode::Block(blk) => {
185                 s.s.space();
186                 s.synth_comment(format!("block {}", blk.id))
187             }
188             pprust::AnnNode::Expr(expr) => {
189                 s.s.space();
190                 s.synth_comment(expr.id.to_string());
191                 s.pclose()
192             }
193             pprust::AnnNode::Pat(pat) => {
194                 s.s.space();
195                 s.synth_comment(format!("pat {}", pat.id));
196             }
197         }
198     }
199 }
200
201 impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
202     fn sess(&self) -> &Session {
203         self.sess
204     }
205
206     fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
207         self.tcx.map(|tcx| tcx.hir())
208     }
209
210     fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
211         self
212     }
213 }
214
215 impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
216     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
217         if let Some(ref tcx) = self.tcx {
218             pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
219         }
220     }
221     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
222         if let pprust_hir::AnnNode::Expr(_) = node {
223             s.popen();
224         }
225     }
226     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
227         match node {
228             pprust_hir::AnnNode::Name(_) => {}
229             pprust_hir::AnnNode::Item(item) => {
230                 s.s.space();
231                 s.synth_comment(format!("hir_id: {}", item.hir_id()));
232             }
233             pprust_hir::AnnNode::SubItem(id) => {
234                 s.s.space();
235                 s.synth_comment(id.to_string());
236             }
237             pprust_hir::AnnNode::Block(blk) => {
238                 s.s.space();
239                 s.synth_comment(format!("block hir_id: {}", blk.hir_id));
240             }
241             pprust_hir::AnnNode::Expr(expr) => {
242                 s.s.space();
243                 s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
244                 s.pclose();
245             }
246             pprust_hir::AnnNode::Pat(pat) => {
247                 s.s.space();
248                 s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
249             }
250             pprust_hir::AnnNode::Arm(arm) => {
251                 s.s.space();
252                 s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
253             }
254         }
255     }
256 }
257
258 struct HygieneAnnotation<'a> {
259     sess: &'a Session,
260 }
261
262 impl<'a> PrinterSupport for HygieneAnnotation<'a> {
263     fn sess(&self) -> &Session {
264         self.sess
265     }
266
267     fn pp_ann(&self) -> &dyn pprust::PpAnn {
268         self
269     }
270 }
271
272 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
273     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
274         match node {
275             pprust::AnnNode::Ident(&Ident { name, span }) => {
276                 s.s.space();
277                 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
278             }
279             pprust::AnnNode::Name(&name) => {
280                 s.s.space();
281                 s.synth_comment(name.as_u32().to_string())
282             }
283             pprust::AnnNode::Crate(_) => {
284                 s.s.hardbreak();
285                 let verbose = self.sess.verbose();
286                 s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
287                 s.s.hardbreak_if_not_bol();
288             }
289             _ => {}
290         }
291     }
292 }
293
294 struct TypedAnnotation<'tcx> {
295     tcx: TyCtxt<'tcx>,
296     maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
297 }
298
299 impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
300     fn sess(&self) -> &Session {
301         self.tcx.sess
302     }
303
304     fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
305         Some(self.tcx.hir())
306     }
307
308     fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
309         self
310     }
311 }
312
313 impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
314     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
315         let old_maybe_typeck_results = self.maybe_typeck_results.get();
316         if let pprust_hir::Nested::Body(id) = nested {
317             self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id)));
318         }
319         let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
320         pprust_hir::PpAnn::nested(pp_ann, state, nested);
321         self.maybe_typeck_results.set(old_maybe_typeck_results);
322     }
323     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
324         if let pprust_hir::AnnNode::Expr(_) = node {
325             s.popen();
326         }
327     }
328     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
329         if let pprust_hir::AnnNode::Expr(expr) = node {
330             let typeck_results = self.maybe_typeck_results.get().or_else(|| {
331                 self.tcx
332                     .hir()
333                     .maybe_body_owned_by(expr.hir_id.owner.def_id)
334                     .map(|body_id| self.tcx.typeck_body(body_id))
335             });
336
337             if let Some(typeck_results) = typeck_results {
338                 s.s.space();
339                 s.s.word("as");
340                 s.s.space();
341                 s.s.word(typeck_results.expr_ty(expr).to_string());
342             }
343
344             s.pclose();
345         }
346     }
347 }
348
349 fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
350     let src_name = input.source_name();
351     let src = String::clone(
352         sess.source_map()
353             .get_source_file(&src_name)
354             .expect("get_source_file")
355             .src
356             .as_ref()
357             .expect("src"),
358     );
359     (src, src_name)
360 }
361
362 fn write_or_print(out: &str, ofile: &Option<PathBuf>, sess: &Session) {
363     match ofile {
364         None => print!("{out}"),
365         Some(p) => {
366             if let Err(e) = std::fs::write(p, out) {
367                 sess.emit_fatal(UnprettyDumpFail {
368                     path: p.display().to_string(),
369                     err: e.to_string(),
370                 });
371             }
372         }
373     }
374 }
375
376 pub fn print_after_parsing(sess: &Session, io: &CompilerIO, krate: &ast::Crate, ppm: PpMode) {
377     let (src, src_name) = get_source(&io.input, sess);
378
379     let out = match ppm {
380         Source(s) => {
381             // Silently ignores an identified node.
382             call_with_pp_support(&s, sess, None, move |annotation| {
383                 debug!("pretty printing source code {:?}", s);
384                 let sess = annotation.sess();
385                 let parse = &sess.parse_sess;
386                 pprust::print_crate(
387                     sess.source_map(),
388                     krate,
389                     src_name,
390                     src,
391                     annotation.pp_ann(),
392                     false,
393                     parse.edition,
394                     &sess.parse_sess.attr_id_generator,
395                 )
396             })
397         }
398         AstTree(PpAstTreeMode::Normal) => {
399             debug!("pretty printing AST tree");
400             format!("{krate:#?}")
401         }
402         _ => unreachable!(),
403     };
404
405     write_or_print(&out, &io.output_file, sess);
406 }
407
408 pub fn print_after_hir_lowering<'tcx>(
409     tcx: TyCtxt<'tcx>,
410     io: &CompilerIO,
411     krate: &ast::Crate,
412     ppm: PpMode,
413 ) {
414     if ppm.needs_analysis() {
415         abort_on_err(print_with_analysis(tcx, ppm, &io.output_file), tcx.sess);
416         return;
417     }
418
419     let (src, src_name) = get_source(&io.input, tcx.sess);
420
421     let out = match ppm {
422         Source(s) => {
423             // Silently ignores an identified node.
424             call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
425                 debug!("pretty printing source code {:?}", s);
426                 let sess = annotation.sess();
427                 let parse = &sess.parse_sess;
428                 pprust::print_crate(
429                     sess.source_map(),
430                     krate,
431                     src_name,
432                     src,
433                     annotation.pp_ann(),
434                     true,
435                     parse.edition,
436                     &sess.parse_sess.attr_id_generator,
437                 )
438             })
439         }
440
441         AstTree(PpAstTreeMode::Expanded) => {
442             debug!("pretty-printing expanded AST");
443             format!("{krate:#?}")
444         }
445
446         Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
447             debug!("pretty printing HIR {:?}", s);
448             let sess = annotation.sess();
449             let sm = sess.source_map();
450             let attrs = |id| hir_map.attrs(id);
451             pprust_hir::print_crate(
452                 sm,
453                 hir_map.root_module(),
454                 src_name,
455                 src,
456                 &attrs,
457                 annotation.pp_ann(),
458             )
459         }),
460
461         HirTree => {
462             call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| {
463                 debug!("pretty printing HIR tree");
464                 format!("{:#?}", hir_map.krate())
465             })
466         }
467
468         _ => unreachable!(),
469     };
470
471     write_or_print(&out, &io.output_file, tcx.sess);
472 }
473
474 // In an ideal world, this would be a public function called by the driver after
475 // analysis is performed. However, we want to call `phase_3_run_analysis_passes`
476 // with a different callback than the standard driver, so that isn't easy.
477 // Instead, we call that function ourselves.
478 fn print_with_analysis(
479     tcx: TyCtxt<'_>,
480     ppm: PpMode,
481     ofile: &Option<PathBuf>,
482 ) -> Result<(), ErrorGuaranteed> {
483     tcx.analysis(())?;
484     let out = match ppm {
485         Mir => {
486             let mut out = Vec::new();
487             write_mir_pretty(tcx, None, &mut out).unwrap();
488             String::from_utf8(out).unwrap()
489         }
490
491         MirCFG => {
492             let mut out = Vec::new();
493             write_mir_graphviz(tcx, None, &mut out).unwrap();
494             String::from_utf8(out).unwrap()
495         }
496
497         ThirTree => {
498             let mut out = String::new();
499             abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
500             debug!("pretty printing THIR tree");
501             for did in tcx.hir().body_owners() {
502                 let _ = writeln!(
503                     out,
504                     "{:?}:\n{}\n",
505                     did,
506                     tcx.thir_tree(ty::WithOptConstParam::unknown(did))
507                 );
508             }
509             out
510         }
511
512         _ => unreachable!(),
513     };
514
515     write_or_print(&out, ofile, tcx.sess);
516
517     Ok(())
518 }