]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/graphviz.rs
Rollup merge of #69620 - thekuom:doc/61137-add-long-error-code-e0719, r=davidtwco
[rust.git] / src / librustc_mir / util / graphviz.rs
1 use rustc::mir::*;
2 use rustc::ty::TyCtxt;
3 use rustc_hir::def_id::DefId;
4 use rustc_index::vec::Idx;
5 use std::fmt::Debug;
6 use std::io::{self, Write};
7
8 use super::pretty::dump_mir_def_ids;
9
10 /// Write a graphviz DOT graph of a list of MIRs.
11 pub fn write_mir_graphviz<W>(tcx: TyCtxt<'_>, single: Option<DefId>, w: &mut W) -> io::Result<()>
12 where
13     W: Write,
14 {
15     let def_ids = dump_mir_def_ids(tcx, single);
16
17     let use_subgraphs = def_ids.len() > 1;
18     if use_subgraphs {
19         writeln!(w, "digraph __crate__ {{")?;
20     }
21
22     for def_id in def_ids {
23         let body = &tcx.optimized_mir(def_id);
24         write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?;
25     }
26
27     if use_subgraphs {
28         writeln!(w, "}}")?;
29     }
30
31     Ok(())
32 }
33
34 // Must match `[0-9A-Za-z_]*`. This does not appear in the rendered graph, so
35 // it does not have to be user friendly.
36 pub fn graphviz_safe_def_name(def_id: DefId) -> String {
37     format!("{}_{}", def_id.krate.index(), def_id.index.index(),)
38 }
39
40 /// Write a graphviz DOT graph of the MIR.
41 pub fn write_mir_fn_graphviz<'tcx, W>(
42     tcx: TyCtxt<'tcx>,
43     def_id: DefId,
44     body: &Body<'_>,
45     subgraph: bool,
46     w: &mut W,
47 ) -> io::Result<()>
48 where
49     W: Write,
50 {
51     let kind = if subgraph { "subgraph" } else { "digraph" };
52     let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR
53     let def_name = graphviz_safe_def_name(def_id);
54     writeln!(w, "{} {}Mir_{} {{", kind, cluster, def_name)?;
55
56     // Global graph properties
57     writeln!(w, r#"    graph [fontname="monospace"];"#)?;
58     writeln!(w, r#"    node [fontname="monospace"];"#)?;
59     writeln!(w, r#"    edge [fontname="monospace"];"#)?;
60
61     // Graph label
62     write_graph_label(tcx, def_id, body, w)?;
63
64     // Nodes
65     for (block, _) in body.basic_blocks().iter_enumerated() {
66         write_node(def_id, block, body, w)?;
67     }
68
69     // Edges
70     for (source, _) in body.basic_blocks().iter_enumerated() {
71         write_edges(def_id, source, body, w)?;
72     }
73     writeln!(w, "}}")
74 }
75
76 /// Write a graphviz HTML-styled label for the given basic block, with
77 /// all necessary escaping already performed. (This is suitable for
78 /// emitting directly, as is done in this module, or for use with the
79 /// LabelText::HtmlStr from libgraphviz.)
80 ///
81 /// `init` and `fini` are callbacks for emitting additional rows of
82 /// data (using HTML enclosed with `<tr>` in the emitted text).
83 pub fn write_node_label<W: Write, INIT, FINI>(
84     block: BasicBlock,
85     body: &Body<'_>,
86     w: &mut W,
87     num_cols: u32,
88     init: INIT,
89     fini: FINI,
90 ) -> io::Result<()>
91 where
92     INIT: Fn(&mut W) -> io::Result<()>,
93     FINI: Fn(&mut W) -> io::Result<()>,
94 {
95     let data = &body[block];
96
97     write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
98
99     // Basic block number at the top.
100     write!(
101         w,
102         r#"<tr><td {attrs} colspan="{colspan}">{blk}</td></tr>"#,
103         attrs = r#"bgcolor="gray" align="center""#,
104         colspan = num_cols,
105         blk = block.index()
106     )?;
107
108     init(w)?;
109
110     // List of statements in the middle.
111     if !data.statements.is_empty() {
112         write!(w, r#"<tr><td align="left" balign="left">"#)?;
113         for statement in &data.statements {
114             write!(w, "{}<br/>", escape(statement))?;
115         }
116         write!(w, "</td></tr>")?;
117     }
118
119     // Terminator head at the bottom, not including the list of successor blocks. Those will be
120     // displayed as labels on the edges between blocks.
121     let mut terminator_head = String::new();
122     data.terminator().kind.fmt_head(&mut terminator_head).unwrap();
123     write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head))?;
124
125     fini(w)?;
126
127     // Close the table
128     write!(w, "</table>")
129 }
130
131 /// Write a graphviz DOT node for the given basic block.
132 fn write_node<W: Write>(
133     def_id: DefId,
134     block: BasicBlock,
135     body: &Body<'_>,
136     w: &mut W,
137 ) -> io::Result<()> {
138     // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables.
139     write!(w, r#"    {} [shape="none", label=<"#, node(def_id, block))?;
140     write_node_label(block, body, w, 1, |_| Ok(()), |_| Ok(()))?;
141     // Close the node label and the node itself.
142     writeln!(w, ">];")
143 }
144
145 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
146 fn write_edges<W: Write>(
147     def_id: DefId,
148     source: BasicBlock,
149     body: &Body<'_>,
150     w: &mut W,
151 ) -> io::Result<()> {
152     let terminator = body[source].terminator();
153     let labels = terminator.kind.fmt_successor_labels();
154
155     for (&target, label) in terminator.successors().zip(labels) {
156         let src = node(def_id, source);
157         let trg = node(def_id, target);
158         writeln!(w, r#"    {} -> {} [label="{}"];"#, src, trg, label)?;
159     }
160
161     Ok(())
162 }
163
164 /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that
165 /// will appear below the graph, showing the type of the `fn` this MIR represents and the types of
166 /// all the variables and temporaries.
167 fn write_graph_label<'tcx, W: Write>(
168     tcx: TyCtxt<'tcx>,
169     def_id: DefId,
170     body: &Body<'_>,
171     w: &mut W,
172 ) -> io::Result<()> {
173     write!(w, "    label=<fn {}(", dot::escape_html(&tcx.def_path_str(def_id)))?;
174
175     // fn argument types.
176     for (i, arg) in body.args_iter().enumerate() {
177         if i > 0 {
178             write!(w, ", ")?;
179         }
180         write!(w, "{:?}: {}", Place::from(arg), escape(&body.local_decls[arg].ty))?;
181     }
182
183     write!(w, ") -&gt; {}", escape(&body.return_ty()))?;
184     write!(w, r#"<br align="left"/>"#)?;
185
186     for local in body.vars_and_temps_iter() {
187         let decl = &body.local_decls[local];
188
189         write!(w, "let ")?;
190         if decl.mutability == Mutability::Mut {
191             write!(w, "mut ")?;
192         }
193
194         write!(w, r#"{:?}: {};<br align="left"/>"#, Place::from(local), escape(&decl.ty))?;
195     }
196
197     for var_debug_info in &body.var_debug_info {
198         write!(
199             w,
200             r#"debug {} =&gt; {};<br align="left"/>"#,
201             var_debug_info.name,
202             escape(&var_debug_info.place)
203         )?;
204     }
205
206     writeln!(w, ">;")
207 }
208
209 fn node(def_id: DefId, block: BasicBlock) -> String {
210     format!("bb{}__{}", block.index(), graphviz_safe_def_name(def_id))
211 }
212
213 fn escape<T: Debug>(t: &T) -> String {
214     dot::escape_html(&format!("{:?}", t))
215 }