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