]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | /// This module provides linkage between rustc::middle::graph and |
2 | /// libgraphviz traits. | |
3 | ||
1a4d82fc JJ |
4 | // For clarity, rename the graphviz crate locally to dot. |
5 | use graphviz as dot; | |
6 | ||
9fa01778 XL |
7 | use crate::cfg; |
8 | use crate::hir; | |
9 | use crate::ty::TyCtxt; | |
1a4d82fc JJ |
10 | |
11 | pub type Node<'a> = (cfg::CFGIndex, &'a cfg::CFGNode); | |
12 | pub type Edge<'a> = &'a cfg::CFGEdge; | |
13 | ||
ea8adc8c XL |
14 | pub struct LabelledCFG<'a, 'tcx: 'a> { |
15 | pub tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1a4d82fc JJ |
16 | pub cfg: &'a cfg::CFG, |
17 | pub name: String, | |
85aaf69f SL |
18 | /// `labelled_edges` controls whether we emit labels on the edges |
19 | pub labelled_edges: bool, | |
1a4d82fc JJ |
20 | } |
21 | ||
ea8adc8c XL |
22 | impl<'a, 'tcx> LabelledCFG<'a, 'tcx> { |
23 | fn local_id_to_string(&self, local_id: hir::ItemLocalId) -> String { | |
24 | assert!(self.cfg.owner_def_id.is_local()); | |
0731742a XL |
25 | let node_id = self.tcx.hir().hir_to_node_id(hir::HirId { |
26 | owner: self.tcx.hir().def_index_to_hir_id(self.cfg.owner_def_id.index).owner, | |
ea8adc8c XL |
27 | local_id |
28 | }); | |
0731742a | 29 | let s = self.tcx.hir().node_to_string(node_id); |
ea8adc8c XL |
30 | |
31 | // Replacing newlines with \\l causes each line to be left-aligned, | |
32 | // improving presentation of (long) pretty-printed expressions. | |
33 | if s.contains("\n") { | |
34 | let mut s = s.replace("\n", "\\l"); | |
35 | // Apparently left-alignment applies to the line that precedes | |
36 | // \l, not the line that follows; so, add \l at end of string | |
37 | // if not already present, ensuring last line gets left-aligned | |
38 | // as well. | |
39 | let mut last_two: Vec<_> = | |
40 | s.chars().rev().take(2).collect(); | |
41 | last_two.reverse(); | |
42 | if last_two != ['\\', 'l'] { | |
43 | s.push_str("\\l"); | |
44 | } | |
45 | s | |
46 | } else { | |
47 | s | |
1a4d82fc | 48 | } |
1a4d82fc JJ |
49 | } |
50 | } | |
51 | ||
32a655c1 | 52 | impl<'a, 'hir> dot::Labeller<'a> for LabelledCFG<'a, 'hir> { |
54a0048b SL |
53 | type Node = Node<'a>; |
54 | type Edge = Edge<'a>; | |
c34b1796 | 55 | fn graph_id(&'a self) -> dot::Id<'a> { dot::Id::new(&self.name[..]).unwrap() } |
1a4d82fc JJ |
56 | |
57 | fn node_id(&'a self, &(i,_): &Node<'a>) -> dot::Id<'a> { | |
c34b1796 | 58 | dot::Id::new(format!("N{}", i.node_id())).unwrap() |
1a4d82fc JJ |
59 | } |
60 | ||
61 | fn node_label(&'a self, &(i, n): &Node<'a>) -> dot::LabelText<'a> { | |
62 | if i == self.cfg.entry { | |
0bf4aa26 | 63 | dot::LabelText::LabelStr("entry".into()) |
1a4d82fc | 64 | } else if i == self.cfg.exit { |
0bf4aa26 | 65 | dot::LabelText::LabelStr("exit".into()) |
ea8adc8c | 66 | } else if n.data.id() == hir::DUMMY_ITEM_LOCAL_ID { |
0bf4aa26 | 67 | dot::LabelText::LabelStr("(dummy_node)".into()) |
1a4d82fc | 68 | } else { |
ea8adc8c | 69 | let s = self.local_id_to_string(n.data.id()); |
0bf4aa26 | 70 | dot::LabelText::EscStr(s.into()) |
1a4d82fc JJ |
71 | } |
72 | } | |
73 | ||
74 | fn edge_label(&self, e: &Edge<'a>) -> dot::LabelText<'a> { | |
75 | let mut label = String::new(); | |
85aaf69f | 76 | if !self.labelled_edges { |
0bf4aa26 | 77 | return dot::LabelText::EscStr(label.into()); |
85aaf69f | 78 | } |
1a4d82fc | 79 | let mut put_one = false; |
ea8adc8c | 80 | for (i, &id) in e.data.exiting_scopes.iter().enumerate() { |
1a4d82fc JJ |
81 | if put_one { |
82 | label.push_str(",\\l"); | |
83 | } else { | |
84 | put_one = true; | |
85 | } | |
ea8adc8c | 86 | let s = self.local_id_to_string(id); |
1a4d82fc JJ |
87 | label.push_str(&format!("exiting scope_{} {}", |
88 | i, | |
c34b1796 | 89 | &s[..])); |
1a4d82fc | 90 | } |
0bf4aa26 | 91 | dot::LabelText::EscStr(label.into()) |
1a4d82fc JJ |
92 | } |
93 | } | |
94 | ||
54a0048b SL |
95 | impl<'a> dot::GraphWalk<'a> for &'a cfg::CFG { |
96 | type Node = Node<'a>; | |
97 | type Edge = Edge<'a>; | |
1a4d82fc | 98 | fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { |
a1dfa0c6 | 99 | let v: Vec<_> = self.graph.enumerated_nodes().collect(); |
0bf4aa26 | 100 | v.into() |
1a4d82fc JJ |
101 | } |
102 | fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { | |
103 | self.graph.all_edges().iter().collect() | |
104 | } | |
105 | fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { | |
106 | let i = edge.source(); | |
107 | (i, self.graph.node(i)) | |
108 | } | |
109 | fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { | |
110 | let i = edge.target(); | |
111 | (i, self.graph.node(i)) | |
112 | } | |
113 | } | |
114 | ||
32a655c1 | 115 | impl<'a, 'hir> dot::GraphWalk<'a> for LabelledCFG<'a, 'hir> |
1a4d82fc | 116 | { |
54a0048b SL |
117 | type Node = Node<'a>; |
118 | type Edge = Edge<'a>; | |
1a4d82fc JJ |
119 | fn nodes(&'a self) -> dot::Nodes<'a, Node<'a>> { self.cfg.nodes() } |
120 | fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() } | |
121 | fn source(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) } | |
122 | fn target(&'a self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) } | |
123 | } |