]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | /// This module provides linkage between rustc::middle::graph and | |
12 | /// libgraphviz traits. | |
13 | ||
1a4d82fc JJ |
14 | // For clarity, rename the graphviz crate locally to dot. |
15 | use graphviz as dot; | |
9cc50fc6 | 16 | use graphviz::IntoCow; |
1a4d82fc JJ |
17 | |
18 | use syntax::ast; | |
1a4d82fc | 19 | |
e9174d1e | 20 | use front::map as ast_map; |
1a4d82fc JJ |
21 | use middle::cfg; |
22 | ||
23 | pub type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode); | |
24 | pub type Edge<'a> = &'a cfg::CFGEdge; | |
25 | ||
26 | pub struct LabelledCFG<'a, 'ast: 'a> { | |
27 | pub ast_map: &'a ast_map::Map<'ast>, | |
28 | pub cfg: &'a cfg::CFG, | |
29 | pub name: String, | |
85aaf69f SL |
30 | /// `labelled_edges` controls whether we emit labels on the edges |
31 | pub labelled_edges: bool, | |
1a4d82fc JJ |
32 | } |
33 | ||
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. | |
37 | if s.contains("\n") { | |
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 | |
42 | // as well. | |
43 | let mut last_two: Vec<_> = | |
44 | s.chars().rev().take(2).collect(); | |
45 | last_two.reverse(); | |
46 | if last_two != ['\\', 'l'] { | |
47 | s.push_str("\\l"); | |
48 | } | |
49 | s | |
50 | } else { | |
51 | s | |
52 | } | |
53 | } | |
54 | ||
55 | impl<'a, 'ast> dot::Labeller<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> { | |
c34b1796 | 56 | fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new(&self.name[..]).unwrap() } |
1a4d82fc JJ |
57 | |
58 | fn node_id(&'a self, &(i,_): &Node<'a>) -> dot::Id<'a> { | |
c34b1796 | 59 | dot::Id::new(format!("N{}", i.node_id())).unwrap() |
1a4d82fc JJ |
60 | } |
61 | ||
62 | fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> { | |
63 | if i == self.cfg.entry { | |
64 | dot::LabelText::LabelStr("entry".into_cow()) | |
65 | } else if i == self.cfg.exit { | |
66 | dot::LabelText::LabelStr("exit".into_cow()) | |
c34b1796 | 67 | } else if n.data.id() == ast::DUMMY_NODE_ID { |
1a4d82fc JJ |
68 | dot::LabelText::LabelStr("(dummy_node)".into_cow()) |
69 | } else { | |
c34b1796 | 70 | let s = self.ast_map.node_to_string(n.data.id()); |
1a4d82fc JJ |
71 | // left-aligns the lines |
72 | let s = replace_newline_with_backslash_l(s); | |
73 | dot::LabelText::EscStr(s.into_cow()) | |
74 | } | |
75 | } | |
76 | ||
77 | fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> { | |
78 | let mut label = String::new(); | |
85aaf69f SL |
79 | if !self.labelled_edges { |
80 | return dot::LabelText::EscStr(label.into_cow()); | |
81 | } | |
1a4d82fc JJ |
82 | let mut put_one = false; |
83 | for (i, &node_id) in e.data.exiting_scopes.iter().enumerate() { | |
84 | if put_one { | |
85 | label.push_str(",\\l"); | |
86 | } else { | |
87 | put_one = true; | |
88 | } | |
89 | let s = self.ast_map.node_to_string(node_id); | |
90 | // left-aligns the lines | |
91 | let s = replace_newline_with_backslash_l(s); | |
92 | label.push_str(&format!("exiting scope_{} {}", | |
93 | i, | |
c34b1796 | 94 | &s[..])); |
1a4d82fc JJ |
95 | } |
96 | dot::LabelText::EscStr(label.into_cow()) | |
97 | } | |
98 | } | |
99 | ||
100 | impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for &'a cfg::CFG { | |
101 | fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { | |
102 | let mut v = Vec::new(); | |
103 | self.graph.each_node(|i, nd| { v.push((i, nd)); true }); | |
104 | v.into_cow() | |
105 | } | |
106 | fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { | |
107 | self.graph.all_edges().iter().collect() | |
108 | } | |
109 | fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { | |
110 | let i = edge.source(); | |
111 | (i, self.graph.node(i)) | |
112 | } | |
113 | fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { | |
114 | let i = edge.target(); | |
115 | (i, self.graph.node(i)) | |
116 | } | |
117 | } | |
118 | ||
119 | impl<'a, 'ast> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for LabelledCFG<'a, 'ast> | |
120 | { | |
121 | fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } | |
122 | fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } | |
123 | fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } | |
124 | fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } | |
125 | } |