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