]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 abi; | |
27 | use ast::{Block, FnDecl, NodeId}; | |
28 | use ast; | |
c34b1796 | 29 | use ast_map::Node; |
1a4d82fc | 30 | use ast_map; |
1a4d82fc JJ |
31 | use codemap::Span; |
32 | use visit; | |
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. | |
c34b1796 | 43 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
44 | pub struct FnLikeNode<'a> { node: ast_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: visit::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 { | |
c34b1796 | 67 | match self.node { ast::MethodTraitItem(_, Some(_)) => true, _ => false, } |
1a4d82fc JJ |
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). | |
c34b1796 | 83 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
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) -> ast::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 | fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } } | |
100 | match node { | |
101 | ast_map::NodeItem(item) if item.is_fn_like() => | |
102 | Some(FnLikeCode(new(node))), | |
103 | ast_map::NodeTraitItem(tm) if tm.is_fn_like() => | |
104 | Some(FnLikeCode(new(node))), | |
105 | ast_map::NodeImplItem(_) => | |
106 | Some(FnLikeCode(new(node))), | |
107 | ast_map::NodeExpr(e) if e.is_fn_like() => | |
108 | Some(FnLikeCode(new(node))), | |
109 | ast_map::NodeBlock(block) => | |
110 | Some(BlockCode(block)), | |
111 | _ => | |
112 | None, | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | /// These are all the components one can extract from a fn item for | |
118 | /// use when implementing FnLikeNode operations. | |
119 | struct ItemFnParts<'a> { | |
120 | ident: ast::Ident, | |
121 | decl: &'a ast::FnDecl, | |
122 | unsafety: ast::Unsafety, | |
123 | abi: abi::Abi, | |
9346a6ac | 124 | vis: ast::Visibility, |
1a4d82fc JJ |
125 | generics: &'a ast::Generics, |
126 | body: &'a Block, | |
127 | id: ast::NodeId, | |
128 | span: Span | |
129 | } | |
130 | ||
131 | /// These are all the components one can extract from a closure expr | |
132 | /// for use when implementing FnLikeNode operations. | |
133 | struct ClosureParts<'a> { | |
134 | decl: &'a FnDecl, | |
135 | body: &'a Block, | |
136 | id: NodeId, | |
137 | span: Span | |
138 | } | |
139 | ||
140 | impl<'a> ClosureParts<'a> { | |
141 | fn new(d: &'a FnDecl, b: &'a Block, id: NodeId, s: Span) -> ClosureParts<'a> { | |
142 | ClosureParts { decl: d, body: b, id: id, span: s } | |
143 | } | |
144 | } | |
145 | ||
146 | impl<'a> FnLikeNode<'a> { | |
147 | pub fn to_fn_parts(self) -> FnParts<'a> { | |
148 | FnParts { | |
149 | decl: self.decl(), | |
150 | body: self.body(), | |
151 | kind: self.kind(), | |
152 | span: self.span(), | |
153 | id: self.id(), | |
154 | } | |
155 | } | |
156 | ||
157 | pub fn body(self) -> &'a Block { | |
158 | self.handle(|i: ItemFnParts<'a>| &*i.body, | |
9346a6ac | 159 | |_, _, _: &'a ast::MethodSig, _, body: &'a ast::Block, _| body, |
1a4d82fc JJ |
160 | |c: ClosureParts<'a>| c.body) |
161 | } | |
162 | ||
163 | pub fn decl(self) -> &'a FnDecl { | |
164 | self.handle(|i: ItemFnParts<'a>| &*i.decl, | |
9346a6ac | 165 | |_, _, sig: &'a ast::MethodSig, _, _, _| &sig.decl, |
1a4d82fc JJ |
166 | |c: ClosureParts<'a>| c.decl) |
167 | } | |
168 | ||
169 | pub fn span(self) -> Span { | |
170 | self.handle(|i: ItemFnParts| i.span, | |
9346a6ac | 171 | |_, _, _: &'a ast::MethodSig, _, _, span| span, |
1a4d82fc JJ |
172 | |c: ClosureParts| c.span) |
173 | } | |
174 | ||
175 | pub fn id(self) -> NodeId { | |
176 | self.handle(|i: ItemFnParts| i.id, | |
9346a6ac | 177 | |id, _, _: &'a ast::MethodSig, _, _, _| id, |
1a4d82fc JJ |
178 | |c: ClosureParts| c.id) |
179 | } | |
180 | ||
181 | pub fn kind(self) -> visit::FnKind<'a> { | |
85aaf69f | 182 | let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> { |
9346a6ac | 183 | visit::FkItemFn(p.ident, p.generics, p.unsafety, p.abi, p.vis) |
1a4d82fc | 184 | }; |
85aaf69f | 185 | let closure = |_: ClosureParts| { |
1a4d82fc JJ |
186 | visit::FkFnBlock |
187 | }; | |
9346a6ac AL |
188 | let method = |_, ident, sig: &'a ast::MethodSig, vis, _, _| { |
189 | visit::FkMethod(ident, sig, vis) | |
1a4d82fc JJ |
190 | }; |
191 | self.handle(item, method, closure) | |
192 | } | |
193 | ||
194 | fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where | |
195 | I: FnOnce(ItemFnParts<'a>) -> A, | |
9346a6ac AL |
196 | M: FnOnce(NodeId, |
197 | ast::Ident, | |
198 | &'a ast::MethodSig, | |
199 | Option<ast::Visibility>, | |
200 | &'a ast::Block, | |
201 | Span) | |
202 | -> A, | |
1a4d82fc JJ |
203 | C: FnOnce(ClosureParts<'a>) -> A, |
204 | { | |
205 | match self.node { | |
206 | ast_map::NodeItem(i) => match i.node { | |
207 | ast::ItemFn(ref decl, unsafety, abi, ref generics, ref block) => | |
208 | item_fn(ItemFnParts{ | |
209 | ident: i.ident, decl: &**decl, unsafety: unsafety, body: &**block, | |
9346a6ac | 210 | generics: generics, abi: abi, vis: i.vis, id: i.id, span: i.span |
1a4d82fc JJ |
211 | }), |
212 | _ => panic!("item FnLikeNode that is not fn-like"), | |
213 | }, | |
c34b1796 AL |
214 | ast_map::NodeTraitItem(ti) => match ti.node { |
215 | ast::MethodTraitItem(ref sig, Some(ref body)) => { | |
9346a6ac | 216 | method(ti.id, ti.ident, sig, None, body, ti.span) |
c34b1796 | 217 | } |
1a4d82fc JJ |
218 | _ => panic!("trait method FnLikeNode that is not fn-like"), |
219 | }, | |
220 | ast_map::NodeImplItem(ii) => { | |
c34b1796 AL |
221 | match ii.node { |
222 | ast::MethodImplItem(ref sig, ref body) => { | |
9346a6ac | 223 | method(ii.id, ii.ident, sig, Some(ii.vis), body, ii.span) |
c34b1796 | 224 | } |
d9579d0f | 225 | _ => { |
1a4d82fc JJ |
226 | panic!("impl method FnLikeNode that is not fn-like") |
227 | } | |
228 | } | |
229 | } | |
230 | ast_map::NodeExpr(e) => match e.node { | |
85aaf69f | 231 | ast::ExprClosure(_, ref decl, ref block) => |
1a4d82fc JJ |
232 | closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), |
233 | _ => panic!("expr FnLikeNode that is not fn-like"), | |
234 | }, | |
235 | _ => panic!("other FnLikeNode that is not fn-like"), | |
236 | } | |
237 | } | |
238 | } |