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