]> git.lizzy.rs Git - rust.git/blob - src/librustc/front/map/blocks.rs
Port the `map` construction code to use the new visitor.
[rust.git] / src / librustc / front / map / blocks.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 //! This module provides a simplified abstraction for working with
12 //! code blocks identified by their integer node-id.  In particular,
13 //! it captures a common set of attributes that all "function-like
14 //! things" (represented by `FnLike` instances) share.  For example,
15 //! all `FnLike` instances have a type signature (be it explicit or
16 //! inferred).  And all `FnLike` instances have a body, i.e. the code
17 //! that is run when the function-like thing it represents is invoked.
18 //!
19 //! With the above abstraction in place, one can treat the program
20 //! text as a collection of blocks of code (and most such blocks are
21 //! nested within a uniquely determined `FnLike`), and users can ask
22 //! for the `Code` associated with a particular NodeId.
23
24 pub use self::Code::*;
25
26 use front::map::{self, Node};
27 use syntax::abi;
28 use rustc_front::hir::{Block, FnDecl};
29 use syntax::ast::{Name, NodeId};
30 use rustc_front::hir as ast;
31 use syntax::codemap::Span;
32 use rustc_front::intravisit::FnKind;
33
34 /// An FnLikeNode is a Node that is like a fn, in that it has a decl
35 /// and a body (as well as a NodeId, a span, etc).
36 ///
37 /// More specifically, it is one of either:
38 ///   - A function item,
39 ///   - A closure expr (i.e. an ExprClosure), or
40 ///   - The default implementation for a trait method.
41 ///
42 /// To construct one, use the `Code::from_node` function.
43 #[derive(Copy, Clone)]
44 pub struct FnLikeNode<'a> { node: map::Node<'a> }
45
46 /// MaybeFnLike wraps a method that indicates if an object
47 /// corresponds to some FnLikeNode.
48 pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; }
49
50 /// Components shared by fn-like things (fn items, methods, closures).
51 pub struct FnParts<'a> {
52     pub decl: &'a FnDecl,
53     pub body: &'a Block,
54     pub kind: FnKind<'a>,
55     pub span: Span,
56     pub id:   NodeId,
57 }
58
59 impl MaybeFnLike for ast::Item {
60     fn is_fn_like(&self) -> bool {
61         match self.node { ast::ItemFn(..) => true, _ => false, }
62     }
63 }
64
65 impl MaybeFnLike for ast::TraitItem {
66     fn is_fn_like(&self) -> bool {
67         match self.node { ast::MethodTraitItem(_, Some(_)) => true, _ => false, }
68     }
69 }
70
71 impl MaybeFnLike for ast::Expr {
72     fn is_fn_like(&self) -> bool {
73         match self.node {
74             ast::ExprClosure(..) => true,
75             _ => false,
76         }
77     }
78 }
79
80 /// Carries either an FnLikeNode or a Block, as these are the two
81 /// constructs that correspond to "code" (as in, something from which
82 /// we can construct a control-flow graph).
83 #[derive(Copy, Clone)]
84 pub enum Code<'a> {
85     FnLikeCode(FnLikeNode<'a>),
86     BlockCode(&'a Block),
87 }
88
89 impl<'a> Code<'a> {
90     pub fn id(&self) -> NodeId {
91         match *self {
92             FnLikeCode(node) => node.id(),
93             BlockCode(block) => block.id,
94         }
95     }
96
97     /// Attempts to construct a Code from presumed FnLike or Block node input.
98     pub fn from_node(node: Node) -> Option<Code> {
99         if let map::NodeBlock(block) = node {
100             Some(BlockCode(block))
101         } else {
102             FnLikeNode::from_node(node).map(|fn_like| FnLikeCode(fn_like))
103         }
104     }
105 }
106
107 /// These are all the components one can extract from a fn item for
108 /// use when implementing FnLikeNode operations.
109 struct ItemFnParts<'a> {
110     name:     Name,
111     decl:     &'a ast::FnDecl,
112     unsafety: ast::Unsafety,
113     constness: ast::Constness,
114     abi:      abi::Abi,
115     vis:      ast::Visibility,
116     generics: &'a ast::Generics,
117     body:     &'a Block,
118     id:       NodeId,
119     span:     Span
120 }
121
122 /// These are all the components one can extract from a closure expr
123 /// for use when implementing FnLikeNode operations.
124 struct ClosureParts<'a> {
125     decl: &'a FnDecl,
126     body: &'a Block,
127     id: NodeId,
128     span: Span
129 }
130
131 impl<'a> ClosureParts<'a> {
132     fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span) -> ClosureParts<'a> {
133         ClosureParts { decl: d, body: b, id: id, span: s }
134     }
135 }
136
137 impl<'a> FnLikeNode<'a> {
138     /// Attempts to construct a FnLikeNode from presumed FnLike node input.
139     pub fn from_node(node: Node) -> Option<FnLikeNode> {
140         let fn_like = match node {
141             map::NodeItem(item) => item.is_fn_like(),
142             map::NodeTraitItem(tm) => tm.is_fn_like(),
143             map::NodeImplItem(_) => true,
144             map::NodeExpr(e) => e.is_fn_like(),
145             _ => false
146         };
147         if fn_like {
148             Some(FnLikeNode {
149                 node: node
150             })
151         } else {
152             None
153         }
154     }
155
156     pub fn to_fn_parts(self) -> FnParts<'a> {
157         FnParts {
158             decl: self.decl(),
159             body: self.body(),
160             kind: self.kind(),
161             span: self.span(),
162             id:   self.id(),
163         }
164     }
165
166     pub fn body(self) -> &'a Block {
167         self.handle(|i: ItemFnParts<'a>|  &*i.body,
168                     |_, _, _: &'a ast::MethodSig, _, body: &'a ast::Block, _|  body,
169                     |c: ClosureParts<'a>| c.body)
170     }
171
172     pub fn decl(self) -> &'a FnDecl {
173         self.handle(|i: ItemFnParts<'a>|  &*i.decl,
174                     |_, _, sig: &'a ast::MethodSig, _, _, _|  &sig.decl,
175                     |c: ClosureParts<'a>| c.decl)
176     }
177
178     pub fn span(self) -> Span {
179         self.handle(|i: ItemFnParts|     i.span,
180                     |_, _, _: &'a ast::MethodSig, _, _, span| span,
181                     |c: ClosureParts|    c.span)
182     }
183
184     pub fn id(self) -> NodeId {
185         self.handle(|i: ItemFnParts|     i.id,
186                     |id, _, _: &'a ast::MethodSig, _, _, _| id,
187                     |c: ClosureParts|    c.id)
188     }
189
190     pub fn kind(self) -> FnKind<'a> {
191         let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
192             FnKind::ItemFn(p.name, p.generics, p.unsafety, p.constness, p.abi, p.vis)
193         };
194         let closure = |_: ClosureParts| {
195             FnKind::Closure
196         };
197         let method = |_, name: Name, sig: &'a ast::MethodSig, vis, _, _| {
198             FnKind::Method(name, sig, vis)
199         };
200         self.handle(item, method, closure)
201     }
202
203     fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where
204         I: FnOnce(ItemFnParts<'a>) -> A,
205         M: FnOnce(NodeId,
206                   Name,
207                   &'a ast::MethodSig,
208                   Option<ast::Visibility>,
209                   &'a ast::Block,
210                   Span)
211                   -> A,
212         C: FnOnce(ClosureParts<'a>) -> A,
213     {
214         match self.node {
215             map::NodeItem(i) => match i.node {
216                 ast::ItemFn(ref decl, unsafety, constness, abi, ref generics, ref block) =>
217                     item_fn(ItemFnParts {
218                         id: i.id,
219                         name: i.name,
220                         decl: &**decl,
221                         unsafety: unsafety,
222                         body: &**block,
223                         generics: generics,
224                         abi: abi,
225                         vis: i.vis,
226                         constness: constness,
227                         span: i.span
228                     }),
229                 _ => panic!("item FnLikeNode that is not fn-like"),
230             },
231             map::NodeTraitItem(ti) => match ti.node {
232                 ast::MethodTraitItem(ref sig, Some(ref body)) => {
233                     method(ti.id, ti.name, sig, None, body, ti.span)
234                 }
235                 _ => panic!("trait method FnLikeNode that is not fn-like"),
236             },
237             map::NodeImplItem(ii) => {
238                 match ii.node {
239                     ast::ImplItemKind::Method(ref sig, ref body) => {
240                         method(ii.id, ii.name, sig, Some(ii.vis), body, ii.span)
241                     }
242                     _ => {
243                         panic!("impl method FnLikeNode that is not fn-like")
244                     }
245                 }
246             }
247             map::NodeExpr(e) => match e.node {
248                 ast::ExprClosure(_, ref decl, ref block) =>
249                     closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
250                 _ => panic!("expr FnLikeNode that is not fn-like"),
251             },
252             _ => panic!("other FnLikeNode that is not fn-like"),
253         }
254     }
255 }