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.
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.
11 //! The various pretty print routines.
13 pub use self::UserIdentifiedItem
::*;
14 pub use self::PpSourceMode
::*;
15 pub use self::PpMode
::*;
16 use self::NodesMatchingUII
::*;
18 use rustc_trans
::back
::link
;
22 use rustc
::ast_map
::{self, blocks, NodePrinter}
;
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
;
34 use syntax
::fold
::{self, Folder}
;
35 use syntax
::print
::{pp, pprust}
;
37 use syntax
::util
::small_vector
::SmallVector
;
42 use std
::io
::{self, Write}
;
44 use std
::path
::PathBuf
;
45 use std
::str::FromStr
;
47 #[derive(Copy, Clone, PartialEq, Debug)]
48 pub enum PpSourceMode
{
54 PpmExpandedIdentified
,
59 #[derive(Copy, Clone, PartialEq, Debug)]
60 pub enum PpFlowGraphMode
{
62 /// Drops the labels from the edges in the flowgraph output. This
63 /// is mostly for use in the --unpretty flowgraph run-make tests,
64 /// since the labels are largely uninteresting in those cases and
65 /// have become a pain to maintain.
68 #[derive(Copy, Clone, PartialEq, Debug)]
70 PpmSource(PpSourceMode
),
71 PpmFlowGraph(PpFlowGraphMode
),
74 pub fn parse_pretty(sess
: &Session
,
76 extended
: bool
) -> (PpMode
, Option
<UserIdentifiedItem
>) {
77 let mut split
= name
.splitn(2, '
='
);
78 let first
= split
.next().unwrap();
79 let opt_second
= split
.next();
80 let first
= match (first
, extended
) {
81 ("normal", _
) => PpmSource(PpmNormal
),
82 ("everybody_loops", true) => PpmSource(PpmEveryBodyLoops
),
83 ("expanded", _
) => PpmSource(PpmExpanded
),
84 ("typed", _
) => PpmSource(PpmTyped
),
85 ("expanded,identified", _
) => PpmSource(PpmExpandedIdentified
),
86 ("expanded,hygiene", _
) => PpmSource(PpmExpandedHygiene
),
87 ("identified", _
) => PpmSource(PpmIdentified
),
88 ("flowgraph", true) => PpmFlowGraph(PpFlowGraphMode
::Default
),
89 ("flowgraph,unlabelled", true) => PpmFlowGraph(PpFlowGraphMode
::UnlabelledEdges
),
93 "argument to `unpretty` must be one of `normal`, \
94 `expanded`, `flowgraph[,unlabelled]=<nodeid>`, `typed`, `identified`, \
95 `expanded,identified`, or `everybody_loops`; got {}", name
));
98 "argument to `pretty` must be one of `normal`, \
99 `expanded`, `typed`, `identified`, \
100 or `expanded,identified`; got {}", name
));
104 let opt_second
= opt_second
.and_then(|s
| s
.parse
::<UserIdentifiedItem
>().ok());
110 // This slightly awkward construction is to allow for each PpMode to
111 // choose whether it needs to do analyses (which can consume the
112 // Session) and then pass through the session (now attached to the
113 // analysis results) on to the chosen pretty-printer, along with the
116 // Note that since the `&PrinterSupport` is freshly constructed on each
117 // call, it would not make sense to try to attach the lifetime of `self`
118 // to the lifetime of the `&PrinterObject`.
120 // (The `use_once_payload` is working around the current lack of once
121 // functions in the compiler.)
124 /// Constructs a `PrinterSupport` object and passes it to `f`.
125 fn call_with_pp_support
<'tcx
, A
, B
, F
>(&self,
127 ast_map
: Option
<ast_map
::Map
<'tcx
>>,
128 arenas
: &'tcx ty
::CtxtArenas
<'tcx
>,
132 F
: FnOnce(&PrinterSupport
, B
) -> A
,
135 PpmNormal
| PpmEveryBodyLoops
| PpmExpanded
=> {
136 let annotation
= NoAnn { sess: sess, ast_map: ast_map }
;
137 f(&annotation
, payload
)
140 PpmIdentified
| PpmExpandedIdentified
=> {
141 let annotation
= IdentifiedAnnotation { sess: sess, ast_map: ast_map }
;
142 f(&annotation
, payload
)
144 PpmExpandedHygiene
=> {
145 let annotation
= HygieneAnnotation { sess: sess, ast_map: ast_map }
;
146 f(&annotation
, payload
)
149 let ast_map
= ast_map
.expect("--pretty=typed missing ast_map");
150 driver
::phase_3_run_analysis_passes(sess
,
154 resolve
::MakeGlobMap
::No
,
156 let annotation
= TypedAnnotation { tcx: tcx }
;
157 f(&annotation
, payload
)
164 trait PrinterSupport
<'ast
>: pprust
::PpAnn
{
165 /// Provides a uniform interface for re-extracting a reference to a
166 /// `Session` from a value that now owns it.
167 fn sess
<'a
>(&'a
self) -> &'a Session
;
169 /// Provides a uniform interface for re-extracting a reference to an
170 /// `ast_map::Map` from a value that now owns it.
171 fn ast_map
<'a
>(&'a
self) -> Option
<&'a ast_map
::Map
<'ast
>>;
173 /// Produces the pretty-print annotation object.
175 /// (Rust does not yet support upcasting from a trait object to
176 /// an object for one of its super-traits.)
177 fn pp_ann
<'a
>(&'a
self) -> &'a pprust
::PpAnn
;
182 ast_map
: Option
<ast_map
::Map
<'ast
>>
185 impl<'ast
> PrinterSupport
<'ast
> for NoAnn
<'ast
> {
186 fn sess
<'a
>(&'a
self) -> &'a Session { &self.sess }
188 fn ast_map
<'a
>(&'a
self) -> Option
<&'a ast_map
::Map
<'ast
>> {
189 self.ast_map
.as_ref()
192 fn pp_ann
<'a
>(&'a
self) -> &'a pprust
::PpAnn { self }
195 impl<'ast
> pprust
::PpAnn
for NoAnn
<'ast
> {}
197 struct IdentifiedAnnotation
<'ast
> {
199 ast_map
: Option
<ast_map
::Map
<'ast
>>,
202 impl<'ast
> PrinterSupport
<'ast
> for IdentifiedAnnotation
<'ast
> {
203 fn sess
<'a
>(&'a
self) -> &'a Session { &self.sess }
205 fn ast_map
<'a
>(&'a
self) -> Option
<&'a ast_map
::Map
<'ast
>> {
206 self.ast_map
.as_ref()
209 fn pp_ann
<'a
>(&'a
self) -> &'a pprust
::PpAnn { self }
212 impl<'ast
> pprust
::PpAnn
for IdentifiedAnnotation
<'ast
> {
214 s
: &mut pprust
::State
,
215 node
: pprust
::AnnNode
) -> io
::Result
<()> {
217 pprust
::NodeExpr(_
) => s
.popen(),
222 s
: &mut pprust
::State
,
223 node
: pprust
::AnnNode
) -> io
::Result
<()> {
225 pprust
::NodeIdent(_
) | pprust
::NodeName(_
) => Ok(()),
227 pprust
::NodeItem(item
) => {
228 try
!(pp
::space(&mut s
.s
));
229 s
.synth_comment(item
.id
.to_string())
231 pprust
::NodeSubItem(id
) => {
232 try
!(pp
::space(&mut s
.s
));
233 s
.synth_comment(id
.to_string())
235 pprust
::NodeBlock(blk
) => {
236 try
!(pp
::space(&mut s
.s
));
237 s
.synth_comment(format
!("block {}", blk
.id
))
239 pprust
::NodeExpr(expr
) => {
240 try
!(pp
::space(&mut s
.s
));
241 try
!(s
.synth_comment(expr
.id
.to_string()));
244 pprust
::NodePat(pat
) => {
245 try
!(pp
::space(&mut s
.s
));
246 s
.synth_comment(format
!("pat {}", pat
.id
))
252 struct HygieneAnnotation
<'ast
> {
254 ast_map
: Option
<ast_map
::Map
<'ast
>>,
257 impl<'ast
> PrinterSupport
<'ast
> for HygieneAnnotation
<'ast
> {
258 fn sess
<'a
>(&'a
self) -> &'a Session { &self.sess }
260 fn ast_map
<'a
>(&'a
self) -> Option
<&'a ast_map
::Map
<'ast
>> {
261 self.ast_map
.as_ref()
264 fn pp_ann
<'a
>(&'a
self) -> &'a pprust
::PpAnn { self }
267 impl<'ast
> pprust
::PpAnn
for HygieneAnnotation
<'ast
> {
269 s
: &mut pprust
::State
,
270 node
: pprust
::AnnNode
) -> io
::Result
<()> {
272 pprust
::NodeIdent(&ast
::Ident { name: ast::Name(nm), ctxt }
) => {
273 try
!(pp
::space(&mut s
.s
));
274 // FIXME #16420: this doesn't display the connections
275 // between syntax contexts
276 s
.synth_comment(format
!("{}#{}", nm
, ctxt
))
278 pprust
::NodeName(&ast
::Name(nm
)) => {
279 try
!(pp
::space(&mut s
.s
));
280 s
.synth_comment(nm
.to_string())
288 struct TypedAnnotation
<'a
, 'tcx
: 'a
> {
289 tcx
: &'a ty
::ctxt
<'tcx
>,
292 impl<'b
, 'tcx
> PrinterSupport
<'tcx
> for TypedAnnotation
<'b
, 'tcx
> {
293 fn sess
<'a
>(&'a
self) -> &'a Session { &self.tcx.sess }
295 fn ast_map
<'a
>(&'a
self) -> Option
<&'a ast_map
::Map
<'tcx
>> {
299 fn pp_ann
<'a
>(&'a
self) -> &'a pprust
::PpAnn { self }
302 impl<'a
, 'tcx
> pprust
::PpAnn
for TypedAnnotation
<'a
, 'tcx
> {
304 s
: &mut pprust
::State
,
305 node
: pprust
::AnnNode
) -> io
::Result
<()> {
307 pprust
::NodeExpr(_
) => s
.popen(),
312 s
: &mut pprust
::State
,
313 node
: pprust
::AnnNode
) -> io
::Result
<()> {
315 pprust
::NodeExpr(expr
) => {
316 try
!(pp
::space(&mut s
.s
));
317 try
!(pp
::word(&mut s
.s
, "as"));
318 try
!(pp
::space(&mut s
.s
));
319 try
!(pp
::word(&mut s
.s
,
320 &self.tcx
.expr_ty(expr
).to_string()));
328 fn gather_flowgraph_variants(sess
: &Session
) -> Vec
<borrowck_dot
::Variant
> {
329 let print_loans
= sess
.opts
.debugging_opts
.flowgraph_print_loans
;
330 let print_moves
= sess
.opts
.debugging_opts
.flowgraph_print_moves
;
331 let print_assigns
= sess
.opts
.debugging_opts
.flowgraph_print_assigns
;
332 let print_all
= sess
.opts
.debugging_opts
.flowgraph_print_all
;
333 let mut variants
= Vec
::new();
334 if print_all
|| print_loans
{
335 variants
.push(borrowck_dot
::Loans
);
337 if print_all
|| print_moves
{
338 variants
.push(borrowck_dot
::Moves
);
340 if print_all
|| print_assigns
{
341 variants
.push(borrowck_dot
::Assigns
);
346 #[derive(Clone, Debug)]
347 pub enum UserIdentifiedItem
{
348 ItemViaNode(ast
::NodeId
),
349 ItemViaPath(Vec
<String
>),
352 impl FromStr
for UserIdentifiedItem
{
354 fn from_str(s
: &str) -> Result
<UserIdentifiedItem
, ()> {
355 Ok(s
.parse().map(ItemViaNode
).unwrap_or_else(|_
| {
356 ItemViaPath(s
.split("::").map(|s
| s
.to_string()).collect())
361 enum NodesMatchingUII
<'a
, 'ast
: 'a
> {
362 NodesMatchingDirect(option
::IntoIter
<ast
::NodeId
>),
363 NodesMatchingSuffix(ast_map
::NodesMatchingSuffix
<'a
, 'ast
>),
366 impl<'a
, 'ast
> Iterator
for NodesMatchingUII
<'a
, 'ast
> {
367 type Item
= ast
::NodeId
;
369 fn next(&mut self) -> Option
<ast
::NodeId
> {
371 &mut NodesMatchingDirect(ref mut iter
) => iter
.next(),
372 &mut NodesMatchingSuffix(ref mut iter
) => iter
.next(),
377 impl UserIdentifiedItem
{
378 fn reconstructed_input(&self) -> String
{
380 ItemViaNode(node_id
) => node_id
.to_string(),
381 ItemViaPath(ref parts
) => parts
.join("::"),
385 fn all_matching_node_ids
<'a
, 'ast
>(&'a
self, map
: &'a ast_map
::Map
<'ast
>)
386 -> NodesMatchingUII
<'a
, 'ast
> {
388 ItemViaNode(node_id
) =>
389 NodesMatchingDirect(Some(node_id
).into_iter()),
390 ItemViaPath(ref parts
) =>
391 NodesMatchingSuffix(map
.nodes_matching_suffix(&parts
[..])),
395 fn to_one_node_id(self, user_option
: &str, sess
: &Session
, map
: &ast_map
::Map
) -> ast
::NodeId
{
396 let fail_because
= |is_wrong_because
| -> ast
::NodeId
{
398 format
!("{} needs NodeId (int) or unique \
399 path suffix (b::c::d); got {}, which {}",
401 self.reconstructed_input(),
403 sess
.fatal(&message
[..])
406 let mut saw_node
= ast
::DUMMY_NODE_ID
;
408 for node
in self.all_matching_node_ids(map
) {
412 fail_because("does not resolve uniquely");
416 fail_because("does not resolve to any item");
424 fn needs_ast_map(ppm
: &PpMode
, opt_uii
: &Option
<UserIdentifiedItem
>) -> bool
{
426 PpmSource(PpmNormal
) |
427 PpmSource(PpmEveryBodyLoops
) |
428 PpmSource(PpmIdentified
) => opt_uii
.is_some(),
430 PpmSource(PpmExpanded
) |
431 PpmSource(PpmExpandedIdentified
) |
432 PpmSource(PpmExpandedHygiene
) |
433 PpmSource(PpmTyped
) |
434 PpmFlowGraph(_
) => true
438 fn needs_expansion(ppm
: &PpMode
) -> bool
{
440 PpmSource(PpmNormal
) |
441 PpmSource(PpmEveryBodyLoops
) |
442 PpmSource(PpmIdentified
) => false,
444 PpmSource(PpmExpanded
) |
445 PpmSource(PpmExpandedIdentified
) |
446 PpmSource(PpmExpandedHygiene
) |
447 PpmSource(PpmTyped
) |
448 PpmFlowGraph(_
) => true
452 struct ReplaceBodyWithLoop
{
453 within_static_or_const
: bool
,
456 impl ReplaceBodyWithLoop
{
457 fn new() -> ReplaceBodyWithLoop
{
458 ReplaceBodyWithLoop { within_static_or_const: false }
462 impl fold
::Folder
for ReplaceBodyWithLoop
{
463 fn fold_item_underscore(&mut self, i
: ast
::Item_
) -> ast
::Item_
{
465 ast
::ItemStatic(..) | ast
::ItemConst(..) => {
466 self.within_static_or_const
= true;
467 let ret
= fold
::noop_fold_item_underscore(i
, self);
468 self.within_static_or_const
= false;
472 fold
::noop_fold_item_underscore(i
, self)
477 fn fold_trait_item(&mut self, i
: P
<ast
::TraitItem
>) -> SmallVector
<P
<ast
::TraitItem
>> {
479 ast
::ConstTraitItem(..) => {
480 self.within_static_or_const
= true;
481 let ret
= fold
::noop_fold_trait_item(i
, self);
482 self.within_static_or_const
= false;
485 _
=> fold
::noop_fold_trait_item(i
, self),
489 fn fold_impl_item(&mut self, i
: P
<ast
::ImplItem
>) -> SmallVector
<P
<ast
::ImplItem
>> {
491 ast
::ConstImplItem(..) => {
492 self.within_static_or_const
= true;
493 let ret
= fold
::noop_fold_impl_item(i
, self);
494 self.within_static_or_const
= false;
497 _
=> fold
::noop_fold_impl_item(i
, self),
501 fn fold_block(&mut self, b
: P
<ast
::Block
>) -> P
<ast
::Block
> {
502 fn expr_to_block(rules
: ast
::BlockCheckMode
,
503 e
: Option
<P
<ast
::Expr
>>) -> P
<ast
::Block
> {
506 stmts
: vec
![], rules
: rules
,
507 id
: ast
::DUMMY_NODE_ID
, span
: codemap
::DUMMY_SP
,
511 if !self.within_static_or_const
{
513 let empty_block
= expr_to_block(ast
::DefaultBlock
, None
);
514 let loop_expr
= P(ast
::Expr
{
515 node
: ast
::ExprLoop(empty_block
, None
),
516 id
: ast
::DUMMY_NODE_ID
, span
: codemap
::DUMMY_SP
519 expr_to_block(b
.rules
, Some(loop_expr
))
522 fold
::noop_fold_block(b
, self)
526 // in general the pretty printer processes unexpanded code, so
527 // we override the default `fold_mac` method which panics.
528 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
529 fold
::noop_fold_mac(mac
, self)
533 pub fn pretty_print_input(sess
: Session
,
534 cfg
: ast
::CrateConfig
,
537 opt_uii
: Option
<UserIdentifiedItem
>,
538 ofile
: Option
<PathBuf
>) {
539 let krate
= driver
::phase_1_parse_input(&sess
, cfg
, input
);
541 let krate
= if let PpmSource(PpmEveryBodyLoops
) = ppm
{
542 let mut fold
= ReplaceBodyWithLoop
::new();
543 fold
.fold_crate(krate
)
548 let id
= link
::find_crate_name(Some(&sess
), &krate
.attrs
, input
);
550 let is_expanded
= needs_expansion(&ppm
);
551 let compute_ast_map
= needs_ast_map(&ppm
, &opt_uii
);
552 let krate
= if compute_ast_map
{
553 match driver
::phase_2_configure_and_expand(&sess
, krate
, &id
[..], None
) {
561 let mut forest
= ast_map
::Forest
::new(krate
);
562 let arenas
= ty
::CtxtArenas
::new();
564 let (krate
, ast_map
) = if compute_ast_map
{
565 let map
= driver
::assign_node_ids_and_map(&sess
, &mut forest
);
566 (map
.krate(), Some(map
))
568 (forest
.krate(), None
)
571 let src_name
= driver
::source_name(input
);
572 let src
= sess
.codemap().get_filemap(&src_name
[..])
578 let mut rdr
= &src
[..];
580 let mut out
= Vec
::new();
582 match (ppm
, opt_uii
) {
583 (PpmSource(s
), None
) => {
584 let out
: &mut Write
= &mut out
;
585 s
.call_with_pp_support(
586 sess
, ast_map
, &arenas
, id
, box out
, |annotation
, out
| {
587 debug
!("pretty printing source code {:?}", s
);
588 let sess
= annotation
.sess();
589 pprust
::print_crate(sess
.codemap(),
592 src_name
.to_string(),
600 (PpmSource(s
), Some(uii
)) => {
601 let out
: &mut Write
= &mut out
;
602 s
.call_with_pp_support(
603 sess
, ast_map
, &arenas
, id
, (out
,uii
), |annotation
, (out
,uii
)| {
604 debug
!("pretty printing source code {:?}", s
);
605 let sess
= annotation
.sess();
606 let ast_map
= annotation
.ast_map()
607 .expect("--pretty missing ast_map");
609 pprust
::State
::new_from_input(sess
.codemap(),
611 src_name
.to_string(),
616 for node_id
in uii
.all_matching_node_ids(ast_map
) {
617 let node
= ast_map
.get(node_id
);
618 try
!(pp_state
.print_node(&node
));
619 try
!(pp
::space(&mut pp_state
.s
));
620 try
!(pp_state
.synth_comment(ast_map
.path_to_string(node_id
)));
621 try
!(pp
::hardbreak(&mut pp_state
.s
));
623 pp
::eof(&mut pp_state
.s
)
627 (PpmFlowGraph(mode
), opt_uii
) => {
628 debug
!("pretty printing flow graph for {:?}", opt_uii
);
629 let uii
= opt_uii
.unwrap_or_else(|| {
630 sess
.fatal(&format
!("`pretty flowgraph=..` needs NodeId (int) or
631 unique path suffix (b::c::d)"))
634 let ast_map
= ast_map
.expect("--pretty flowgraph missing ast_map");
635 let nodeid
= uii
.to_one_node_id("--pretty", &sess
, &ast_map
);
637 let node
= ast_map
.find(nodeid
).unwrap_or_else(|| {
638 sess
.fatal(&format
!("--pretty flowgraph couldn't find id: {}",
642 let code
= blocks
::Code
::from_node(node
);
643 let out
: &mut Write
= &mut out
;
646 let variants
= gather_flowgraph_variants(&sess
);
647 driver
::phase_3_run_analysis_passes(sess
,
651 resolve
::MakeGlobMap
::No
,
653 print_flowgraph(variants
, tcx
, code
, mode
, out
)
657 let message
= format
!("--pretty=flowgraph needs \
658 block, fn, or method; got {:?}",
661 // point to what was found, if there's an
663 match ast_map
.opt_span(nodeid
) {
664 Some(sp
) => sess
.span_fatal(sp
, &message
[..]),
665 None
=> sess
.fatal(&message
[..])
673 None
=> print
!("{}", String
::from_utf8(out
).unwrap()),
675 match File
::create(&p
) {
676 Ok(mut w
) => w
.write_all(&out
).unwrap(),
677 Err(e
) => panic
!("print-print failed to open {} due to {}",
684 fn print_flowgraph
<W
: Write
>(variants
: Vec
<borrowck_dot
::Variant
>,
687 mode
: PpFlowGraphMode
,
688 mut out
: W
) -> io
::Result
<()> {
689 let cfg
= match code
{
690 blocks
::BlockCode(block
) => cfg
::CFG
::new(tcx
, &*block
),
691 blocks
::FnLikeCode(fn_like
) => cfg
::CFG
::new(tcx
, &*fn_like
.body()),
693 let labelled_edges
= mode
!= PpFlowGraphMode
::UnlabelledEdges
;
694 let lcfg
= LabelledCFG
{
697 name
: format
!("node_{}", code
.id()),
698 labelled_edges
: labelled_edges
,
702 _
if variants
.is_empty() => {
703 let r
= dot
::render(&lcfg
, &mut out
);
704 return expand_err_details(r
);
706 blocks
::BlockCode(_
) => {
707 tcx
.sess
.err("--pretty flowgraph with -Z flowgraph-print \
708 annotations requires fn-like node id.");
711 blocks
::FnLikeCode(fn_like
) => {
712 let fn_parts
= borrowck
::FnPartsWithCFG
::from_fn_like(&fn_like
, &cfg
);
713 let (bccx
, analysis_data
) =
714 borrowck
::build_borrowck_dataflow_data_for_fn(tcx
, fn_parts
);
716 let lcfg
= borrowck_dot
::DataflowLabeller
{
719 borrowck_ctxt
: &bccx
,
720 analysis_data
: &analysis_data
,
722 let r
= dot
::render(&lcfg
, &mut out
);
723 return expand_err_details(r
);
727 fn expand_err_details(r
: io
::Result
<()>) -> io
::Result
<()> {
729 io
::Error
::new(io
::ErrorKind
::Other
,
730 &format
!("graphviz::render failed: {}", ioerr
)[..])