1use std::io::{self, Write};
2
3use rustc_data_structures::graph::{self, iterate};
4use rustc_graphviz as dot;
5
6use crate::ty::TyCtxt;
7
8pub struct GraphvizWriter<
9 'a,
10 G: graph::DirectedGraph + graph::Successors + graph::StartNode,
11 NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
12 EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
13> {
14 graph: &'a G,
15 is_subgraph: bool,
16 graphviz_name: String,
17 graph_label: Option<String>,
18 node_content_fn: NodeContentFn,
19 edge_labels_fn: EdgeLabelsFn,
20}
21
22impl<
23 'a,
24 G: graph::DirectedGraph + graph::Successors + graph::StartNode,
25 NodeContentFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
26 EdgeLabelsFn: Fn(<G as graph::DirectedGraph>::Node) -> Vec<String>,
27> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn>
28{
29 pub fn new(
30 graph: &'a G,
31 graphviz_name: &str,
32 node_content_fn: NodeContentFn,
33 edge_labels_fn: EdgeLabelsFn,
34 ) -> Self {
35 Self {
36 graph,
37 is_subgraph: false,
38 graphviz_name: graphviz_name.to_owned(),
39 graph_label: None,
40 node_content_fn,
41 edge_labels_fn,
42 }
43 }
44
45 pub fn set_graph_label(&mut self, graph_label: &str) {
46 self.graph_label = Some(graph_label.to_owned());
47 }
48
49 pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()>
51 where
52 W: Write,
53 {
54 let kind = if self.is_subgraph { "subgraph" } else { "digraph" };
55 let cluster = if self.is_subgraph { "cluster_" } else { "" }; w.write_fmt(format_args!("{0} {1}{2} {{\n", kind, cluster,
self.graphviz_name))writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
59
60 let font = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("fontname=\"{0}\"",
tcx.sess.opts.unstable_opts.graphviz_font))
})format!(r#"fontname="{}""#, tcx.sess.opts.unstable_opts.graphviz_font);
62 let mut graph_attrs = <[_]>::into_vec(::alloc::boxed::box_new([&font[..]]))vec![&font[..]];
63 let mut content_attrs = <[_]>::into_vec(::alloc::boxed::box_new([&font[..]]))vec![&font[..]];
64
65 let dark_mode = tcx.sess.opts.unstable_opts.graphviz_dark_mode;
66 if dark_mode {
67 graph_attrs.push(r#"bgcolor="black""#);
68 graph_attrs.push(r#"fontcolor="white""#);
69 content_attrs.push(r#"color="white""#);
70 content_attrs.push(r#"fontcolor="white""#);
71 }
72
73 w.write_fmt(format_args!(" graph [{0}];\n", graph_attrs.join(" ")))writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?;
74 let content_attrs_str = content_attrs.join(" ");
75 w.write_fmt(format_args!(" node [{0}];\n", content_attrs_str))writeln!(w, r#" node [{content_attrs_str}];"#)?;
76 w.write_fmt(format_args!(" edge [{0}];\n", content_attrs_str))writeln!(w, r#" edge [{content_attrs_str}];"#)?;
77
78 if let Some(graph_label) = &self.graph_label {
80 self.write_graph_label(graph_label, w)?;
81 }
82
83 for node in iterate::post_order_from(self.graph, self.graph.start_node()) {
85 self.write_node(node, dark_mode, w)?;
86 }
87
88 for source in iterate::post_order_from(self.graph, self.graph.start_node()) {
90 self.write_edges(source, w)?;
91 }
92 w.write_fmt(format_args!("}}\n"))writeln!(w, "}}")
93 }
94
95 pub fn write_node<W>(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()>
97 where
98 W: Write,
99 {
100 w.write_fmt(format_args!(" {0} [shape=\"none\", label=<", self.node(node)))write!(w, r#" {} [shape="none", label=<"#, self.node(node))?;
102
103 w.write_fmt(format_args!("<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">"))write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
104
105 let color = if dark_mode { "dimgray" } else { "gray" };
117 let (blk, bgcolor) = (::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", node))
})format!("{node:?}"), color);
118 w.write_fmt(format_args!("<tr><td bgcolor=\"{3}\" {0} colspan=\"{1}\">{2}</td></tr>",
r#"align="center""#, 1, blk, bgcolor))write!(
119 w,
120 r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
121 attrs = r#"align="center""#,
122 colspan = 1,
123 blk = blk,
124 bgcolor = bgcolor
125 )?;
126
127 for section in (self.node_content_fn)(node) {
128 w.write_fmt(format_args!("<tr><td align=\"left\" balign=\"left\">{0}</td></tr>",
dot::escape_html(§ion)))write!(
129 w,
130 r#"<tr><td align="left" balign="left">{}</td></tr>"#,
131 dot::escape_html(§ion)
132 )?;
133 }
134
135 w.write_fmt(format_args!("</table>"))write!(w, "</table>")?;
137
138 w.write_fmt(format_args!(">];\n"))writeln!(w, ">];")
140 }
141
142 fn write_edges<W>(&self, source: G::Node, w: &mut W) -> io::Result<()>
144 where
145 W: Write,
146 {
147 let edge_labels = (self.edge_labels_fn)(source);
148 for (index, target) in self.graph.successors(source).enumerate() {
149 let src = self.node(source);
150 let trg = self.node(target);
151 let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
152 dot::escape_html(edge_label)
153 } else {
154 "".to_owned()
155 };
156 w.write_fmt(format_args!(" {0} -> {1} [label=<{2}>];\n", src, trg,
escaped_edge_label))writeln!(w, r#" {src} -> {trg} [label=<{escaped_edge_label}>];"#)?;
157 }
158 Ok(())
159 }
160
161 fn write_graph_label<W>(&self, label: &str, w: &mut W) -> io::Result<()>
164 where
165 W: Write,
166 {
167 let escaped_label = dot::escape_html(label);
168 w.write_fmt(format_args!(" label=<<br/><br/>{0}<br align=\"left\"/><br/><br/><br/>>;\n",
escaped_label))writeln!(w, r#" label=<<br/><br/>{escaped_label}<br align="left"/><br/><br/><br/>>;"#)
169 }
170
171 fn node(&self, node: G::Node) -> String {
172 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}__{1}", node,
self.graphviz_name))
})format!("{:?}__{}", node, self.graphviz_name)
173 }
174}