1 use rustc_data_structures
::graph
::{self, iterate}
;
2 use rustc_graphviz
as dot
;
3 use rustc_middle
::ty
::TyCtxt
;
4 use std
::io
::{self, Write}
;
6 pub struct GraphvizWriter
<
8 G
: graph
::DirectedGraph
+ graph
::WithSuccessors
+ graph
::WithStartNode
+ graph
::WithNumNodes
,
9 NodeContentFn
: Fn(<G
as graph
::DirectedGraph
>::Node
) -> Vec
<String
>,
10 EdgeLabelsFn
: Fn(<G
as graph
::DirectedGraph
>::Node
) -> Vec
<String
>,
14 graphviz_name
: String
,
15 graph_label
: Option
<String
>,
16 node_content_fn
: NodeContentFn
,
17 edge_labels_fn
: EdgeLabelsFn
,
22 G
: graph
::DirectedGraph
+ graph
::WithSuccessors
+ graph
::WithStartNode
+ graph
::WithNumNodes
,
23 NodeContentFn
: Fn(<G
as graph
::DirectedGraph
>::Node
) -> Vec
<String
>,
24 EdgeLabelsFn
: Fn(<G
as graph
::DirectedGraph
>::Node
) -> Vec
<String
>,
25 > GraphvizWriter
<'a
, G
, NodeContentFn
, EdgeLabelsFn
>
30 node_content_fn
: NodeContentFn
,
31 edge_labels_fn
: EdgeLabelsFn
,
36 graphviz_name
: graphviz_name
.to_owned(),
43 pub fn set_graph_label(&mut self, graph_label
: &str) {
44 self.graph_label
= Some(graph_label
.to_owned());
47 /// Write a graphviz DOT of the graph
48 pub fn write_graphviz
<'tcx
, W
>(&self, tcx
: TyCtxt
<'tcx
>, w
: &mut W
) -> io
::Result
<()>
52 let kind
= if self.is_subgraph { "subgraph" }
else { "digraph" }
;
53 let cluster
= if self.is_subgraph { "cluster_" }
else { "" }
; // Print border around graph
54 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
55 // prepend "Mir_" to the graphviz_safe_def_name(def_id)
56 writeln
!(w
, "{} {}{} {{", kind
, cluster
, self.graphviz_name
)?
;
58 // Global graph properties
59 let font
= format
!(r
#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
60 let mut graph_attrs
= vec
![&font
[..]];
61 let mut content_attrs
= vec
![&font
[..]];
63 let dark_mode
= tcx
.sess
.opts
.unstable_opts
.graphviz_dark_mode
;
65 graph_attrs
.push(r
#"bgcolor="black""#);
66 graph_attrs
.push(r
#"fontcolor="white""#);
67 content_attrs
.push(r
#"color="white""#);
68 content_attrs
.push(r
#"fontcolor="white""#);
71 writeln
!(w
, r
#" graph [{}];"#, graph_attrs.join(" "))?;
72 let content_attrs_str
= content_attrs
.join(" ");
73 writeln
!(w
, r
#" node [{}];"#, content_attrs_str)?;
74 writeln
!(w
, r
#" edge [{}];"#, content_attrs_str)?;
77 if let Some(graph_label
) = &self.graph_label
{
78 self.write_graph_label(graph_label
, w
)?
;
82 for node
in iterate
::post_order_from(self.graph
, self.graph
.start_node()) {
83 self.write_node(node
, dark_mode
, w
)?
;
87 for source
in iterate
::post_order_from(self.graph
, self.graph
.start_node()) {
88 self.write_edges(source
, w
)?
;
93 /// Write a graphviz DOT node for the given node.
94 pub fn write_node
<W
>(&self, node
: G
::Node
, dark_mode
: bool
, w
: &mut W
) -> io
::Result
<()>
98 // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
99 write
!(w
, r
#" {} [shape="none", label=<"#, self.node(node))?;
101 write
!(w
, r
#"<table border="0" cellborder="1" cellspacing="0">"#)?;
103 // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
104 // we need generic way to know if node header should have a different color. For example,
107 // let (blk, bgcolor) = if data.is_cleanup {
108 // let color = if dark_mode { "royalblue" } else { "lightblue" };
109 // (format!("{:?} (cleanup)", node), color)
111 // let color = if dark_mode { "dimgray" } else { "gray" };
112 // (format!("{:?}", node), color)
114 let color
= if dark_mode { "dimgray" }
else { "gray" }
;
115 let (blk
, bgcolor
) = (format
!("{:?}", node
), color
);
118 r
#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
119 attrs
= r
#"align="center""#,
125 for section
in (self.node_content_fn
)(node
) {
128 r
#"<tr><td align="left" balign="left">{}</td></tr>"#,
129 dot
::escape_html(§ion
).replace('
\n'
, "<br/>")
134 write
!(w
, "</table>")?
;
136 // Close the node label and the node itself.
140 /// Write graphviz DOT edges with labels between the given node and all of its successors.
141 fn write_edges
<W
>(&self, source
: G
::Node
, w
: &mut W
) -> io
::Result
<()>
145 let edge_labels
= (self.edge_labels_fn
)(source
);
146 for (index
, target
) in self.graph
.successors(source
).enumerate() {
147 let src
= self.node(source
);
148 let trg
= self.node(target
);
149 let escaped_edge_label
= if let Some(edge_label
) = edge_labels
.get(index
) {
150 dot
::escape_html(edge_label
).replace('
\n'
, r
#"<br align="left"/>"#)
154 writeln
!(w
, r
#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?;
159 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
160 /// will appear below the graph.
161 fn write_graph_label
<W
>(&self, label
: &str, w
: &mut W
) -> io
::Result
<()>
165 let lines
= label
.split('
\n'
).map(|s
| dot
::escape_html(s
)).collect
::<Vec
<_
>>();
166 let escaped_label
= lines
.join(r
#"<br align="left"/>"#);
167 writeln
!(w
, r
#" label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
170 fn node(&self, node
: G
::Node
) -> String
{
171 format
!("{:?}__{}", node
, self.graphviz_name
)