]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/pretty.rs
b0b5594b93eac1bc1bbdd427534f4d178d1cdf60
[rust.git] / src / librustc_driver / pretty.rs
1 //! The various pretty-printing routines.
2
3 use rustc::cfg;
4 use rustc::cfg::graphviz::LabelledCFG;
5 use rustc::hir;
6 use rustc::hir::map as hir_map;
7 use rustc::hir::map::blocks;
8 use rustc::hir::print as pprust_hir;
9 use rustc::session::Session;
10 use rustc::session::config::{Input, OutputFilenames};
11 use rustc::ty::{self, TyCtxt, Resolutions, AllArenas};
12 use rustc_borrowck as borrowck;
13 use rustc_borrowck::graphviz as borrowck_dot;
14 use rustc_data_structures::thin_vec::ThinVec;
15 use rustc_metadata::cstore::CStore;
16 use rustc_mir::util::{write_mir_pretty, write_mir_graphviz};
17
18 use syntax::ast::{self, BlockCheckMode};
19 use syntax::mut_visit::{*, MutVisitor, visit_clobber};
20 use syntax::print::{pprust};
21 use syntax::print::pprust::PrintState;
22 use syntax::ptr::P;
23 use syntax_pos::{self, FileName};
24
25 use graphviz as dot;
26 use smallvec::SmallVec;
27
28 use std::cell::Cell;
29 use std::fs::File;
30 use std::io::{self, Write};
31 use std::ops::DerefMut;
32 use std::option;
33 use std::path::Path;
34 use std::str::FromStr;
35 use std::mem;
36
37 pub use self::UserIdentifiedItem::*;
38 pub use self::PpSourceMode::*;
39 pub use self::PpMode::*;
40 use self::NodesMatchingUII::*;
41 use {abort_on_err, driver};
42
43 #[derive(Copy, Clone, PartialEq, Debug)]
44 pub enum PpSourceMode {
45     PpmNormal,
46     PpmEveryBodyLoops,
47     PpmExpanded,
48     PpmIdentified,
49     PpmExpandedIdentified,
50     PpmExpandedHygiene,
51     PpmTyped,
52 }
53
54 #[derive(Copy, Clone, PartialEq, Debug)]
55 pub enum PpFlowGraphMode {
56     Default,
57     /// Drops the labels from the edges in the flowgraph output. This
58     /// is mostly for use in the -Z unpretty flowgraph run-make tests,
59     /// since the labels are largely uninteresting in those cases and
60     /// have become a pain to maintain.
61     UnlabelledEdges,
62 }
63 #[derive(Copy, Clone, PartialEq, Debug)]
64 pub enum PpMode {
65     PpmSource(PpSourceMode),
66     PpmHir(PpSourceMode),
67     PpmHirTree(PpSourceMode),
68     PpmFlowGraph(PpFlowGraphMode),
69     PpmMir,
70     PpmMirCFG,
71 }
72
73 impl PpMode {
74     pub fn needs_ast_map(&self, opt_uii: &Option<UserIdentifiedItem>) -> bool {
75         match *self {
76             PpmSource(PpmNormal) |
77             PpmSource(PpmEveryBodyLoops) |
78             PpmSource(PpmIdentified) => opt_uii.is_some(),
79
80             PpmSource(PpmExpanded) |
81             PpmSource(PpmExpandedIdentified) |
82             PpmSource(PpmExpandedHygiene) |
83             PpmHir(_) |
84             PpmHirTree(_) |
85             PpmMir |
86             PpmMirCFG |
87             PpmFlowGraph(_) => true,
88             PpmSource(PpmTyped) => panic!("invalid state"),
89         }
90     }
91
92     pub fn needs_analysis(&self) -> bool {
93         match *self {
94             PpmMir | PpmMirCFG | PpmFlowGraph(_) => true,
95             _ => false,
96         }
97     }
98 }
99
100 pub fn parse_pretty(sess: &Session,
101                     name: &str,
102                     extended: bool)
103                     -> (PpMode, Option<UserIdentifiedItem>) {
104     let mut split = name.splitn(2, '=');
105     let first = split.next().unwrap();
106     let opt_second = split.next();
107     let first = match (first, extended) {
108         ("normal", _) => PpmSource(PpmNormal),
109         ("identified", _) => PpmSource(PpmIdentified),
110         ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops),
111         ("expanded", _) => PpmSource(PpmExpanded),
112         ("expanded,identified", _) => PpmSource(PpmExpandedIdentified),
113         ("expanded,hygiene", _) => PpmSource(PpmExpandedHygiene),
114         ("hir", true) => PpmHir(PpmNormal),
115         ("hir,identified", true) => PpmHir(PpmIdentified),
116         ("hir,typed", true) => PpmHir(PpmTyped),
117         ("hir-tree", true) => PpmHirTree(PpmNormal),
118         ("mir", true) => PpmMir,
119         ("mir-cfg", true) => PpmMirCFG,
120         ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode::Default),
121         ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode::UnlabelledEdges),
122         _ => {
123             if extended {
124                 sess.fatal(&format!("argument to `unpretty` must be one of `normal`, \
125                                      `expanded`, `flowgraph[,unlabelled]=<nodeid>`, \
126                                      `identified`, `expanded,identified`, `everybody_loops`, \
127                                      `hir`, `hir,identified`, `hir,typed`, `hir-tree`, \
128                                      `mir` or `mir-cfg`; got {}",
129                                     name));
130             } else {
131                 sess.fatal(&format!("argument to `pretty` must be one of `normal`, `expanded`, \
132                                      `identified`, or `expanded,identified`; got {}",
133                                     name));
134             }
135         }
136     };
137     let opt_second = opt_second.and_then(|s| s.parse::<UserIdentifiedItem>().ok());
138     (first, opt_second)
139 }
140
141
142
143 // This slightly awkward construction is to allow for each PpMode to
144 // choose whether it needs to do analyses (which can consume the
145 // Session) and then pass through the session (now attached to the
146 // analysis results) on to the chosen pretty-printer, along with the
147 // `&PpAnn` object.
148 //
149 // Note that since the `&PrinterSupport` is freshly constructed on each
150 // call, it would not make sense to try to attach the lifetime of `self`
151 // to the lifetime of the `&PrinterObject`.
152 //
153 // (The `use_once_payload` is working around the current lack of once
154 // functions in the compiler.)
155
156 impl PpSourceMode {
157     /// Constructs a `PrinterSupport` object and passes it to `f`.
158     fn call_with_pp_support<'tcx, A, F>(&self,
159                                         sess: &'tcx Session,
160                                         hir_map: Option<&hir_map::Map<'tcx>>,
161                                         f: F)
162                                         -> A
163         where F: FnOnce(&dyn PrinterSupport) -> A
164     {
165         match *self {
166             PpmNormal | PpmEveryBodyLoops | PpmExpanded => {
167                 let annotation = NoAnn {
168                     sess,
169                     hir_map: hir_map.map(|m| m.clone()),
170                 };
171                 f(&annotation)
172             }
173
174             PpmIdentified | PpmExpandedIdentified => {
175                 let annotation = IdentifiedAnnotation {
176                     sess,
177                     hir_map: hir_map.map(|m| m.clone()),
178                 };
179                 f(&annotation)
180             }
181             PpmExpandedHygiene => {
182                 let annotation = HygieneAnnotation {
183                     sess,
184                 };
185                 f(&annotation)
186             }
187             _ => panic!("Should use call_with_pp_support_hir"),
188         }
189     }
190     fn call_with_pp_support_hir<'tcx, A, F>(
191         &self,
192         sess: &'tcx Session,
193         cstore: &'tcx CStore,
194         hir_map: &hir_map::Map<'tcx>,
195         resolutions: &Resolutions,
196         output_filenames: &OutputFilenames,
197         id: &str,
198         f: F
199     ) -> A
200         where F: FnOnce(&dyn HirPrinterSupport, &hir::Crate) -> A
201     {
202         match *self {
203             PpmNormal => {
204                 let annotation = NoAnn {
205                     sess,
206                     hir_map: Some(hir_map.clone()),
207                 };
208                 f(&annotation, hir_map.forest.krate())
209             }
210
211             PpmIdentified => {
212                 let annotation = IdentifiedAnnotation {
213                     sess,
214                     hir_map: Some(hir_map.clone()),
215                 };
216                 f(&annotation, hir_map.forest.krate())
217             }
218             PpmTyped => {
219                 let control = &driver::CompileController::basic();
220                 let codegen_backend = ::get_codegen_backend(sess);
221                 let mut arenas = AllArenas::new();
222                 abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
223                                                                  control,
224                                                                  sess,
225                                                                  cstore,
226                                                                  hir_map.clone(),
227                                                                  resolutions.clone(),
228                                                                  &mut arenas,
229                                                                  id,
230                                                                  output_filenames,
231                                                                  |tcx, _, _| {
232                     let empty_tables = ty::TypeckTables::empty(None);
233                     let annotation = TypedAnnotation {
234                         tcx,
235                         tables: Cell::new(&empty_tables)
236                     };
237                     tcx.dep_graph.with_ignore(|| {
238                         f(&annotation, hir_map.forest.krate())
239                     })
240                 }),
241                              sess)
242             }
243             _ => panic!("Should use call_with_pp_support"),
244         }
245     }
246 }
247
248 trait PrinterSupport: pprust::PpAnn {
249     /// Provides a uniform interface for re-extracting a reference to a
250     /// `Session` from a value that now owns it.
251     fn sess<'a>(&'a self) -> &'a Session;
252
253     /// Produces the pretty-print annotation object.
254     ///
255     /// (Rust does not yet support upcasting from a trait object to
256     /// an object for one of its super-traits.)
257     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn;
258 }
259
260 trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
261     /// Provides a uniform interface for re-extracting a reference to a
262     /// `Session` from a value that now owns it.
263     fn sess<'a>(&'a self) -> &'a Session;
264
265     /// Provides a uniform interface for re-extracting a reference to an
266     /// `hir_map::Map` from a value that now owns it.
267     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>>;
268
269     /// Produces the pretty-print annotation object.
270     ///
271     /// (Rust does not yet support upcasting from a trait object to
272     /// an object for one of its super-traits.)
273     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn;
274
275     /// Computes an user-readable representation of a path, if possible.
276     fn node_path(&self, id: ast::NodeId) -> Option<String> {
277         self.hir_map().and_then(|map| map.def_path_from_id(id)).map(|path| {
278             path.data
279                 .into_iter()
280                 .map(|elem| elem.data.to_string())
281                 .collect::<Vec<_>>()
282                 .join("::")
283         })
284     }
285 }
286
287 struct NoAnn<'hir> {
288     sess: &'hir Session,
289     hir_map: Option<hir_map::Map<'hir>>,
290 }
291
292 impl<'hir> PrinterSupport for NoAnn<'hir> {
293     fn sess<'a>(&'a self) -> &'a Session {
294         self.sess
295     }
296
297     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
298         self
299     }
300 }
301
302 impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
303     fn sess<'a>(&'a self) -> &'a Session {
304         self.sess
305     }
306
307     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
308         self.hir_map.as_ref()
309     }
310
311     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
312         self
313     }
314 }
315
316 impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
317 impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
318     fn nested(&self, state: &mut pprust_hir::State, nested: pprust_hir::Nested)
319               -> io::Result<()> {
320         if let Some(ref map) = self.hir_map {
321             pprust_hir::PpAnn::nested(map, state, nested)
322         } else {
323             Ok(())
324         }
325     }
326 }
327
328 struct IdentifiedAnnotation<'hir> {
329     sess: &'hir Session,
330     hir_map: Option<hir_map::Map<'hir>>,
331 }
332
333 impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
334     fn sess<'a>(&'a self) -> &'a Session {
335         self.sess
336     }
337
338     fn pp_ann<'a>(&'a self) -> &'a dyn pprust::PpAnn {
339         self
340     }
341 }
342
343 impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
344     fn pre(&self, s: &mut pprust::State, node: pprust::AnnNode) -> io::Result<()> {
345         match node {
346             pprust::AnnNode::Expr(_) => s.popen(),
347             _ => Ok(()),
348         }
349     }
350     fn post(&self, s: &mut pprust::State, node: pprust::AnnNode) -> io::Result<()> {
351         match node {
352             pprust::AnnNode::Ident(_) |
353             pprust::AnnNode::Name(_) => Ok(()),
354
355             pprust::AnnNode::Item(item) => {
356                 s.s.space()?;
357                 s.synth_comment(item.id.to_string())
358             }
359             pprust::AnnNode::SubItem(id) => {
360                 s.s.space()?;
361                 s.synth_comment(id.to_string())
362             }
363             pprust::AnnNode::Block(blk) => {
364                 s.s.space()?;
365                 s.synth_comment(format!("block {}", blk.id))
366             }
367             pprust::AnnNode::Expr(expr) => {
368                 s.s.space()?;
369                 s.synth_comment(expr.id.to_string())?;
370                 s.pclose()
371             }
372             pprust::AnnNode::Pat(pat) => {
373                 s.s.space()?;
374                 s.synth_comment(format!("pat {}", pat.id))
375             }
376         }
377     }
378 }
379
380 impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
381     fn sess<'a>(&'a self) -> &'a Session {
382         self.sess
383     }
384
385     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'hir>> {
386         self.hir_map.as_ref()
387     }
388
389     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
390         self
391     }
392 }
393
394 impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
395     fn nested(&self, state: &mut pprust_hir::State, nested: pprust_hir::Nested)
396               -> io::Result<()> {
397         if let Some(ref map) = self.hir_map {
398             pprust_hir::PpAnn::nested(map, state, nested)
399         } else {
400             Ok(())
401         }
402     }
403     fn pre(&self, s: &mut pprust_hir::State, node: pprust_hir::AnnNode) -> io::Result<()> {
404         match node {
405             pprust_hir::AnnNode::Expr(_) => s.popen(),
406             _ => Ok(()),
407         }
408     }
409     fn post(&self, s: &mut pprust_hir::State, node: pprust_hir::AnnNode) -> io::Result<()> {
410         match node {
411             pprust_hir::AnnNode::Name(_) => Ok(()),
412             pprust_hir::AnnNode::Item(item) => {
413                 s.s.space()?;
414                 s.synth_comment(format!("node_id: {} hir local_id: {}",
415                                         item.id, item.hir_id.local_id.as_u32()))
416             }
417             pprust_hir::AnnNode::SubItem(id) => {
418                 s.s.space()?;
419                 s.synth_comment(id.to_string())
420             }
421             pprust_hir::AnnNode::Block(blk) => {
422                 s.s.space()?;
423                 s.synth_comment(format!("block hir_id: {} hir local_id: {}",
424                                         blk.hir_id, blk.hir_id.local_id.as_u32()))
425             }
426             pprust_hir::AnnNode::Expr(expr) => {
427                 s.s.space()?;
428                 s.synth_comment(format!("node_id: {} hir local_id: {}",
429                                         expr.id, expr.hir_id.local_id.as_u32()))?;
430                 s.pclose()
431             }
432             pprust_hir::AnnNode::Pat(pat) => {
433                 s.s.space()?;
434                 s.synth_comment(format!("pat node_id: {} hir local_id: {}",
435                                         pat.id, pat.hir_id.local_id.as_u32()))
436             }
437         }
438     }
439 }
440
441 struct HygieneAnnotation<'a> {
442     sess: &'a Session
443 }
444
445 impl<'a> PrinterSupport for HygieneAnnotation<'a> {
446     fn sess(&self) -> &Session {
447         self.sess
448     }
449
450     fn pp_ann(&self) -> &dyn pprust::PpAnn {
451         self
452     }
453 }
454
455 impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
456     fn post(&self, s: &mut pprust::State, node: pprust::AnnNode) -> io::Result<()> {
457         match node {
458             pprust::AnnNode::Ident(&ast::Ident { name, span }) => {
459                 s.s.space()?;
460                 // FIXME #16420: this doesn't display the connections
461                 // between syntax contexts
462                 s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
463             }
464             pprust::AnnNode::Name(&name) => {
465                 s.s.space()?;
466                 s.synth_comment(name.as_u32().to_string())
467             }
468             _ => Ok(()),
469         }
470     }
471 }
472
473
474 struct TypedAnnotation<'a, 'tcx: 'a> {
475     tcx: TyCtxt<'a, 'tcx, 'tcx>,
476     tables: Cell<&'a ty::TypeckTables<'tcx>>,
477 }
478
479 impl<'b, 'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'b, 'tcx> {
480     fn sess<'a>(&'a self) -> &'a Session {
481         &self.tcx.sess
482     }
483
484     fn hir_map<'a>(&'a self) -> Option<&'a hir_map::Map<'tcx>> {
485         Some(&self.tcx.hir())
486     }
487
488     fn pp_ann<'a>(&'a self) -> &'a dyn pprust_hir::PpAnn {
489         self
490     }
491
492     fn node_path(&self, id: ast::NodeId) -> Option<String> {
493         Some(self.tcx.node_path_str(id))
494     }
495 }
496
497 impl<'a, 'tcx> pprust_hir::PpAnn for TypedAnnotation<'a, 'tcx> {
498     fn nested(&self, state: &mut pprust_hir::State, nested: pprust_hir::Nested)
499               -> io::Result<()> {
500         let old_tables = self.tables.get();
501         if let pprust_hir::Nested::Body(id) = nested {
502             self.tables.set(self.tcx.body_tables(id));
503         }
504         pprust_hir::PpAnn::nested(self.tcx.hir(), state, nested)?;
505         self.tables.set(old_tables);
506         Ok(())
507     }
508     fn pre(&self, s: &mut pprust_hir::State, node: pprust_hir::AnnNode) -> io::Result<()> {
509         match node {
510             pprust_hir::AnnNode::Expr(_) => s.popen(),
511             _ => Ok(()),
512         }
513     }
514     fn post(&self, s: &mut pprust_hir::State, node: pprust_hir::AnnNode) -> io::Result<()> {
515         match node {
516             pprust_hir::AnnNode::Expr(expr) => {
517                 s.s.space()?;
518                 s.s.word("as")?;
519                 s.s.space()?;
520                 s.s.word(self.tables.get().expr_ty(expr).to_string())?;
521                 s.pclose()
522             }
523             _ => Ok(()),
524         }
525     }
526 }
527
528 fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
529     let print_loans = sess.opts.debugging_opts.flowgraph_print_loans;
530     let print_moves = sess.opts.debugging_opts.flowgraph_print_moves;
531     let print_assigns = sess.opts.debugging_opts.flowgraph_print_assigns;
532     let print_all = sess.opts.debugging_opts.flowgraph_print_all;
533     let mut variants = Vec::new();
534     if print_all || print_loans {
535         variants.push(borrowck_dot::Loans);
536     }
537     if print_all || print_moves {
538         variants.push(borrowck_dot::Moves);
539     }
540     if print_all || print_assigns {
541         variants.push(borrowck_dot::Assigns);
542     }
543     variants
544 }
545
546 #[derive(Clone, Debug)]
547 pub enum UserIdentifiedItem {
548     ItemViaNode(ast::NodeId),
549     ItemViaPath(Vec<String>),
550 }
551
552 impl FromStr for UserIdentifiedItem {
553     type Err = ();
554     fn from_str(s: &str) -> Result<UserIdentifiedItem, ()> {
555         Ok(s.parse()
556             .map(ast::NodeId::from_u32)
557             .map(ItemViaNode)
558             .unwrap_or_else(|_| ItemViaPath(s.split("::").map(|s| s.to_string()).collect())))
559     }
560 }
561
562 enum NodesMatchingUII<'a, 'hir: 'a> {
563     NodesMatchingDirect(option::IntoIter<ast::NodeId>),
564     NodesMatchingSuffix(hir_map::NodesMatchingSuffix<'a, 'hir>),
565 }
566
567 impl<'a, 'hir> Iterator for NodesMatchingUII<'a, 'hir> {
568     type Item = ast::NodeId;
569
570     fn next(&mut self) -> Option<ast::NodeId> {
571         match self {
572             &mut NodesMatchingDirect(ref mut iter) => iter.next(),
573             &mut NodesMatchingSuffix(ref mut iter) => iter.next(),
574         }
575     }
576
577     fn size_hint(&self) -> (usize, Option<usize>) {
578         match self {
579             &NodesMatchingDirect(ref iter) => iter.size_hint(),
580             &NodesMatchingSuffix(ref iter) => iter.size_hint(),
581         }
582     }
583 }
584
585 impl UserIdentifiedItem {
586     fn reconstructed_input(&self) -> String {
587         match *self {
588             ItemViaNode(node_id) => node_id.to_string(),
589             ItemViaPath(ref parts) => parts.join("::"),
590         }
591     }
592
593     fn all_matching_node_ids<'a, 'hir>(&'a self,
594                                        map: &'a hir_map::Map<'hir>)
595                                        -> NodesMatchingUII<'a, 'hir> {
596         match *self {
597             ItemViaNode(node_id) => NodesMatchingDirect(Some(node_id).into_iter()),
598             ItemViaPath(ref parts) => NodesMatchingSuffix(map.nodes_matching_suffix(&parts)),
599         }
600     }
601
602     fn to_one_node_id(self, user_option: &str, sess: &Session, map: &hir_map::Map) -> ast::NodeId {
603         let fail_because = |is_wrong_because| -> ast::NodeId {
604             let message = format!("{} needs NodeId (int) or unique path suffix (b::c::d); got \
605                                    {}, which {}",
606                                   user_option,
607                                   self.reconstructed_input(),
608                                   is_wrong_because);
609             sess.fatal(&message)
610         };
611
612         let mut saw_node = ast::DUMMY_NODE_ID;
613         let mut seen = 0;
614         for node in self.all_matching_node_ids(map) {
615             saw_node = node;
616             seen += 1;
617             if seen > 1 {
618                 fail_because("does not resolve uniquely");
619             }
620         }
621         if seen == 0 {
622             fail_because("does not resolve to any item");
623         }
624
625         assert!(seen == 1);
626         return saw_node;
627     }
628 }
629
630 // Note: Also used by librustdoc, see PR #43348. Consider moving this struct elsewhere.
631 //
632 // FIXME: Currently the `everybody_loops` transformation is not applied to:
633 //  * `const fn`, due to issue #43636 that `loop` is not supported for const evaluation. We are
634 //    waiting for miri to fix that.
635 //  * `impl Trait`, due to issue #43869 that functions returning impl Trait cannot be diverging.
636 //    Solving this may require `!` to implement every trait, which relies on the an even more
637 //    ambitious form of the closed RFC #1637. See also [#34511].
638 //
639 // [#34511]: https://github.com/rust-lang/rust/issues/34511#issuecomment-322340401
640 pub struct ReplaceBodyWithLoop<'a> {
641     within_static_or_const: bool,
642     nested_blocks: Option<Vec<ast::Block>>,
643     sess: &'a Session,
644 }
645
646 impl<'a> ReplaceBodyWithLoop<'a> {
647     pub fn new(sess: &'a Session) -> ReplaceBodyWithLoop<'a> {
648         ReplaceBodyWithLoop {
649             within_static_or_const: false,
650             nested_blocks: None,
651             sess
652         }
653     }
654
655     fn run<R, F: FnOnce(&mut Self) -> R>(&mut self, is_const: bool, action: F) -> R {
656         let old_const = mem::replace(&mut self.within_static_or_const, is_const);
657         let old_blocks = self.nested_blocks.take();
658         let ret = action(self);
659         self.within_static_or_const = old_const;
660         self.nested_blocks = old_blocks;
661         ret
662     }
663
664     fn should_ignore_fn(ret_ty: &ast::FnDecl) -> bool {
665         if let ast::FunctionRetTy::Ty(ref ty) = ret_ty.output {
666             fn involves_impl_trait(ty: &ast::Ty) -> bool {
667                 match ty.node {
668                     ast::TyKind::ImplTrait(..) => true,
669                     ast::TyKind::Slice(ref subty) |
670                     ast::TyKind::Array(ref subty, _) |
671                     ast::TyKind::Ptr(ast::MutTy { ty: ref subty, .. }) |
672                     ast::TyKind::Rptr(_, ast::MutTy { ty: ref subty, .. }) |
673                     ast::TyKind::Paren(ref subty) => involves_impl_trait(subty),
674                     ast::TyKind::Tup(ref tys) => any_involves_impl_trait(tys.iter()),
675                     ast::TyKind::Path(_, ref path) => path.segments.iter().any(|seg| {
676                         match seg.args.as_ref().map(|generic_arg| &**generic_arg) {
677                             None => false,
678                             Some(&ast::GenericArgs::AngleBracketed(ref data)) => {
679                                 let types = data.args.iter().filter_map(|arg| match arg {
680                                     ast::GenericArg::Type(ty) => Some(ty),
681                                     _ => None,
682                                 });
683                                 any_involves_impl_trait(types.into_iter()) ||
684                                 any_involves_impl_trait(data.bindings.iter().map(|b| &b.ty))
685                             },
686                             Some(&ast::GenericArgs::Parenthesized(ref data)) => {
687                                 any_involves_impl_trait(data.inputs.iter()) ||
688                                 any_involves_impl_trait(data.output.iter())
689                             }
690                         }
691                     }),
692                     _ => false,
693                 }
694             }
695
696             fn any_involves_impl_trait<'a, I: Iterator<Item = &'a P<ast::Ty>>>(mut it: I) -> bool {
697                 it.any(|subty| involves_impl_trait(subty))
698             }
699
700             involves_impl_trait(ty)
701         } else {
702             false
703         }
704     }
705 }
706
707 impl<'a> MutVisitor for ReplaceBodyWithLoop<'a> {
708     fn visit_item_kind(&mut self, i: &mut ast::ItemKind) {
709         let is_const = match i {
710             ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => true,
711             ast::ItemKind::Fn(ref decl, ref header, _, _) =>
712                 header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl),
713             _ => false,
714         };
715         self.run(is_const, |s| noop_visit_item_kind(i, s))
716     }
717
718     fn flat_map_trait_item(&mut self, i: ast::TraitItem) -> SmallVec<[ast::TraitItem; 1]> {
719         let is_const = match i.node {
720             ast::TraitItemKind::Const(..) => true,
721             ast::TraitItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) =>
722                 header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl),
723             _ => false,
724         };
725         self.run(is_const, |s| noop_flat_map_trait_item(i, s))
726     }
727
728     fn flat_map_impl_item(&mut self, i: ast::ImplItem) -> SmallVec<[ast::ImplItem; 1]> {
729         let is_const = match i.node {
730             ast::ImplItemKind::Const(..) => true,
731             ast::ImplItemKind::Method(ast::MethodSig { ref decl, ref header, .. }, _) =>
732                 header.constness.node == ast::Constness::Const || Self::should_ignore_fn(decl),
733             _ => false,
734         };
735         self.run(is_const, |s| noop_flat_map_impl_item(i, s))
736     }
737
738     fn visit_anon_const(&mut self, c: &mut ast::AnonConst) {
739         self.run(true, |s| noop_visit_anon_const(c, s))
740     }
741
742     fn visit_block(&mut self, b: &mut P<ast::Block>) {
743         fn stmt_to_block(rules: ast::BlockCheckMode,
744                          s: Option<ast::Stmt>,
745                          sess: &Session) -> ast::Block {
746             ast::Block {
747                 stmts: s.into_iter().collect(),
748                 rules,
749                 id: sess.next_node_id(),
750                 span: syntax_pos::DUMMY_SP,
751             }
752         }
753
754         fn block_to_stmt(b: ast::Block, sess: &Session) -> ast::Stmt {
755             let expr = P(ast::Expr {
756                 id: sess.next_node_id(),
757                 node: ast::ExprKind::Block(P(b), None),
758                 span: syntax_pos::DUMMY_SP,
759                 attrs: ThinVec::new(),
760             });
761
762             ast::Stmt {
763                 id: sess.next_node_id(),
764                 node: ast::StmtKind::Expr(expr),
765                 span: syntax_pos::DUMMY_SP,
766             }
767         }
768
769         let empty_block = stmt_to_block(BlockCheckMode::Default, None, self.sess);
770         let loop_expr = P(ast::Expr {
771             node: ast::ExprKind::Loop(P(empty_block), None),
772             id: self.sess.next_node_id(),
773             span: syntax_pos::DUMMY_SP,
774                 attrs: ThinVec::new(),
775         });
776
777         let loop_stmt = ast::Stmt {
778             id: self.sess.next_node_id(),
779             span: syntax_pos::DUMMY_SP,
780             node: ast::StmtKind::Expr(loop_expr),
781         };
782
783         if self.within_static_or_const {
784             noop_visit_block(b, self)
785         } else {
786             visit_clobber(b.deref_mut(), |b| {
787                 let mut stmts = vec![];
788                 for s in b.stmts {
789                     let old_blocks = self.nested_blocks.replace(vec![]);
790
791                     stmts.extend(self.flat_map_stmt(s).into_iter().filter(|s| s.is_item()));
792
793                     // we put a Some in there earlier with that replace(), so this is valid
794                     let new_blocks = self.nested_blocks.take().unwrap();
795                     self.nested_blocks = old_blocks;
796                     stmts.extend(new_blocks.into_iter().map(|b| block_to_stmt(b, &self.sess)));
797                 }
798
799                 let mut new_block = ast::Block {
800                     stmts,
801                     ..b
802                 };
803
804                 if let Some(old_blocks) = self.nested_blocks.as_mut() {
805                     //push our fresh block onto the cache and yield an empty block with `loop {}`
806                     if !new_block.stmts.is_empty() {
807                         old_blocks.push(new_block);
808                     }
809
810                     stmt_to_block(b.rules, Some(loop_stmt), self.sess)
811                 } else {
812                     //push `loop {}` onto the end of our fresh block and yield that
813                     new_block.stmts.push(loop_stmt);
814
815                     new_block
816                 }
817             })
818         }
819     }
820
821     // in general the pretty printer processes unexpanded code, so
822     // we override the default `visit_mac` method which panics.
823     fn visit_mac(&mut self, mac: &mut ast::Mac) {
824         noop_visit_mac(mac, self)
825     }
826 }
827
828 fn print_flowgraph<'a, 'tcx, W: Write>(variants: Vec<borrowck_dot::Variant>,
829                                        tcx: TyCtxt<'a, 'tcx, 'tcx>,
830                                        code: blocks::Code<'tcx>,
831                                        mode: PpFlowGraphMode,
832                                        mut out: W)
833                                        -> io::Result<()> {
834     let body_id = match code {
835         blocks::Code::Expr(expr) => {
836             // Find the function this expression is from.
837             let mut node_id = expr.id;
838             loop {
839                 let node = tcx.hir().get(node_id);
840                 if let Some(n) = hir::map::blocks::FnLikeNode::from_node(node) {
841                     break n.body();
842                 }
843                 let parent = tcx.hir().get_parent_node(node_id);
844                 assert_ne!(node_id, parent);
845                 node_id = parent;
846             }
847         }
848         blocks::Code::FnLike(fn_like) => fn_like.body(),
849     };
850     let body = tcx.hir().body(body_id);
851     let cfg = cfg::CFG::new(tcx, &body);
852     let labelled_edges = mode != PpFlowGraphMode::UnlabelledEdges;
853     let lcfg = LabelledCFG {
854         tcx,
855         cfg: &cfg,
856         name: format!("node_{}", code.id()),
857         labelled_edges,
858     };
859
860     match code {
861         _ if variants.is_empty() => {
862             let r = dot::render(&lcfg, &mut out);
863             return expand_err_details(r);
864         }
865         blocks::Code::Expr(_) => {
866             tcx.sess.err("--pretty flowgraph with -Z flowgraph-print annotations requires \
867                           fn-like node id.");
868             return Ok(());
869         }
870         blocks::Code::FnLike(fn_like) => {
871             let (bccx, analysis_data) =
872                 borrowck::build_borrowck_dataflow_data_for_fn(tcx, fn_like.body(), &cfg);
873
874             let lcfg = borrowck_dot::DataflowLabeller {
875                 inner: lcfg,
876                 variants,
877                 borrowck_ctxt: &bccx,
878                 analysis_data: &analysis_data,
879             };
880             let r = dot::render(&lcfg, &mut out);
881             return expand_err_details(r);
882         }
883     }
884
885     fn expand_err_details(r: io::Result<()>) -> io::Result<()> {
886         r.map_err(|ioerr| {
887             io::Error::new(io::ErrorKind::Other,
888                            format!("graphviz::render failed: {}", ioerr))
889         })
890     }
891 }
892
893 pub fn visit_crate(sess: &Session, krate: &mut ast::Crate, ppm: PpMode) {
894     if let PpmSource(PpmEveryBodyLoops) = ppm {
895         ReplaceBodyWithLoop::new(sess).visit_crate(krate);
896     }
897 }
898
899 fn get_source(input: &Input, sess: &Session) -> (Vec<u8>, FileName) {
900     let src_name = driver::source_name(input);
901     let src = sess.source_map()
902         .get_source_file(&src_name)
903         .unwrap()
904         .src
905         .as_ref()
906         .unwrap()
907         .as_bytes()
908         .to_vec();
909     (src, src_name)
910 }
911
912 fn write_output(out: Vec<u8>, ofile: Option<&Path>) {
913     match ofile {
914         None => print!("{}", String::from_utf8(out).unwrap()),
915         Some(p) => {
916             match File::create(p) {
917                 Ok(mut w) => w.write_all(&out).unwrap(),
918                 Err(e) => panic!("print-print failed to open {} due to {}", p.display(), e),
919             }
920         }
921     }
922 }
923
924 pub fn print_after_parsing(sess: &Session,
925                            input: &Input,
926                            krate: &ast::Crate,
927                            ppm: PpMode,
928                            ofile: Option<&Path>) {
929     let (src, src_name) = get_source(input, sess);
930
931     let mut rdr = &*src;
932     let mut out = Vec::new();
933
934     if let PpmSource(s) = ppm {
935         // Silently ignores an identified node.
936         let out: &mut dyn Write = &mut out;
937         s.call_with_pp_support(sess, None, move |annotation| {
938             debug!("pretty printing source code {:?}", s);
939             let sess = annotation.sess();
940             pprust::print_crate(sess.source_map(),
941                                 &sess.parse_sess,
942                                 krate,
943                                 src_name,
944                                 &mut rdr,
945                                 box out,
946                                 annotation.pp_ann(),
947                                 false)
948         }).unwrap()
949     } else {
950         unreachable!();
951     };
952
953     write_output(out, ofile);
954 }
955
956 pub fn print_after_hir_lowering<'tcx, 'a: 'tcx>(sess: &'a Session,
957                                                 cstore: &'tcx CStore,
958                                                 hir_map: &hir_map::Map<'tcx>,
959                                                 resolutions: &Resolutions,
960                                                 input: &Input,
961                                                 krate: &ast::Crate,
962                                                 crate_name: &str,
963                                                 ppm: PpMode,
964                                                 output_filenames: &OutputFilenames,
965                                                 opt_uii: Option<UserIdentifiedItem>,
966                                                 ofile: Option<&Path>) {
967     if ppm.needs_analysis() {
968         print_with_analysis(sess,
969                             cstore,
970                             hir_map,
971                             resolutions,
972                             crate_name,
973                             output_filenames,
974                             ppm,
975                             opt_uii,
976                             ofile);
977         return;
978     }
979
980     let (src, src_name) = get_source(input, sess);
981
982     let mut rdr = &src[..];
983     let mut out = Vec::new();
984
985     match (ppm, opt_uii) {
986             (PpmSource(s), _) => {
987                 // Silently ignores an identified node.
988                 let out: &mut dyn Write = &mut out;
989                 s.call_with_pp_support(sess, Some(hir_map), move |annotation| {
990                     debug!("pretty printing source code {:?}", s);
991                     let sess = annotation.sess();
992                     pprust::print_crate(sess.source_map(),
993                                         &sess.parse_sess,
994                                         krate,
995                                         src_name,
996                                         &mut rdr,
997                                         box out,
998                                         annotation.pp_ann(),
999                                         true)
1000                 })
1001             }
1002
1003             (PpmHir(s), None) => {
1004                 let out: &mut dyn Write = &mut out;
1005                 s.call_with_pp_support_hir(sess,
1006                                            cstore,
1007                                            hir_map,
1008                                            resolutions,
1009                                            output_filenames,
1010                                            crate_name,
1011                                            move |annotation, krate| {
1012                     debug!("pretty printing source code {:?}", s);
1013                     let sess = annotation.sess();
1014                     pprust_hir::print_crate(sess.source_map(),
1015                                             &sess.parse_sess,
1016                                             krate,
1017                                             src_name,
1018                                             &mut rdr,
1019                                             box out,
1020                                             annotation.pp_ann(),
1021                                             true)
1022                 })
1023             }
1024
1025             (PpmHirTree(s), None) => {
1026                 let out: &mut dyn Write = &mut out;
1027                 s.call_with_pp_support_hir(sess,
1028                                            cstore,
1029                                            hir_map,
1030                                            resolutions,
1031                                            output_filenames,
1032                                            crate_name,
1033                                            move |_annotation, krate| {
1034                     debug!("pretty printing source code {:?}", s);
1035                     write!(out, "{:#?}", krate)
1036                 })
1037             }
1038
1039             (PpmHir(s), Some(uii)) => {
1040                 let out: &mut dyn Write = &mut out;
1041                 s.call_with_pp_support_hir(sess,
1042                                            cstore,
1043                                            hir_map,
1044                                            resolutions,
1045                                            output_filenames,
1046                                            crate_name,
1047                                            move |annotation, _| {
1048                     debug!("pretty printing source code {:?}", s);
1049                     let sess = annotation.sess();
1050                     let hir_map = annotation.hir_map().expect("-Z unpretty missing HIR map");
1051                     let mut pp_state = pprust_hir::State::new_from_input(sess.source_map(),
1052                                                                          &sess.parse_sess,
1053                                                                          src_name,
1054                                                                          &mut rdr,
1055                                                                          box out,
1056                                                                          annotation.pp_ann(),
1057                                                                          true);
1058                     for node_id in uii.all_matching_node_ids(hir_map) {
1059                         let node = hir_map.get(node_id);
1060                         pp_state.print_node(node)?;
1061                         pp_state.s.space()?;
1062                         let path = annotation.node_path(node_id)
1063                             .expect("-Z unpretty missing node paths");
1064                         pp_state.synth_comment(path)?;
1065                         pp_state.s.hardbreak()?;
1066                     }
1067                     pp_state.s.eof()
1068                 })
1069             }
1070
1071             (PpmHirTree(s), Some(uii)) => {
1072                 let out: &mut dyn Write = &mut out;
1073                 s.call_with_pp_support_hir(sess,
1074                                            cstore,
1075                                            hir_map,
1076                                            resolutions,
1077                                            output_filenames,
1078                                            crate_name,
1079                                            move |_annotation, _krate| {
1080                     debug!("pretty printing source code {:?}", s);
1081                     for node_id in uii.all_matching_node_ids(hir_map) {
1082                         let node = hir_map.get(node_id);
1083                         write!(out, "{:#?}", node)?;
1084                     }
1085                     Ok(())
1086                 })
1087             }
1088
1089             _ => unreachable!(),
1090         }
1091         .unwrap();
1092
1093     write_output(out, ofile);
1094 }
1095
1096 // In an ideal world, this would be a public function called by the driver after
1097 // analysis is performed. However, we want to call `phase_3_run_analysis_passes`
1098 // with a different callback than the standard driver, so that isn't easy.
1099 // Instead, we call that function ourselves.
1100 fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session,
1101                                        cstore: &'a CStore,
1102                                        hir_map: &hir_map::Map<'tcx>,
1103                                        resolutions: &Resolutions,
1104                                        crate_name: &str,
1105                                        output_filenames: &OutputFilenames,
1106                                        ppm: PpMode,
1107                                        uii: Option<UserIdentifiedItem>,
1108                                        ofile: Option<&Path>) {
1109     let nodeid = if let Some(uii) = uii {
1110         debug!("pretty printing for {:?}", uii);
1111         Some(uii.to_one_node_id("-Z unpretty", sess, &hir_map))
1112     } else {
1113         debug!("pretty printing for whole crate");
1114         None
1115     };
1116
1117     let mut out = Vec::new();
1118
1119     let control = &driver::CompileController::basic();
1120     let codegen_backend = ::get_codegen_backend(sess);
1121     let mut arenas = AllArenas::new();
1122     abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
1123                                                      control,
1124                                                      sess,
1125                                                      cstore,
1126                                                      hir_map.clone(),
1127                                                      resolutions.clone(),
1128                                                      &mut arenas,
1129                                                      crate_name,
1130                                                      output_filenames,
1131                                                      |tcx, _, _| {
1132         match ppm {
1133             PpmMir | PpmMirCFG => {
1134                 if let Some(nodeid) = nodeid {
1135                     let def_id = tcx.hir().local_def_id(nodeid);
1136                     match ppm {
1137                         PpmMir => write_mir_pretty(tcx, Some(def_id), &mut out),
1138                         PpmMirCFG => write_mir_graphviz(tcx, Some(def_id), &mut out),
1139                         _ => unreachable!(),
1140                     }?;
1141                 } else {
1142                     match ppm {
1143                         PpmMir => write_mir_pretty(tcx, None, &mut out),
1144                         PpmMirCFG => write_mir_graphviz(tcx, None, &mut out),
1145                         _ => unreachable!(),
1146                     }?;
1147                 }
1148                 Ok(())
1149             }
1150             PpmFlowGraph(mode) => {
1151                 let nodeid =
1152                     nodeid.expect("`pretty flowgraph=..` needs NodeId (int) or unique path \
1153                                    suffix (b::c::d)");
1154                 let node = tcx.hir().find(nodeid).unwrap_or_else(|| {
1155                     tcx.sess.fatal(&format!("--pretty flowgraph couldn't find id: {}", nodeid))
1156                 });
1157
1158                 match blocks::Code::from_node(&tcx.hir(), nodeid) {
1159                     Some(code) => {
1160                         let variants = gather_flowgraph_variants(tcx.sess);
1161
1162                         let out: &mut dyn Write = &mut out;
1163
1164                         print_flowgraph(variants, tcx, code, mode, out)
1165                     }
1166                     None => {
1167                         let message = format!("--pretty=flowgraph needs block, fn, or method; \
1168                                                got {:?}",
1169                                               node);
1170
1171                         tcx.sess.span_fatal(tcx.hir().span(nodeid), &message)
1172                     }
1173                 }
1174             }
1175             _ => unreachable!(),
1176         }
1177     }),
1178                  sess)
1179         .unwrap();
1180
1181     write_output(out, ofile);
1182 }