]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/pretty.rs
Auto merge of #66716 - derekdreery:debug_non_exhaustive, r=dtolnay
[rust.git] / src / librustc_driver / pretty.rs
1 //! The various pretty-printing routines.
2
3 use rustc::hir::map as hir_map;
4 use rustc::session::config::{Input, PpMode, PpSourceMode};
5 use rustc::session::Session;
6 use rustc::ty::{self, TyCtxt};
7 use rustc::util::common::ErrorReported;
8 use rustc_hir as hir;
9 use rustc_hir::def_id::LOCAL_CRATE;
10 use rustc_hir::print as pprust_hir;
11 use rustc_mir::util::{write_mir_graphviz, write_mir_pretty};
12
13 use rustc_span::FileName;
14 use syntax::ast;
15 use syntax::print::pprust;
16
17 use std::cell::Cell;
18 use std::fs::File;
19 use std::io::Write;
20 use std::path::Path;
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 // (The `use_once_payload` is working around the current lack of once
37 // functions in the compiler.)
38
39 /// Constructs a `PrinterSupport` object and passes it to `f`.
40 fn call_with_pp_support<'tcx, A, F>(
41     ppmode: &PpSourceMode,
42     sess: &'tcx Session,
43     tcx: Option<TyCtxt<'tcx>>,
44     f: F,
45 ) -> A
46 where
47     F: FnOnce(&dyn PrinterSupport) -> A,
48 {
49     match *ppmode {
50         PpmNormal | PpmEveryBodyLoops | PpmExpanded => {
51             let annotation = NoAnn { sess, tcx };
52             f(&annotation)
53         }
54
55         PpmIdentified | PpmExpandedIdentified => {
56             let annotation = IdentifiedAnnotation { sess, tcx };
57             f(&annotation)
58         }
59         PpmExpandedHygiene => {
60             let annotation = HygieneAnnotation { sess };
61             f(&annotation)
62         }
63         _ => panic!("Should use call_with_pp_support_hir"),
64     }
65 }
66 fn call_with_pp_support_hir<A, F>(ppmode: &PpSourceMode, tcx: TyCtxt<'_>, f: F) -> A
67 where
68     F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate<'_>) -> A,
69 {
70     match *ppmode {
71         PpmNormal => {
72             let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
73             f(&annotation, tcx.hir().forest.krate())
74         }
75
76         PpmIdentified => {
77             let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
78             f(&annotation, tcx.hir().forest.krate())
79         }
80         PpmTyped => {
81             abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess);
82
83             let empty_tables = ty::TypeckTables::empty(None);
84             let annotation = TypedAnnotation { tcx, tables: Cell::new(&empty_tables) };
85             tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir().forest.krate()))
86         }
87         _ => panic!("Should use call_with_pp_support"),
88     }
89 }
90
91 trait PrinterSupport: pprust::PpAnn {
92     /// Provides a uniform interface for re-extracting a reference to a
93     /// `Session` from a value that now owns it.
94     fn sess(&self) -> &Session;
95
96     /// Produces the pretty-print annotation object.
97     ///
98     /// (Rust does not yet support upcasting from a trait object to
99     /// an object for one of its super-traits.)
100     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn;
101 }
102
103 trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
104     /// Provides a uniform interface for re-extracting a reference to a
105     /// `Session` from a value that now owns it.
106     fn sess(&self) -> &Session;
107
108     /// Provides a uniform interface for re-extracting a reference to an
109     /// `hir_map::Map` from a value that now owns it.
110     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>>;
111
112     /// Produces the pretty-print annotation object.
113     ///
114     /// (Rust does not yet support upcasting from a trait object to
115     /// an object for one of its super-traits.)
116     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn;
117
118     /// Computes an user-readable representation of a path, if possible.
119     fn node_path(&self, id: hir::HirId) -> Option<String> {
120         self.hir_map().and_then(|map| map.def_path_from_hir_id(id)).map(|path| {
121             path.data.into_iter().map(|elem| elem.data.to_string()).collect::<Vec<_>>().join("::")
122         })
123     }
124 }
125
126 struct NoAnn<'hir> {
127     sess: &'hir Session,
128     tcx: Option<TyCtxt<'hir>>,
129 }
130
131 impl<'hir> PrinterSupport for NoAnn<'hir> {
132     fn sess(&self) -> &Session {
133         self.sess
134     }
135
136     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
137         self
138     }
139 }
140
141 impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
142     fn sess(&self) -> &Session {
143         self.sess
144     }
145
146     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
147         self.tcx.map(|tcx| tcx.hir())
148     }
149
150     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
151         self
152     }
153 }
154
155 impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
156 impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
157     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
158         if let Some(tcx) = self.tcx {
159             pprust_hir::PpAnn::nested(tcx.hir(), state, nested)
160         }
161     }
162 }
163
164 struct IdentifiedAnnotation<'hir> {
165     sess: &'hir Session,
166     tcx: Option<TyCtxt<'hir>>,
167 }
168
169 impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
170     fn sess(&self) -> &Session {
171         self.sess
172     }
173
174     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
175         self
176     }
177 }
178
179 impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
180     fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
181         match node {
182             pprust::AnnNode::Expr(_) => s.popen(),
183             _ => {}
184         }
185     }
186     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
187         match node {
188             pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
189
190             pprust::AnnNode::Item(item) => {
191                 s.s.space();
192                 s.synth_comment(item.id.to_string())
193             }
194             pprust::AnnNode::SubItem(id) => {
195                 s.s.space();
196                 s.synth_comment(id.to_string())
197             }
198             pprust::AnnNode::Block(blk) => {
199                 s.s.space();
200                 s.synth_comment(format!("block {}", blk.id))
201             }
202             pprust::AnnNode::Expr(expr) => {
203                 s.s.space();
204                 s.synth_comment(expr.id.to_string());
205                 s.pclose()
206             }
207             pprust::AnnNode::Pat(pat) => {
208                 s.s.space();
209                 s.synth_comment(format!("pat {}", pat.id));
210             }
211         }
212     }
213 }
214
215 impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
216     fn sess(&self) -> &Session {
217         self.sess
218     }
219
220     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
221         self.tcx.map(|tcx| tcx.hir())
222     }
223
224     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
225         self
226     }
227 }
228
229 impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
230     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
231         if let Some(ref tcx) = self.tcx {
232             pprust_hir::PpAnn::nested(tcx.hir(), state, nested)
233         }
234     }
235     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
236         match node {
237             pprust_hir::AnnNode::Expr(_) => s.popen(),
238             _ => {}
239         }
240     }
241     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
242         match node {
243             pprust_hir::AnnNode::Name(_) => {}
244             pprust_hir::AnnNode::Item(item) => {
245                 s.s.space();
246                 s.synth_comment(format!("hir_id: {}", item.hir_id));
247             }
248             pprust_hir::AnnNode::SubItem(id) => {
249                 s.s.space();
250                 s.synth_comment(id.to_string());
251             }
252             pprust_hir::AnnNode::Block(blk) => {
253                 s.s.space();
254                 s.synth_comment(format!("block hir_id: {}", blk.hir_id));
255             }
256             pprust_hir::AnnNode::Expr(expr) => {
257                 s.s.space();
258                 s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
259                 s.pclose();
260             }
261             pprust_hir::AnnNode::Pat(pat) => {
262                 s.s.space();
263                 s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
264             }
265             pprust_hir::AnnNode::Arm(arm) => {
266                 s.s.space();
267                 s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
268             }
269         }
270     }
271 }
272
273 struct HygieneAnnotation<'a> {
274     sess: &'a Session,
275 }
276
277 impl<'a> PrinterSupport for HygieneAnnotation<'a> {
278     fn sess(&self) -> &Session {
279         self.sess
280     }
281
282     fn pp_ann(&self) -> &dyn pprust::PpAnn {
283         self
284     }
285 }
286
287 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
288     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
289         match node {
290             pprust::AnnNode::Ident(&ast::Ident { name, span }) => {
291                 s.s.space();
292                 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
293             }
294             pprust::AnnNode::Name(&name) => {
295                 s.s.space();
296                 s.synth_comment(name.as_u32().to_string())
297             }
298             pprust::AnnNode::Crate(_) => {
299                 s.s.hardbreak();
300                 let verbose = self.sess.verbose();
301                 s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
302                 s.s.hardbreak_if_not_bol();
303             }
304             _ => {}
305         }
306     }
307 }
308
309 struct TypedAnnotation<'a, 'tcx> {
310     tcx: TyCtxt<'tcx>,
311     tables: Cell<&'a ty::TypeckTables<'tcx>>,
312 }
313
314 impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> {
315     fn sess(&self) -> &Session {
316         &self.tcx.sess
317     }
318
319     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'tcx>> {
320         Some(&self.tcx.hir())
321     }
322
323     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
324         self
325     }
326
327     fn node_path(&self, id: hir::HirId) -> Option<String> {
328         Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id)))
329     }
330 }
331
332 impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
333     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
334         let old_tables = self.tables.get();
335         if let pprust_hir::Nested::Body(id) = nested {
336             self.tables.set(self.tcx.body_tables(id));
337         }
338         pprust_hir::PpAnn::nested(self.tcx.hir(), state, nested);
339         self.tables.set(old_tables);
340     }
341     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
342         match node {
343             pprust_hir::AnnNode::Expr(_) => s.popen(),
344             _ => {}
345         }
346     }
347     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
348         match node {
349             pprust_hir::AnnNode::Expr(expr) => {
350                 s.s.space();
351                 s.s.word("as");
352                 s.s.space();
353                 s.s.word(self.tables.get().expr_ty(expr).to_string());
354                 s.pclose();
355             }
356             _ => {}
357         }
358     }
359 }
360
361 fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
362     let src_name = input.source_name();
363     let src =
364         String::clone(&sess.source_map().get_source_file(&src_name).unwrap().src.as_ref().unwrap());
365     (src, src_name)
366 }
367
368 fn write_output(out: Vec<u8>, ofile: Option<&Path>) {
369     match ofile {
370         None => print!("{}", String::from_utf8(out).unwrap()),
371         Some(p) => match File::create(p) {
372             Ok(mut w) => w.write_all(&out).unwrap(),
373             Err(e) => panic!("print-print failed to open {} due to {}", p.display(), e),
374         },
375     }
376 }
377
378 pub fn print_after_parsing(
379     sess: &Session,
380     input: &Input,
381     krate: &ast::Crate,
382     ppm: PpMode,
383     ofile: Option<&Path>,
384 ) {
385     let (src, src_name) = get_source(input, sess);
386
387     let mut out = String::new();
388
389     if let PpmSource(s) = ppm {
390         // Silently ignores an identified node.
391         let out = &mut out;
392         call_with_pp_support(&s, sess, None, move |annotation| {
393             debug!("pretty printing source code {:?}", s);
394             let sess = annotation.sess();
395             *out = pprust::print_crate(
396                 sess.source_map(),
397                 &sess.parse_sess,
398                 krate,
399                 src_name,
400                 src,
401                 annotation.pp_ann(),
402                 false,
403             )
404         })
405     } else {
406         unreachable!();
407     };
408
409     write_output(out.into_bytes(), ofile);
410 }
411
412 pub fn print_after_hir_lowering<'tcx>(
413     tcx: TyCtxt<'tcx>,
414     input: &Input,
415     krate: &ast::Crate,
416     ppm: PpMode,
417     ofile: Option<&Path>,
418 ) {
419     if ppm.needs_analysis() {
420         abort_on_err(print_with_analysis(tcx, ppm, ofile), tcx.sess);
421         return;
422     }
423
424     let (src, src_name) = get_source(input, tcx.sess);
425
426     let mut out = String::new();
427
428     match ppm {
429         PpmSource(s) => {
430             // Silently ignores an identified node.
431             let out = &mut out;
432             call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
433                 debug!("pretty printing source code {:?}", s);
434                 let sess = annotation.sess();
435                 *out = pprust::print_crate(
436                     sess.source_map(),
437                     &sess.parse_sess,
438                     krate,
439                     src_name,
440                     src,
441                     annotation.pp_ann(),
442                     true,
443                 )
444             })
445         }
446
447         PpmHir(s) => {
448             let out = &mut out;
449             call_with_pp_support_hir(&s, tcx, move |annotation, krate| {
450                 debug!("pretty printing source code {:?}", s);
451                 let sess = annotation.sess();
452                 *out = pprust_hir::print_crate(
453                     sess.source_map(),
454                     &sess.parse_sess,
455                     krate,
456                     src_name,
457                     src,
458                     annotation.pp_ann(),
459                 )
460             })
461         }
462
463         PpmHirTree(s) => {
464             let out = &mut out;
465             call_with_pp_support_hir(&s, tcx, move |_annotation, krate| {
466                 debug!("pretty printing source code {:?}", s);
467                 *out = format!("{:#?}", krate);
468             });
469         }
470
471         _ => unreachable!(),
472     }
473
474     write_output(out.into_bytes(), ofile);
475 }
476
477 // In an ideal world, this would be a public function called by the driver after
478 // analysis is performed. However, we want to call `phase_3_run_analysis_passes`
479 // with a different callback than the standard driver, so that isn't easy.
480 // Instead, we call that function ourselves.
481 fn print_with_analysis(
482     tcx: TyCtxt<'_>,
483     ppm: PpMode,
484     ofile: Option<&Path>,
485 ) -> Result<(), ErrorReported> {
486     let mut out = Vec::new();
487
488     tcx.analysis(LOCAL_CRATE)?;
489
490     match ppm {
491         PpmMir | PpmMirCFG => match ppm {
492             PpmMir => write_mir_pretty(tcx, None, &mut out),
493             PpmMirCFG => write_mir_graphviz(tcx, None, &mut out),
494             _ => unreachable!(),
495         },
496         _ => unreachable!(),
497     }
498     .unwrap();
499
500     write_output(out, ofile);
501
502     Ok(())
503 }