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