]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/pretty.rs
Move the HIR cfg to `rustc_ast_borrowck`
[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::map::blocks;
6 use rustc::hir::print as pprust_hir;
7 use rustc::hir::def_id::LOCAL_CRATE;
8 use rustc::session::Session;
9 use rustc::session::config::Input;
10 use rustc::ty::{self, TyCtxt};
11 use rustc::util::common::ErrorReported;
12 use rustc_interface::util::ReplaceBodyWithLoop;
13 use rustc_ast_borrowck as borrowck;
14 use rustc_ast_borrowck::graphviz as borrowck_dot;
15 use rustc_ast_borrowck::cfg::{self, graphviz::LabelledCFG};
16 use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
17
18 use syntax::ast;
19 use syntax::mut_visit::MutVisitor;
20 use syntax::print::{pprust};
21 use syntax_pos::FileName;
22
23 use graphviz as dot;
24
25 use std::cell::Cell;
26 use std::fs::File;
27 use std::io::{self, Write};
28 use std::option;
29 use std::path::Path;
30 use std::str::FromStr;
31
32 pub use self::UserIdentifiedItem::*;
33 pub use self::PpSourceMode::*;
34 pub use self::PpMode::*;
35 use self::NodesMatchingUII::*;
36 use crate::abort_on_err;
37
38 use crate::source_name;
39
40 #[derive(Copy, Clone, PartialEq, Debug)]
41 pub enum PpSourceMode {
42     PpmNormal,
43     PpmEveryBodyLoops,
44     PpmExpanded,
45     PpmIdentified,
46     PpmExpandedIdentified,
47     PpmExpandedHygiene,
48     PpmTyped,
49 }
50
51 #[derive(Copy, Clone, PartialEq, Debug)]
52 pub enum PpFlowGraphMode {
53     Default,
54     /// Drops the labels from the edges in the flowgraph output. This
55     /// is mostly for use in the -Z unpretty flowgraph run-make tests,
56     /// since the labels are largely uninteresting in those cases and
57     /// have become a pain to maintain.
58     UnlabelledEdges,
59 }
60 #[derive(Copy, Clone, PartialEq, Debug)]
61 pub enum PpMode {
62     PpmSource(PpSourceMode),
63     PpmHir(PpSourceMode),
64     PpmHirTree(PpSourceMode),
65     PpmFlowGraph(PpFlowGraphMode),
66     PpmMir,
67     PpmMirCFG,
68 }
69
70 impl PpMode {
71     pub fn needs_ast_map(&self, opt_uii: &Option<UserIdentifiedItem>) -> bool {
72         match *self {
73             PpmSource(PpmNormal) |
74             PpmSource(PpmEveryBodyLoops) |
75             PpmSource(PpmIdentified) => opt_uii.is_some(),
76
77             PpmSource(PpmExpanded) |
78             PpmSource(PpmExpandedIdentified) |
79             PpmSource(PpmExpandedHygiene) |
80             PpmHir(_) |
81             PpmHirTree(_) |
82             PpmMir |
83             PpmMirCFG |
84             PpmFlowGraph(_) => true,
85             PpmSource(PpmTyped) => panic!("invalid state"),
86         }
87     }
88
89     pub fn needs_analysis(&self) -> bool {
90         match *self {
91             PpmMir | PpmMirCFG | PpmFlowGraph(_) => true,
92             _ => false,
93         }
94     }
95 }
96
97 pub fn parse_pretty(sess: &Session,
98                     name: &str,
99                     extended: bool)
100                     -> (PpMode, Option<UserIdentifiedItem>) {
101     let mut split = name.splitn(2, '=');
102     let first = split.next().unwrap();
103     let opt_second = split.next();
104     let first = match (first, extended) {
105         ("normal", _) => PpmSource(PpmNormal),
106         ("identified", _) => PpmSource(PpmIdentified),
107         ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops),
108         ("expanded", _) => PpmSource(PpmExpanded),
109         ("expanded,identified", _) => PpmSource(PpmExpandedIdentified),
110         ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene),
111         ("hir", true) => PpmHir(PpmNormal),
112         ("hir,identified", true) => PpmHir(PpmIdentified),
113         ("hir,typed", true) => PpmHir(PpmTyped),
114         ("hir-tree", true) => PpmHirTree(PpmNormal),
115         ("mir", true) => PpmMir,
116         ("mir-cfg", true) => PpmMirCFG,
117         ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default),
118         ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges),
119         _ => {
120             if extended {
121                 sess.fatal(&format!("argument to `unpretty` must be one of `normal`, \
122                                      `expanded`, `flowgraph[,unlabelled]=<nodeid>`, \
123                                      `identified`, `expanded,identified`, `everybody_loops`, \
124                                      `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \
125                                      `mir` or `mir-cfg`; got {}",
126                                     name));
127             } else {
128                 sess.fatal(&format!("argument to `pretty` must be one of `normal`, `expanded`, \
129                                      `identified`, or `expanded,identified`; got {}",
130                                     name));
131             }
132         }
133     };
134     let opt_second = opt_second.and_then(|s| s.parse::<UserIdentifiedItem>().ok());
135     (first, opt_second)
136 }
137
138
139
140 // This slightly awkward construction is to allow for each PpMode to
141 // choose whether it needs to do analyses (which can consume the
142 // Session) and then pass through the session (now attached to the
143 // analysis results) on to the chosen pretty-printer, along with the
144 // `&PpAnn` object.
145 //
146 // Note that since the `&PrinterSupport` is freshly constructed on each
147 // call, it would not make sense to try to attach the lifetime of `self`
148 // to the lifetime of the `&PrinterObject`.
149 //
150 // (The `use_once_payload` is working around the current lack of once
151 // functions in the compiler.)
152
153 impl PpSourceMode {
154     /// Constructs a `PrinterSupport` object and passes it to `f`.
155     fn call_with_pp_support<'tcx, A, F>(
156         &self,
157         sess: &'tcx Session,
158         tcx: Option<TyCtxt<'tcx>>,
159         f: F,
160     ) -> A
161     where
162         F: FnOnce(&dyn PrinterSupport) -> A,
163     {
164         match *self {
165             PpmNormal | PpmEveryBodyLoops | PpmExpanded => {
166                 let annotation = NoAnn {
167                     sess,
168                     tcx,
169                 };
170                 f(&annotation)
171             }
172
173             PpmIdentified | PpmExpandedIdentified => {
174                 let annotation = IdentifiedAnnotation {
175                     sess,
176                     tcx,
177                 };
178                 f(&annotation)
179             }
180             PpmExpandedHygiene => {
181                 let annotation = HygieneAnnotation {
182                     sess,
183                 };
184                 f(&annotation)
185             }
186             _ => panic!("Should use call_with_pp_support_hir"),
187         }
188     }
189     fn call_with_pp_support_hir<A, F>(&self, tcx: TyCtxt<'_>, f: F) -> A
190     where
191         F: FnOnce(&dyn HirPrinterSupport<'_>, &hir::Crate) -> A,
192     {
193         match *self {
194             PpmNormal => {
195                 let annotation = NoAnn {
196                     sess: tcx.sess,
197                     tcx: Some(tcx),
198                 };
199                 f(&annotation, tcx.hir().forest.krate())
200             }
201
202             PpmIdentified => {
203                 let annotation = IdentifiedAnnotation {
204                     sess: tcx.sess,
205                     tcx: Some(tcx),
206                 };
207                 f(&annotation, tcx.hir().forest.krate())
208             }
209             PpmTyped => {
210                 abort_on_err(tcx.analysis(LOCAL_CRATE), tcx.sess);
211
212                 let empty_tables = ty::TypeckTables::empty(None);
213                 let annotation = TypedAnnotation {
214                     tcx,
215                     tables: Cell::new(&empty_tables)
216                 };
217                 tcx.dep_graph.with_ignore(|| {
218                     f(&annotation, tcx.hir().forest.krate())
219                 })
220             }
221             _ => panic!("Should use call_with_pp_support"),
222         }
223     }
224 }
225
226 trait PrinterSupport: pprust::PpAnn {
227     /// Provides a uniform interface for re-extracting a reference to a
228     /// `Session` from a value that now owns it.
229     fn sess(&self) -> &Session;
230
231     /// Produces the pretty-print annotation object.
232     ///
233     /// (Rust does not yet support upcasting from a trait object to
234     /// an object for one of its super-traits.)
235     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn;
236 }
237
238 trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
239     /// Provides a uniform interface for re-extracting a reference to a
240     /// `Session` from a value that now owns it.
241     fn sess(&self) -> &Session;
242
243     /// Provides a uniform interface for re-extracting a reference to an
244     /// `hir_map::Map` from a value that now owns it.
245     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>>;
246
247     /// Produces the pretty-print annotation object.
248     ///
249     /// (Rust does not yet support upcasting from a trait object to
250     /// an object for one of its super-traits.)
251     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn;
252
253     /// Computes an user-readable representation of a path, if possible.
254     fn node_path(&self, id: hir::HirId) -> Option<String> {
255         self.hir_map().and_then(|map| {
256             map.def_path_from_hir_id(id)
257         }).map(|path| {
258             path.data
259                 .into_iter()
260                 .map(|elem| elem.data.to_string())
261                 .collect::<Vec<_>>()
262                 .join("::")
263         })
264     }
265 }
266
267 struct NoAnn<'hir> {
268     sess: &'hir Session,
269     tcx: Option<TyCtxt<'hir>>,
270 }
271
272 impl<'hir> PrinterSupport for NoAnn<'hir> {
273     fn sess(&self) -> &Session {
274         self.sess
275     }
276
277     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
278         self
279     }
280 }
281
282 impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
283     fn sess(&self) -> &Session {
284         self.sess
285     }
286
287     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
288         self.tcx.map(|tcx| tcx.hir())
289     }
290
291     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
292         self
293     }
294 }
295
296 impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
297 impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
298     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
299         if let Some(tcx) = self.tcx {
300             pprust_hir::PpAnn::nested(tcx.hir(), state, nested)
301         }
302     }
303 }
304
305 struct IdentifiedAnnotation<'hir> {
306     sess: &'hir Session,
307     tcx: Option<TyCtxt<'hir>>,
308 }
309
310 impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
311     fn sess(&self) -> &Session {
312         self.sess
313     }
314
315     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
316         self
317     }
318 }
319
320 impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
321     fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
322         match node {
323             pprust::AnnNode::Expr(_) => s.popen(),
324             _ => {}
325         }
326     }
327     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
328         match node {
329             pprust::AnnNode::Ident(_) |
330             pprust::AnnNode::Name(_) => {},
331
332             pprust::AnnNode::Item(item) => {
333                 s.s.space();
334                 s.synth_comment(item.id.to_string())
335             }
336             pprust::AnnNode::SubItem(id) => {
337                 s.s.space();
338                 s.synth_comment(id.to_string())
339             }
340             pprust::AnnNode::Block(blk) => {
341                 s.s.space();
342                 s.synth_comment(format!("block {}", blk.id))
343             }
344             pprust::AnnNode::Expr(expr) => {
345                 s.s.space();
346                 s.synth_comment(expr.id.to_string());
347                 s.pclose()
348             }
349             pprust::AnnNode::Pat(pat) => {
350                 s.s.space();
351                 s.synth_comment(format!("pat {}", pat.id));
352             }
353         }
354     }
355 }
356
357 impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
358     fn sess(&self) -> &Session {
359         self.sess
360     }
361
362     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
363         self.tcx.map(|tcx| tcx.hir())
364     }
365
366     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
367         self
368     }
369 }
370
371 impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
372     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
373         if let Some(ref tcx) = self.tcx {
374             pprust_hir::PpAnn::nested(tcx.hir(), state, nested)
375         }
376     }
377     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
378         match node {
379             pprust_hir::AnnNode::Expr(_) => s.popen(),
380             _ => {}
381         }
382     }
383     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
384         match node {
385             pprust_hir::AnnNode::Name(_) => {},
386             pprust_hir::AnnNode::Item(item) => {
387                 s.s.space();
388                 s.synth_comment(format!("hir_id: {}", item.hir_id));
389             }
390             pprust_hir::AnnNode::SubItem(id) => {
391                 s.s.space();
392                 s.synth_comment(id.to_string());
393             }
394             pprust_hir::AnnNode::Block(blk) => {
395                 s.s.space();
396                 s.synth_comment(format!("block hir_id: {}", blk.hir_id));
397             }
398             pprust_hir::AnnNode::Expr(expr) => {
399                 s.s.space();
400                 s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
401                 s.pclose();
402             }
403             pprust_hir::AnnNode::Pat(pat) => {
404                 s.s.space();
405                 s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
406             }
407             pprust_hir::AnnNode::Arm(arm) => {
408                 s.s.space();
409                 s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
410             }
411         }
412     }
413 }
414
415 struct HygieneAnnotation<'a> {
416     sess: &'a Session
417 }
418
419 impl<'a> PrinterSupport for HygieneAnnotation<'a> {
420     fn sess(&self) -> &Session {
421         self.sess
422     }
423
424     fn pp_ann(&self) -> &dyn pprust::PpAnn {
425         self
426     }
427 }
428
429 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
430     fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
431         match node {
432             pprust::AnnNode::Ident(&ast::Ident { name, span }) => {
433                 s.s.space();
434                 // FIXME #16420: this doesn't display the connections
435                 // between syntax contexts
436                 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
437             }
438             pprust::AnnNode::Name(&name) => {
439                 s.s.space();
440                 s.synth_comment(name.as_u32().to_string())
441             }
442             _ => {}
443         }
444     }
445 }
446
447 struct TypedAnnotation<'a, 'tcx> {
448     tcx: TyCtxt<'tcx>,
449     tables: Cell<&'a ty::TypeckTables<'tcx>>,
450 }
451
452 impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> {
453     fn sess(&self) -> &Session {
454         &self.tcx.sess
455     }
456
457     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'tcx>> {
458         Some(&self.tcx.hir())
459     }
460
461     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
462         self
463     }
464
465     fn node_path(&self, id: hir::HirId) -> Option<String> {
466         Some(self.tcx.def_path_str(self.tcx.hir().local_def_id(id)))
467     }
468 }
469
470 impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
471     fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
472         let old_tables = self.tables.get();
473         if let pprust_hir::Nested::Body(id) = nested {
474             self.tables.set(self.tcx.body_tables(id));
475         }
476         pprust_hir::PpAnn::nested(self.tcx.hir(), state, nested);
477         self.tables.set(old_tables);
478     }
479     fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
480         match node {
481             pprust_hir::AnnNode::Expr(_) => s.popen(),
482             _ => {}
483         }
484     }
485     fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
486         match node {
487             pprust_hir::AnnNode::Expr(expr) => {
488                 s.s.space();
489                 s.s.word("as");
490                 s.s.space();
491                 s.s.word(self.tables.get().expr_ty(expr).to_string());
492                 s.pclose();
493             }
494             _ => {},
495         }
496     }
497 }
498
499 fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
500     let print_loans = sess.opts.debugging_opts.flowgraph_print_loans;
501     let print_moves = sess.opts.debugging_opts.flowgraph_print_moves;
502     let print_assigns = sess.opts.debugging_opts.flowgraph_print_assigns;
503     let print_all = sess.opts.debugging_opts.flowgraph_print_all;
504     let mut variants = Vec::new();
505     if print_all || print_loans {
506         variants.push(borrowck_dot::Loans);
507     }
508     if print_all || print_moves {
509         variants.push(borrowck_dot::Moves);
510     }
511     if print_all || print_assigns {
512         variants.push(borrowck_dot::Assigns);
513     }
514     variants
515 }
516
517 #[derive(Clone, Debug)]
518 pub enum UserIdentifiedItem {
519     ItemViaNode(ast::NodeId),
520     ItemViaPath(Vec<String>),
521 }
522
523 impl FromStr for UserIdentifiedItem {
524     type Err = ();
525     fn from_str(s: &str) -> Result<UserIdentifiedItem, ()> {
526         Ok(s.parse()
527             .map(ast::NodeId::from_u32)
528             .map(ItemViaNode)
529             .unwrap_or_else(|_| ItemViaPath(s.split("::").map(|s| s.to_string()).collect())))
530     }
531 }
532
533 enum NodesMatchingUII<'a> {
534     NodesMatchingDirect(option::IntoIter<ast::NodeId>),
535     NodesMatchingSuffix(Box<dyn Iterator<Item = ast::NodeId> + 'a>),
536 }
537
538 impl<'a> Iterator for NodesMatchingUII<'a> {
539     type Item = ast::NodeId;
540
541     fn next(&mut self) -> Option<ast::NodeId> {
542         match self {
543             &mut NodesMatchingDirect(ref mut iter) => iter.next(),
544             &mut NodesMatchingSuffix(ref mut iter) => iter.next(),
545         }
546     }
547
548     fn size_hint(&self) -> (usize, Option<usize>) {
549         match self {
550             &NodesMatchingDirect(ref iter) => iter.size_hint(),
551             &NodesMatchingSuffix(ref iter) => iter.size_hint(),
552         }
553     }
554 }
555
556 impl UserIdentifiedItem {
557     fn reconstructed_input(&self) -> String {
558         match *self {
559             ItemViaNode(node_id) => node_id.to_string(),
560             ItemViaPath(ref parts) => parts.join("::"),
561         }
562     }
563
564     fn all_matching_node_ids<'a, 'hir>(&'a self,
565                                        map: &'a hir_map::Map<'hir>)
566                                        -> NodesMatchingUII<'a> {
567         match *self {
568             ItemViaNode(node_id) => NodesMatchingDirect(Some(node_id).into_iter()),
569             ItemViaPath(ref parts) => {
570                 NodesMatchingSuffix(Box::new(map.nodes_matching_suffix(&parts)))
571             }
572         }
573     }
574
575     fn to_one_node_id(self,
576                       user_option: &str,
577                       sess: &Session,
578                       map: &hir_map::Map<'_>)
579                       -> ast::NodeId {
580         let fail_because = |is_wrong_because| -> ast::NodeId {
581             let message = format!("{} needs NodeId (int) or unique path suffix (b::c::d); got \
582                                    {}, which {}",
583                                   user_option,
584                                   self.reconstructed_input(),
585                                   is_wrong_because);
586             sess.fatal(&message)
587         };
588
589         let mut saw_node = ast::DUMMY_NODE_ID;
590         let mut seen = 0;
591         for node in self.all_matching_node_ids(map) {
592             saw_node = node;
593             seen += 1;
594             if seen > 1 {
595                 fail_because("does not resolve uniquely");
596             }
597         }
598         if seen == 0 {
599             fail_because("does not resolve to any item");
600         }
601
602         assert!(seen == 1);
603         return saw_node;
604     }
605 }
606
607 fn print_flowgraph<'tcx, W: Write>(
608     variants: Vec<borrowck_dot::Variant>,
609     tcx: TyCtxt<'tcx>,
610     code: blocks::Code<'tcx>,
611     mode: PpFlowGraphMode,
612     mut out: W,
613 ) -> io::Result<()> {
614     let body_id = match code {
615         blocks::Code::Expr(expr) => {
616             // Find the function this expression is from.
617             let mut hir_id = expr.hir_id;
618             loop {
619                 let node = tcx.hir().get(hir_id);
620                 if let Some(n) = hir::map::blocks::FnLikeNode::from_node(node) {
621                     break n.body();
622                 }
623                 let parent = tcx.hir().get_parent_node(hir_id);
624                 assert_ne!(hir_id, parent);
625                 hir_id = parent;
626             }
627         }
628         blocks::Code::FnLike(fn_like) => fn_like.body(),
629     };
630     let body = tcx.hir().body(body_id);
631     let cfg = cfg::CFG::new(tcx, &body);
632     let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges;
633     let hir_id = code.id();
634     // We have to disassemble the hir_id because name must be ASCII
635     // alphanumeric. This does not appear in the rendered graph, so it does not
636     // have to be user friendly.
637     let name = format!(
638         "hir_id_{}_{}",
639         hir_id.owner.index(),
640         hir_id.local_id.index(),
641     );
642     let lcfg = LabelledCFG {
643         tcx,
644         cfg: &cfg,
645         name,
646         labelled_edges,
647     };
648
649     match code {
650         _ if variants.is_empty() => {
651             let r = dot::render(&lcfg, &mut out);
652             return expand_err_details(r);
653         }
654         blocks::Code::Expr(_) => {
655             tcx.sess.err("--pretty flowgraph with -Z flowgraph-print annotations requires \
656                           fn-like node id.");
657             return Ok(());
658         }
659         blocks::Code::FnLike(fn_like) => {
660             let (bccx, analysis_data) =
661                 borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.body(), &cfg);
662
663             let lcfg = borrowck_dot::DataflowLabeller {
664                 inner: lcfg,
665                 variants,
666                 borrowck_ctxt: &bccx,
667                 analysis_data: &analysis_data,
668             };
669             let r = dot::render(&lcfg, &mut out);
670             return expand_err_details(r);
671         }
672     }
673
674     fn expand_err_details(r: io::Result<()>) -> io::Result<()> {
675         r.map_err(|ioerr| {
676             io::Error::new(io::ErrorKind::Other,
677                            format!("graphviz::render failed: {}", ioerr))
678         })
679     }
680 }
681
682 pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) {
683     if let PpmSource(PpmEveryBodyLoops) = ppm {
684         ReplaceBodyWithLoop::new(sess).visit_crate(krate);
685     }
686 }
687
688 fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
689     let src_name = source_name(input);
690     let src = String::clone(&sess.source_map()
691         .get_source_file(&src_name)
692         .unwrap()
693         .src
694         .as_ref()
695         .unwrap());
696     (src, src_name)
697 }
698
699 fn write_output(out: Vec<u8>, ofile: Option<&Path>) {
700     match ofile {
701         None => print!("{}", String::from_utf8(out).unwrap()),
702         Some(p) => {
703             match File::create(p) {
704                 Ok(mut w) => w.write_all(&out).unwrap(),
705                 Err(e) => panic!("print-print failed to open {} due to {}", p.display(), e),
706             }
707         }
708     }
709 }
710
711 pub fn print_after_parsing(sess: &Session,
712                            input: &Input,
713                            krate: &ast::Crate,
714                            ppm: PpMode,
715                            ofile: Option<&Path>) {
716     let (src, src_name) = get_source(input, sess);
717
718     let mut out = String::new();
719
720     if let PpmSource(s) = ppm {
721         // Silently ignores an identified node.
722         let out = &mut out;
723         s.call_with_pp_support(sess, None, move |annotation| {
724             debug!("pretty printing source code {:?}", s);
725             let sess = annotation.sess();
726             *out = pprust::print_crate(sess.source_map(),
727                                 &sess.parse_sess,
728                                 krate,
729                                 src_name,
730                                 src,
731                                 annotation.pp_ann(),
732                                 false)
733         })
734     } else {
735         unreachable!();
736     };
737
738     write_output(out.into_bytes(), ofile);
739 }
740
741 pub fn print_after_hir_lowering<'tcx>(
742     tcx: TyCtxt<'tcx>,
743     input: &Input,
744     krate: &ast::Crate,
745     ppm: PpMode,
746     opt_uii: Option<UserIdentifiedItem>,
747     ofile: Option<&Path>,
748 ) {
749     if ppm.needs_analysis() {
750         abort_on_err(print_with_analysis(
751             tcx,
752             ppm,
753             opt_uii,
754             ofile
755         ), tcx.sess);
756         return;
757     }
758
759     let (src, src_name) = get_source(input, tcx.sess);
760
761     let mut out = String::new();
762
763     match (ppm, opt_uii) {
764             (PpmSource(s), _) => {
765                 // Silently ignores an identified node.
766                 let out = &mut out;
767                 let src = src.clone();
768                 s.call_with_pp_support(tcx.sess, Some(tcx), move |annotation| {
769                     debug!("pretty printing source code {:?}", s);
770                     let sess = annotation.sess();
771                     *out = pprust::print_crate(sess.source_map(),
772                                         &sess.parse_sess,
773                                         krate,
774                                         src_name,
775                                         src,
776                                         annotation.pp_ann(),
777                                         true)
778                 })
779             }
780
781             (PpmHir(s), None) => {
782                 let out = &mut out;
783                 let src = src.clone();
784                 s.call_with_pp_support_hir(tcx, move |annotation, krate| {
785                     debug!("pretty printing source code {:?}", s);
786                     let sess = annotation.sess();
787                     *out = pprust_hir::print_crate(sess.source_map(),
788                                             &sess.parse_sess,
789                                             krate,
790                                             src_name,
791                                             src,
792                                             annotation.pp_ann())
793                 })
794             }
795
796             (PpmHirTree(s), None) => {
797                 let out = &mut out;
798                 s.call_with_pp_support_hir(tcx, move |_annotation, krate| {
799                     debug!("pretty printing source code {:?}", s);
800                     *out = format!("{:#?}", krate);
801                 });
802             }
803
804             (PpmHir(s), Some(uii)) => {
805                 let out = &mut out;
806                 let src = src.clone();
807                 s.call_with_pp_support_hir(tcx, move |annotation, _| {
808                     debug!("pretty printing source code {:?}", s);
809                     let sess = annotation.sess();
810                     let hir_map = annotation.hir_map().expect("-Z unpretty missing HIR map");
811                     let mut pp_state = pprust_hir::State::new_from_input(sess.source_map(),
812                                                                          &sess.parse_sess,
813                                                                          src_name,
814                                                                          src,
815                                                                          annotation.pp_ann());
816                     for node_id in uii.all_matching_node_ids(hir_map) {
817                         let hir_id = tcx.hir().node_to_hir_id(node_id);
818                         let node = hir_map.get(hir_id);
819                         pp_state.print_node(node);
820                         pp_state.s.space();
821                         let path = annotation.node_path(hir_id)
822                             .expect("-Z unpretty missing node paths");
823                         pp_state.synth_comment(path);
824                         pp_state.s.hardbreak();
825                     }
826                     *out = pp_state.s.eof();
827                 })
828             }
829
830             (PpmHirTree(s), Some(uii)) => {
831                 let out = &mut out;
832                 s.call_with_pp_support_hir(tcx, move |_annotation, _krate| {
833                     debug!("pretty printing source code {:?}", s);
834                     for node_id in uii.all_matching_node_ids(tcx.hir()) {
835                         let hir_id = tcx.hir().node_to_hir_id(node_id);
836                         let node = tcx.hir().get(hir_id);
837                         out.push_str(&format!("{:#?}", node));
838                     }
839                 })
840             }
841
842             _ => unreachable!(),
843         }
844
845     write_output(out.into_bytes(), ofile);
846 }
847
848 // In an ideal world, this would be a public function called by the driver after
849 // analysis is performed. However, we want to call `phase_3_run_analysis_passes`
850 // with a different callback than the standard driver, so that isn't easy.
851 // Instead, we call that function ourselves.
852 fn print_with_analysis(
853     tcx: TyCtxt<'_>,
854     ppm: PpMode,
855     uii: Option<UserIdentifiedItem>,
856     ofile: Option<&Path>,
857 ) -> Result<(), ErrorReported> {
858     let nodeid = if let Some(uii) = uii {
859         debug!("pretty printing for {:?}", uii);
860         Some(uii.to_one_node_id("-Z unpretty", tcx.sess, tcx.hir()))
861     } else {
862         debug!("pretty printing for whole crate");
863         None
864     };
865
866     let mut out = Vec::new();
867
868     tcx.analysis(LOCAL_CRATE)?;
869
870     let mut print = || match ppm {
871         PpmMir | PpmMirCFG => {
872             if let Some(nodeid) = nodeid {
873                 let def_id = tcx.hir().local_def_id_from_node_id(nodeid);
874                 match ppm {
875                     PpmMir => write_mir_pretty(tcx, Some(def_id), &mut out),
876                     PpmMirCFG => write_mir_graphviz(tcx, Some(def_id), &mut out),
877                     _ => unreachable!(),
878                 }?;
879             } else {
880                 match ppm {
881                     PpmMir => write_mir_pretty(tcx, None, &mut out),
882                     PpmMirCFG => write_mir_graphviz(tcx, None, &mut out),
883                     _ => unreachable!(),
884                 }?;
885             }
886             Ok(())
887         }
888         PpmFlowGraph(mode) => {
889             let nodeid =
890                 nodeid.expect("`pretty flowgraph=..` needs NodeId (int) or unique path \
891                                 suffix (b::c::d)");
892             let hir_id = tcx.hir().node_to_hir_id(nodeid);
893             let node = tcx.hir().find(hir_id).unwrap_or_else(|| {
894                 tcx.sess.fatal(&format!("`--pretty=flowgraph` couldn't find ID: {}", nodeid))
895             });
896
897             match blocks::Code::from_node(&tcx.hir(), hir_id) {
898                 Some(code) => {
899                     let variants = gather_flowgraph_variants(tcx.sess);
900
901                     let out: &mut dyn Write = &mut out;
902
903                     print_flowgraph(variants, tcx, code, mode, out)
904                 }
905                 None => {
906                     let message = format!("`--pretty=flowgraph` needs block, fn, or method; \
907                                             got {:?}",
908                                             node);
909
910                     let hir_id = tcx.hir().node_to_hir_id(nodeid);
911                     tcx.sess.span_fatal(tcx.hir().span(hir_id), &message)
912                 }
913             }
914         }
915         _ => unreachable!(),
916     };
917
918     print().unwrap();
919
920     write_output(out, ofile);
921
922     Ok(())
923 }