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 /// This module provides linkage between rustc::middle::graph and
12 /// libgraphviz traits.
14 // For clarity, rename the graphviz crate locally to dot.
16 use graphviz
::IntoCow
;
20 use hir
::map
as ast_map
;
23 pub type Node
<'a
> = (cfg
::CFGIndex
, &'a cfg
::CFGNode
);
24 pub type Edge
<'a
> = &'a cfg
::CFGEdge
;
26 pub struct LabelledCFG
<'a
, 'ast
: 'a
> {
27 pub ast_map
: &'a ast_map
::Map
<'ast
>,
28 pub cfg
: &'a cfg
::CFG
,
30 /// `labelled_edges` controls whether we emit labels on the edges
31 pub labelled_edges
: bool
,
34 fn replace_newline_with_backslash_l(s
: String
) -> String
{
35 // Replacing newlines with \\l causes each line to be left-aligned,
36 // improving presentation of (long) pretty-printed expressions.
38 let mut s
= s
.replace("\n", "\\l");
39 // Apparently left-alignment applies to the line that precedes
40 // \l, not the line that follows; so, add \l at end of string
41 // if not already present, ensuring last line gets left-aligned
43 let mut last_two
: Vec
<_
> =
44 s
.chars().rev().take(2).collect();
46 if last_two
!= ['
\\'
, 'l'
] {
55 impl<'a
, 'ast
> dot
::Labeller
<'a
> for LabelledCFG
<'a
, 'ast
> {
58 fn graph_id(&'a
self) -> dot
::Id
<'a
> { dot::Id::new(&self.name[..]).unwrap() }
60 fn node_id(&'a
self, &(i
,_
): &Node
<'a
>) -> dot
::Id
<'a
> {
61 dot
::Id
::new(format
!("N{}", i
.node_id())).unwrap()
64 fn node_label(&'a
self, &(i
, n
): &Node
<'a
>) -> dot
::LabelText
<'a
> {
65 if i
== self.cfg
.entry
{
66 dot
::LabelText
::LabelStr("entry".into_cow())
67 } else if i
== self.cfg
.exit
{
68 dot
::LabelText
::LabelStr("exit".into_cow())
69 } else if n
.data
.id() == ast
::DUMMY_NODE_ID
{
70 dot
::LabelText
::LabelStr("(dummy_node)".into_cow())
72 let s
= self.ast_map
.node_to_string(n
.data
.id());
73 // left-aligns the lines
74 let s
= replace_newline_with_backslash_l(s
);
75 dot
::LabelText
::EscStr(s
.into_cow())
79 fn edge_label(&self, e
: &Edge
<'a
>) -> dot
::LabelText
<'a
> {
80 let mut label
= String
::new();
81 if !self.labelled_edges
{
82 return dot
::LabelText
::EscStr(label
.into_cow());
84 let mut put_one
= false;
85 for (i
, &node_id
) in e
.data
.exiting_scopes
.iter().enumerate() {
87 label
.push_str(",\\l");
91 let s
= self.ast_map
.node_to_string(node_id
);
92 // left-aligns the lines
93 let s
= replace_newline_with_backslash_l(s
);
94 label
.push_str(&format
!("exiting scope_{} {}",
98 dot
::LabelText
::EscStr(label
.into_cow())
102 impl<'a
> dot
::GraphWalk
<'a
> for &'a cfg
::CFG
{
103 type Node
= Node
<'a
>;
104 type Edge
= Edge
<'a
>;
105 fn nodes(&'a
self) -> dot
::Nodes
<'a
, Node
<'a
>> {
106 let mut v
= Vec
::new();
107 self.graph
.each_node(|i
, nd
| { v.push((i, nd)); true }
);
110 fn edges(&'a
self) -> dot
::Edges
<'a
, Edge
<'a
>> {
111 self.graph
.all_edges().iter().collect()
113 fn source(&'a
self, edge
: &Edge
<'a
>) -> Node
<'a
> {
114 let i
= edge
.source();
115 (i
, self.graph
.node(i
))
117 fn target(&'a
self, edge
: &Edge
<'a
>) -> Node
<'a
> {
118 let i
= edge
.target();
119 (i
, self.graph
.node(i
))
123 impl<'a
, 'ast
> dot
::GraphWalk
<'a
> for LabelledCFG
<'a
, 'ast
>
125 type Node
= Node
<'a
>;
126 type Edge
= Edge
<'a
>;
127 fn nodes(&'a
self) -> dot
::Nodes
<'a
, Node
<'a
>> { self.cfg.nodes() }
128 fn edges(&'a
self) -> dot
::Edges
<'a
, Edge
<'a
>> { self.cfg.edges() }
129 fn source(&'a
self, edge
: &Edge
<'a
>) -> Node
<'a
> { self.cfg.source(edge) }
130 fn target(&'a
self, edge
: &Edge
<'a
>) -> Node
<'a
> { self.cfg.target(edge) }