]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/pretty.rs
Rollup merge of #41214 - estebank:less-multiline, r=petrochenkov
[rust.git] / src / librustc_mir / util / pretty.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::hir;
12 use rustc::hir::def_id::DefId;
13 use rustc::mir::*;
14 use rustc::mir::transform::MirSource;
15 use rustc::ty::TyCtxt;
16 use rustc_data_structures::fx::FxHashMap;
17 use rustc_data_structures::indexed_vec::{Idx};
18 use std::fmt::Display;
19 use std::fs;
20 use std::io::{self, Write};
21 use std::path::{PathBuf, Path};
22
23 const INDENT: &'static str = "    ";
24 /// Alignment for lining up comments following MIR statements
25 const ALIGN: usize = 40;
26
27 /// If the session is properly configured, dumps a human-readable
28 /// representation of the mir into:
29 ///
30 /// ```text
31 /// rustc.node<node_id>.<pass_name>.<disambiguator>
32 /// ```
33 ///
34 /// Output from this function is controlled by passing `-Z dump-mir=<filter>`,
35 /// where `<filter>` takes the following forms:
36 ///
37 /// - `all` -- dump MIR for all fns, all passes, all everything
38 /// - `substring1&substring2,...` -- `&`-separated list of substrings
39 ///   that can appear in the pass-name or the `item_path_str` for the given
40 ///   node-id. If any one of the substrings match, the data is dumped out.
41 pub fn dump_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
42                           pass_name: &str,
43                           disambiguator: &Display,
44                           src: MirSource,
45                           mir: &Mir<'tcx>) {
46     let filters = match tcx.sess.opts.debugging_opts.dump_mir {
47         None => return,
48         Some(ref filters) => filters,
49     };
50     let node_id = src.item_id();
51     let node_path = tcx.item_path_str(tcx.hir.local_def_id(node_id));
52     let is_matched =
53         filters.split("&")
54                .any(|filter| {
55                    filter == "all" ||
56                        pass_name.contains(filter) ||
57                        node_path.contains(filter)
58                });
59     if !is_matched {
60         return;
61     }
62
63     let promotion_id = match src {
64         MirSource::Promoted(_, id) => format!("-{:?}", id),
65         _ => String::new()
66     };
67
68     let mut file_path = PathBuf::new();
69     if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
70         let p = Path::new(file_dir);
71         file_path.push(p);
72     };
73     let file_name = format!("rustc.node{}{}.{}.{}.mir",
74                             node_id, promotion_id, pass_name, disambiguator);
75     file_path.push(&file_name);
76     let _ = fs::File::create(&file_path).and_then(|mut file| {
77         writeln!(file, "// MIR for `{}`", node_path)?;
78         writeln!(file, "// node_id = {}", node_id)?;
79         writeln!(file, "// pass_name = {}", pass_name)?;
80         writeln!(file, "// disambiguator = {}", disambiguator)?;
81         writeln!(file, "")?;
82         write_mir_fn(tcx, src, mir, &mut file)?;
83         Ok(())
84     });
85 }
86
87 /// Write out a human-readable textual representation for the given MIR.
88 pub fn write_mir_pretty<'a, 'b, 'tcx, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
89                                          iter: I,
90                                          w: &mut Write)
91                                          -> io::Result<()>
92     where I: Iterator<Item=DefId>, 'tcx: 'a
93 {
94     writeln!(w, "// WARNING: This output format is intended for human consumers only")?;
95     writeln!(w, "// and is subject to change without notice. Knock yourself out.")?;
96
97     let mut first = true;
98     for def_id in iter.filter(DefId::is_local) {
99         let mir = &tcx.item_mir(def_id);
100
101         if first {
102             first = false;
103         } else {
104             // Put empty lines between all items
105             writeln!(w, "")?;
106         }
107
108         let id = tcx.hir.as_local_node_id(def_id).unwrap();
109         let src = MirSource::from_node(tcx, id);
110         write_mir_fn(tcx, src, mir, w)?;
111
112         for (i, mir) in mir.promoted.iter_enumerated() {
113             writeln!(w, "")?;
114             write_mir_fn(tcx, MirSource::Promoted(id, i), mir, w)?;
115         }
116     }
117     Ok(())
118 }
119
120 pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
121                               src: MirSource,
122                               mir: &Mir<'tcx>,
123                               w: &mut Write)
124                               -> io::Result<()> {
125     write_mir_intro(tcx, src, mir, w)?;
126     for block in mir.basic_blocks().indices() {
127         write_basic_block(tcx, block, mir, w)?;
128         if block.index() + 1 != mir.basic_blocks().len() {
129             writeln!(w, "")?;
130         }
131     }
132
133     writeln!(w, "}}")?;
134     Ok(())
135 }
136
137 /// Write out a human-readable textual representation for the given basic block.
138 fn write_basic_block(tcx: TyCtxt,
139                      block: BasicBlock,
140                      mir: &Mir,
141                      w: &mut Write)
142                      -> io::Result<()> {
143     let data = &mir[block];
144
145     // Basic block label at the top.
146     writeln!(w, "{}{:?}: {{", INDENT, block)?;
147
148     // List of statements in the middle.
149     let mut current_location = Location { block: block, statement_index: 0 };
150     for statement in &data.statements {
151         let indented_mir = format!("{0}{0}{1:?};", INDENT, statement);
152         writeln!(w, "{0:1$} // {2}",
153                  indented_mir,
154                  ALIGN,
155                  comment(tcx, statement.source_info))?;
156
157         current_location.statement_index += 1;
158     }
159
160     // Terminator at the bottom.
161     let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
162     writeln!(w, "{0:1$} // {2}",
163              indented_terminator,
164              ALIGN,
165              comment(tcx, data.terminator().source_info))?;
166
167     writeln!(w, "{}}}", INDENT)
168 }
169
170 fn comment(tcx: TyCtxt, SourceInfo { span, scope }: SourceInfo) -> String {
171     format!("scope {} at {}", scope.index(), tcx.sess.codemap().span_to_string(span))
172 }
173
174 /// Prints user-defined variables in a scope tree.
175 ///
176 /// Returns the total number of variables printed.
177 fn write_scope_tree(tcx: TyCtxt,
178                     mir: &Mir,
179                     scope_tree: &FxHashMap<VisibilityScope, Vec<VisibilityScope>>,
180                     w: &mut Write,
181                     parent: VisibilityScope,
182                     depth: usize)
183                     -> io::Result<()> {
184     let indent = depth * INDENT.len();
185
186     let children = match scope_tree.get(&parent) {
187         Some(childs) => childs,
188         None => return Ok(()),
189     };
190
191     for &child in children {
192         let data = &mir.visibility_scopes[child];
193         assert_eq!(data.parent_scope, Some(parent));
194         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
195
196         // User variable types (including the user's name in a comment).
197         for local in mir.vars_iter() {
198             let var = &mir.local_decls[local];
199             let (name, source_info) = if var.source_info.scope == child {
200                 (var.name.unwrap(), var.source_info)
201             } else {
202                 // Not a variable or not declared in this scope.
203                 continue;
204             };
205
206             let mut_str = if var.mutability == Mutability::Mut {
207                 "mut "
208             } else {
209                 ""
210             };
211
212             let indent = indent + INDENT.len();
213             let indented_var = format!("{0:1$}let {2}{3:?}: {4};",
214                                        INDENT,
215                                        indent,
216                                        mut_str,
217                                        local,
218                                        var.ty);
219             writeln!(w, "{0:1$} // \"{2}\" in {3}",
220                      indented_var,
221                      ALIGN,
222                      name,
223                      comment(tcx, source_info))?;
224         }
225
226         write_scope_tree(tcx, mir, scope_tree, w, child, depth + 1)?;
227
228         writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?;
229     }
230
231     Ok(())
232 }
233
234 /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its
235 /// local variables (both user-defined bindings and compiler temporaries).
236 fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
237                              src: MirSource,
238                              mir: &Mir,
239                              w: &mut Write)
240                              -> io::Result<()> {
241     write_mir_sig(tcx, src, mir, w)?;
242     writeln!(w, " {{")?;
243
244     // construct a scope tree and write it out
245     let mut scope_tree: FxHashMap<VisibilityScope, Vec<VisibilityScope>> = FxHashMap();
246     for (index, scope_data) in mir.visibility_scopes.iter().enumerate() {
247         if let Some(parent) = scope_data.parent_scope {
248             scope_tree.entry(parent)
249                       .or_insert(vec![])
250                       .push(VisibilityScope::new(index));
251         } else {
252             // Only the argument scope has no parent, because it's the root.
253             assert_eq!(index, ARGUMENT_VISIBILITY_SCOPE.index());
254         }
255     }
256
257     // Print return pointer
258     let indented_retptr = format!("{}let mut {:?}: {};",
259                                   INDENT,
260                                   RETURN_POINTER,
261                                   mir.return_ty);
262     writeln!(w, "{0:1$} // return pointer",
263              indented_retptr,
264              ALIGN)?;
265
266     write_scope_tree(tcx, mir, &scope_tree, w, ARGUMENT_VISIBILITY_SCOPE, 1)?;
267
268     write_temp_decls(mir, w)?;
269
270     // Add an empty line before the first block is printed.
271     writeln!(w, "")?;
272
273     Ok(())
274 }
275
276 fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
277                  -> io::Result<()>
278 {
279     match src {
280         MirSource::Fn(_) => write!(w, "fn")?,
281         MirSource::Const(_) => write!(w, "const")?,
282         MirSource::Static(_, hir::MutImmutable) => write!(w, "static")?,
283         MirSource::Static(_, hir::MutMutable) => write!(w, "static mut")?,
284         MirSource::Promoted(_, i) => write!(w, "{:?} in", i)?
285     }
286
287     write!(w, " {}", tcx.node_path_str(src.item_id()))?;
288
289     if let MirSource::Fn(_) = src {
290         write!(w, "(")?;
291
292         // fn argument types.
293         for (i, arg) in mir.args_iter().enumerate() {
294             if i != 0 {
295                 write!(w, ", ")?;
296             }
297             write!(w, "{:?}: {}", Lvalue::Local(arg), mir.local_decls[arg].ty)?;
298         }
299
300         write!(w, ") -> {}", mir.return_ty)
301     } else {
302         assert_eq!(mir.arg_count, 0);
303         write!(w, ": {} =", mir.return_ty)
304     }
305 }
306
307 fn write_temp_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
308     // Compiler-introduced temporary types.
309     for temp in mir.temps_iter() {
310         writeln!(w, "{}let mut {:?}: {};", INDENT, temp, mir.local_decls[temp].ty)?;
311     }
312
313     Ok(())
314 }