]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/util/generic_graphviz.rs
Auto merge of #79342 - CDirkx:ipaddr-const, r=oli-obk
[rust.git] / compiler / rustc_mir / src / util / generic_graphviz.rs
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};
5
6 pub struct GraphvizWriter<
7     'a,
8     G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
9     NodeContentFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
10     EdgeLabelsFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
11 > {
12     graph: &'a G,
13     is_subgraph: bool,
14     graphviz_name: String,
15     graph_label: Option<String>,
16     node_content_fn: NodeContentFn,
17     edge_labels_fn: EdgeLabelsFn,
18 }
19
20 impl<
21     'a,
22     G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes,
23     NodeContentFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
24     EdgeLabelsFn: Fn(<G as rustc_data_structures::graph::DirectedGraph>::Node) -> Vec<String>,
25 > GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn>
26 {
27     pub fn new(
28         graph: &'a G,
29         graphviz_name: &str,
30         node_content_fn: NodeContentFn,
31         edge_labels_fn: EdgeLabelsFn,
32     ) -> Self {
33         Self {
34             graph,
35             is_subgraph: false,
36             graphviz_name: graphviz_name.to_owned(),
37             graph_label: None,
38             node_content_fn,
39             edge_labels_fn,
40         }
41     }
42
43     pub fn new_subgraph(
44         graph: &'a G,
45         graphviz_name: &str,
46         node_content_fn: NodeContentFn,
47         edge_labels_fn: EdgeLabelsFn,
48     ) -> Self {
49         Self {
50             graph,
51             is_subgraph: true,
52             graphviz_name: graphviz_name.to_owned(),
53             graph_label: None,
54             node_content_fn,
55             edge_labels_fn,
56         }
57     }
58
59     pub fn set_graph_label(&mut self, graph_label: &str) {
60         self.graph_label = Some(graph_label.to_owned());
61     }
62
63     /// Write a graphviz DOT of the graph
64     pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()>
65     where
66         W: Write,
67     {
68         let kind = if self.is_subgraph { "subgraph" } else { "digraph" };
69         let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph
70         // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
71         // prepend "Mir_" to the graphviz_safe_def_name(def_id)
72         writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?;
73
74         // Global graph properties
75         let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font);
76         let mut graph_attrs = vec![&font[..]];
77         let mut content_attrs = vec![&font[..]];
78
79         let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode;
80         if dark_mode {
81             graph_attrs.push(r#"bgcolor="black""#);
82             graph_attrs.push(r#"fontcolor="white""#);
83             content_attrs.push(r#"color="white""#);
84             content_attrs.push(r#"fontcolor="white""#);
85         }
86
87         writeln!(w, r#"    graph [{}];"#, graph_attrs.join(" "))?;
88         let content_attrs_str = content_attrs.join(" ");
89         writeln!(w, r#"    node [{}];"#, content_attrs_str)?;
90         writeln!(w, r#"    edge [{}];"#, content_attrs_str)?;
91
92         // Graph label
93         if let Some(graph_label) = &self.graph_label {
94             self.write_graph_label(graph_label, w)?;
95         }
96
97         // Nodes
98         for node in iterate::post_order_from(self.graph, self.graph.start_node()) {
99             self.write_node(node, dark_mode, w)?;
100         }
101
102         // Edges
103         for source in iterate::post_order_from(self.graph, self.graph.start_node()) {
104             self.write_edges(source, w)?;
105         }
106         writeln!(w, "}}")
107     }
108
109     /// Write a graphviz DOT node for the given node.
110     pub fn write_node<W>(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()>
111     where
112         W: Write,
113     {
114         // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
115         write!(w, r#"    {} [shape="none", label=<"#, self.node(node))?;
116
117         write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
118
119         // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation,
120         // we need generic way to know if node header should have a different color. For example,
121         // for MIR:
122         //
123         // let (blk, bgcolor) = if data.is_cleanup {
124         //     let color = if dark_mode { "royalblue" } else { "lightblue" };
125         //     (format!("{:?} (cleanup)", node), color)
126         // } else {
127         //     let color = if dark_mode { "dimgray" } else { "gray" };
128         //     (format!("{:?}", node), color)
129         // };
130         let color = if dark_mode { "dimgray" } else { "gray" };
131         let (blk, bgcolor) = (format!("{:?}", node), color);
132         write!(
133             w,
134             r#"<tr><td bgcolor="{bgcolor}" {attrs} colspan="{colspan}">{blk}</td></tr>"#,
135             attrs = r#"align="center""#,
136             colspan = 1,
137             blk = blk,
138             bgcolor = bgcolor
139         )?;
140
141         for section in (self.node_content_fn)(node) {
142             write!(
143                 w,
144                 r#"<tr><td align="left" balign="left">{}</td></tr>"#,
145                 dot::escape_html(&section).replace("\n", "<br/>")
146             )?;
147         }
148
149         // Close the table
150         write!(w, "</table>")?;
151
152         // Close the node label and the node itself.
153         writeln!(w, ">];")
154     }
155
156     /// Write graphviz DOT edges with labels between the given node and all of its successors.
157     fn write_edges<W>(&self, source: G::Node, w: &mut W) -> io::Result<()>
158     where
159         W: Write,
160     {
161         let edge_labels = (self.edge_labels_fn)(source);
162         for (index, target) in self.graph.successors(source).enumerate() {
163             let src = self.node(source);
164             let trg = self.node(target);
165             let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) {
166                 dot::escape_html(edge_label).replace("\n", r#"<br align="left"/>"#)
167             } else {
168                 "".to_owned()
169             };
170             writeln!(w, r#"    {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?;
171         }
172         Ok(())
173     }
174
175     /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
176     /// will appear below the graph.
177     fn write_graph_label<W>(&self, label: &str, w: &mut W) -> io::Result<()>
178     where
179         W: Write,
180     {
181         let lines = label.split('\n').map(|s| dot::escape_html(s)).collect::<Vec<_>>();
182         let escaped_label = lines.join(r#"<br align="left"/>"#);
183         writeln!(w, r#"    label=<<br/><br/>{}<br align="left"/><br/><br/><br/>>;"#, escaped_label)
184     }
185
186     fn node(&self, node: G::Node) -> String {
187         format!("{:?}__{}", node, self.graphviz_name)
188     }
189 }