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