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