]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/dump.rs
Use ast attributes every where (remove HIR attributes).
[rust.git] / src / librustc_mir / dump.rs
1 // Copyright 2015 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 //! An experimental pass that scources for `#[rustc_mir]` attributes,
12 //! builds the resulting MIR, and dumps it out into a file for inspection.
13 //!
14 //! The attribute formats that are currently accepted are:
15 //!
16 //! - `#[rustc_mir(graphviz="file.gv")]`
17 //! - `#[rustc_mir(pretty="file.mir")]`
18
19 extern crate syntax;
20 extern crate rustc;
21 extern crate rustc_front;
22
23 use build;
24 use dot;
25 use repr::Mir;
26 use std::fs::File;
27 use tcx::{PatNode, Cx};
28
29 use self::rustc::middle::def_id::DefId;
30 use self::rustc::middle::infer;
31 use self::rustc::middle::region::CodeExtentData;
32 use self::rustc::middle::ty::{self, Ty};
33 use self::rustc::util::common::ErrorReported;
34 use self::rustc_front::hir;
35 use self::rustc_front::visit;
36 use self::syntax::ast;
37 use self::syntax::attr::AttrMetaMethods;
38 use self::syntax::codemap::Span;
39
40 pub fn dump_crate(tcx: &ty::ctxt) {
41     let mut dump = OuterDump { tcx: tcx };
42     visit::walk_crate(&mut dump, tcx.map.krate());
43 }
44
45 ///////////////////////////////////////////////////////////////////////////
46 // OuterDump -- walks a crate, looking for fn items and methods to build MIR from
47
48 struct OuterDump<'a,'tcx:'a> {
49     tcx: &'a ty::ctxt<'tcx>,
50 }
51
52 impl<'a, 'tcx> OuterDump<'a, 'tcx> {
53     fn visit_mir<OP>(&self, attributes: &'tcx [ast::Attribute], mut walk_op: OP)
54         where OP: FnMut(&mut InnerDump<'a,'tcx>)
55     {
56         let mut built_mir = false;
57
58         for attr in attributes {
59             if attr.check_name("rustc_mir") {
60                 let mut closure_dump = InnerDump { tcx: self.tcx, attr: Some(attr) };
61                 walk_op(&mut closure_dump);
62                 built_mir = true;
63             }
64         }
65
66         let always_build_mir = self.tcx.sess.opts.always_build_mir;
67         if !built_mir && always_build_mir {
68             let mut closure_dump = InnerDump { tcx: self.tcx, attr: None };
69             walk_op(&mut closure_dump);
70         }
71     }
72 }
73
74
75 impl<'a, 'tcx> visit::Visitor<'tcx> for OuterDump<'a, 'tcx> {
76     fn visit_item(&mut self, item: &'tcx hir::Item) {
77         self.visit_mir(&item.attrs, |c| visit::walk_item(c, item));
78         visit::walk_item(self, item);
79     }
80
81     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
82         match trait_item.node {
83             hir::MethodTraitItem(_, Some(_)) => {
84                 self.visit_mir(&trait_item.attrs, |c| visit::walk_trait_item(c, trait_item));
85             }
86             _ => { }
87         }
88         visit::walk_trait_item(self, trait_item);
89     }
90 }
91
92 ///////////////////////////////////////////////////////////////////////////
93 // InnerDump -- dumps MIR for a single fn and its contained closures
94
95 struct InnerDump<'a,'tcx:'a> {
96     tcx: &'a ty::ctxt<'tcx>,
97     attr: Option<&'a ast::Attribute>,
98 }
99
100 impl<'a, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'tcx> {
101     fn visit_item(&mut self, _: &'tcx hir::Item) {
102         // ignore nested items; they need their own graphviz annotation
103     }
104
105     fn visit_fn(&mut self,
106                 fk: visit::FnKind<'tcx>,
107                 decl: &'tcx hir::FnDecl,
108                 body: &'tcx hir::Block,
109                 span: Span,
110                 id: ast::NodeId) {
111         let (prefix, implicit_arg_tys) = match fk {
112             visit::FnKind::Closure =>
113                 (format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
114             _ =>
115                 (format!(""), vec![]),
116         };
117
118         let param_env =
119             ty::ParameterEnvironment::for_item(self.tcx, id);
120
121         let infcx =
122             infer::new_infer_ctxt(self.tcx,
123                                   &self.tcx.tables,
124                                   Some(param_env),
125                                   true);
126
127         match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
128             Ok(mir) => {
129                 let meta_item_list =
130                     self.attr.iter()
131                              .flat_map(|a| a.meta_item_list())
132                              .flat_map(|l| l.iter());
133                 for item in meta_item_list {
134                     if item.check_name("graphviz") {
135                         match item.value_str() {
136                             Some(s) => {
137                                 match
138                                     File::create(format!("{}{}", prefix, s))
139                                     .and_then(|ref mut output| dot::render(&mir, output))
140                                 {
141                                     Ok(()) => { }
142                                     Err(e) => {
143                                         self.tcx.sess.span_fatal(
144                                             item.span,
145                                             &format!("Error writing graphviz \
146                                                       results to `{}`: {}",
147                                                      s, e));
148                                     }
149                                 }
150                             }
151                             None => {
152                                 self.tcx.sess.span_err(
153                                     item.span,
154                                     &format!("graphviz attribute requires a path"));
155                             }
156                         }
157                     }
158                 }
159             }
160             Err(ErrorReported) => { }
161         }
162
163         visit::walk_fn(self, fk, decl, body, span);
164     }
165 }
166
167 fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
168                          implicit_arg_tys: Vec<Ty<'tcx>>,
169                          fn_id: ast::NodeId,
170                          span: Span,
171                          decl: &'tcx hir::FnDecl,
172                          body: &'tcx hir::Block)
173                          -> Result<Mir<Cx<'a,'tcx>>, ErrorReported> {
174     let arguments =
175         decl.inputs
176             .iter()
177             .map(|arg| {
178                 let ty = cx.tcx.node_id_to_type(arg.id);
179                 (ty, PatNode::irrefutable(&arg.pat))
180             })
181             .collect();
182
183     let parameter_scope =
184         cx.tcx.region_maps.lookup_code_extent(
185             CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
186     Ok(build::construct(cx,
187                         span,
188                         implicit_arg_tys,
189                         arguments,
190                         parameter_scope,
191                         body))
192 }
193
194 fn closure_self_ty<'a,'tcx>(tcx: &ty::ctxt<'tcx>,
195                             closure_expr_id: ast::NodeId,
196                             body_id: ast::NodeId)
197                             -> Ty<'tcx>
198 {
199     let closure_ty = tcx.node_id_to_type(closure_expr_id);
200
201     // We're just hard-coding the idea that the signature will be
202     // &self or &mut self and hence will have a bound region with
203     // number 0, hokey.
204     let region =
205         ty::Region::ReFree(
206             ty::FreeRegion {
207                 scope: tcx.region_maps.item_extent(body_id),
208                 bound_region: ty::BoundRegion::BrAnon(0)
209             });
210     let region =
211         tcx.mk_region(region);
212
213     match tcx.closure_kind(DefId::local(closure_expr_id)) {
214         ty::ClosureKind::FnClosureKind =>
215             tcx.mk_ref(region,
216                        ty::TypeAndMut { ty: closure_ty,
217                                         mutbl: hir::MutImmutable }),
218         ty::ClosureKind::FnMutClosureKind =>
219             tcx.mk_ref(region,
220                        ty::TypeAndMut { ty: closure_ty,
221                                         mutbl: hir::MutMutable }),
222         ty::ClosureKind::FnOnceClosureKind =>
223             closure_ty
224     }
225 }