]>
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 | ||
9e0c209e | 24 | use hir as ast; |
b7449926 XL |
25 | use hir::map; |
26 | use hir::{Expr, FnDecl, Node}; | |
9e0c209e | 27 | use hir::intravisit::FnKind; |
8faf50e0 | 28 | use syntax::ast::{Attribute, Ident, Name, NodeId}; |
3157f602 | 29 | use syntax_pos::Span; |
1a4d82fc JJ |
30 | |
31 | /// An FnLikeNode is a Node that is like a fn, in that it has a decl | |
32 | /// and a body (as well as a NodeId, a span, etc). | |
33 | /// | |
34 | /// More specifically, it is one of either: | |
ff7c6d11 | 35 | /// |
1a4d82fc | 36 | /// - A function item, |
8faf50e0 | 37 | /// - A closure expr (i.e. an ExprKind::Closure), or |
1a4d82fc JJ |
38 | /// - The default implementation for a trait method. |
39 | /// | |
40 | /// To construct one, use the `Code::from_node` function. | |
32a655c1 | 41 | #[derive(Copy, Clone, Debug)] |
b7449926 | 42 | pub struct FnLikeNode<'a> { node: Node<'a> } |
1a4d82fc JJ |
43 | |
44 | /// MaybeFnLike wraps a method that indicates if an object | |
45 | /// corresponds to some FnLikeNode. | |
46 | pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; } | |
47 | ||
1a4d82fc JJ |
48 | impl MaybeFnLike for ast::Item { |
49 | fn is_fn_like(&self) -> bool { | |
8faf50e0 XL |
50 | match self.node { ast::ItemKind::Fn(..) => true, _ => false, } |
51 | } | |
52 | } | |
53 | ||
54 | impl MaybeFnLike for ast::ImplItem { | |
55 | fn is_fn_like(&self) -> bool { | |
56 | match self.node { ast::ImplItemKind::Method(..) => true, _ => false, } | |
1a4d82fc JJ |
57 | } |
58 | } | |
59 | ||
60 | impl MaybeFnLike for ast::TraitItem { | |
61 | fn is_fn_like(&self) -> bool { | |
32a655c1 SL |
62 | match self.node { |
63 | ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true, | |
64 | _ => false, | |
65 | } | |
1a4d82fc JJ |
66 | } |
67 | } | |
68 | ||
69 | impl MaybeFnLike for ast::Expr { | |
70 | fn is_fn_like(&self) -> bool { | |
71 | match self.node { | |
8faf50e0 | 72 | ast::ExprKind::Closure(..) => true, |
1a4d82fc JJ |
73 | _ => false, |
74 | } | |
75 | } | |
76 | } | |
77 | ||
476ff2be | 78 | /// Carries either an FnLikeNode or a Expr, as these are the two |
1a4d82fc JJ |
79 | /// constructs that correspond to "code" (as in, something from which |
80 | /// we can construct a control-flow graph). | |
c34b1796 | 81 | #[derive(Copy, Clone)] |
1a4d82fc | 82 | pub enum Code<'a> { |
476ff2be SL |
83 | FnLike(FnLikeNode<'a>), |
84 | Expr(&'a Expr), | |
1a4d82fc JJ |
85 | } |
86 | ||
87 | impl<'a> Code<'a> { | |
e9174d1e | 88 | pub fn id(&self) -> NodeId { |
1a4d82fc | 89 | match *self { |
476ff2be SL |
90 | Code::FnLike(node) => node.id(), |
91 | Code::Expr(block) => block.id, | |
1a4d82fc JJ |
92 | } |
93 | } | |
94 | ||
476ff2be SL |
95 | /// Attempts to construct a Code from presumed FnLike or Expr node input. |
96 | pub fn from_node(map: &map::Map<'a>, id: NodeId) -> Option<Code<'a>> { | |
97 | match map.get(id) { | |
b7449926 | 98 | map::Node::Block(_) => { |
476ff2be SL |
99 | // Use the parent, hopefully an expression node. |
100 | Code::from_node(map, map.get_parent_node(id)) | |
101 | } | |
b7449926 | 102 | map::Node::Expr(expr) => Some(Code::Expr(expr)), |
476ff2be | 103 | node => FnLikeNode::from_node(node).map(Code::FnLike) |
1a4d82fc JJ |
104 | } |
105 | } | |
106 | } | |
107 | ||
108 | /// These are all the components one can extract from a fn item for | |
109 | /// use when implementing FnLikeNode operations. | |
110 | struct ItemFnParts<'a> { | |
b039eaaf | 111 | name: Name, |
1a4d82fc | 112 | decl: &'a ast::FnDecl, |
8faf50e0 | 113 | header: ast::FnHeader, |
54a0048b | 114 | vis: &'a ast::Visibility, |
1a4d82fc | 115 | generics: &'a ast::Generics, |
32a655c1 | 116 | body: ast::BodyId, |
e9174d1e | 117 | id: NodeId, |
54a0048b SL |
118 | span: Span, |
119 | attrs: &'a [Attribute], | |
1a4d82fc JJ |
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, | |
32a655c1 | 126 | body: ast::BodyId, |
1a4d82fc | 127 | id: NodeId, |
54a0048b SL |
128 | span: Span, |
129 | attrs: &'a [Attribute], | |
1a4d82fc JJ |
130 | } |
131 | ||
132 | impl<'a> ClosureParts<'a> { | |
32a655c1 | 133 | fn new(d: &'a FnDecl, b: ast::BodyId, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self { |
a7813a04 XL |
134 | ClosureParts { |
135 | decl: d, | |
136 | body: b, | |
041b39d2 | 137 | id, |
a7813a04 | 138 | span: s, |
041b39d2 | 139 | attrs, |
a7813a04 | 140 | } |
1a4d82fc JJ |
141 | } |
142 | } | |
143 | ||
144 | impl<'a> FnLikeNode<'a> { | |
62682a34 | 145 | /// Attempts to construct a FnLikeNode from presumed FnLike node input. |
0bf4aa26 | 146 | pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> { |
62682a34 | 147 | let fn_like = match node { |
b7449926 XL |
148 | map::Node::Item(item) => item.is_fn_like(), |
149 | map::Node::TraitItem(tm) => tm.is_fn_like(), | |
150 | map::Node::ImplItem(it) => it.is_fn_like(), | |
151 | map::Node::Expr(e) => e.is_fn_like(), | |
62682a34 SL |
152 | _ => false |
153 | }; | |
154 | if fn_like { | |
155 | Some(FnLikeNode { | |
041b39d2 | 156 | node, |
62682a34 SL |
157 | }) |
158 | } else { | |
159 | None | |
160 | } | |
161 | } | |
162 | ||
32a655c1 | 163 | pub fn body(self) -> ast::BodyId { |
b7449926 XL |
164 | self.handle(|i: ItemFnParts<'a>| i.body, |
165 | |_, _, _: &'a ast::MethodSig, _, body: ast::BodyId, _, _| body, | |
1a4d82fc JJ |
166 | |c: ClosureParts<'a>| c.body) |
167 | } | |
168 | ||
169 | pub fn decl(self) -> &'a FnDecl { | |
b7449926 XL |
170 | self.handle(|i: ItemFnParts<'a>| &*i.decl, |
171 | |_, _, sig: &'a ast::MethodSig, _, _, _, _| &sig.decl, | |
1a4d82fc JJ |
172 | |c: ClosureParts<'a>| c.decl) |
173 | } | |
174 | ||
175 | pub fn span(self) -> Span { | |
0bf4aa26 | 176 | self.handle(|i: ItemFnParts<'_>| i.span, |
54a0048b | 177 | |_, _, _: &'a ast::MethodSig, _, _, span, _| span, |
0bf4aa26 | 178 | |c: ClosureParts<'_>| c.span) |
1a4d82fc JJ |
179 | } |
180 | ||
181 | pub fn id(self) -> NodeId { | |
0bf4aa26 | 182 | self.handle(|i: ItemFnParts<'_>| i.id, |
54a0048b | 183 | |id, _, _: &'a ast::MethodSig, _, _, _, _| id, |
0bf4aa26 | 184 | |c: ClosureParts<'_>| c.id) |
1a4d82fc JJ |
185 | } |
186 | ||
476ff2be SL |
187 | pub fn constness(self) -> ast::Constness { |
188 | match self.kind() { | |
8faf50e0 XL |
189 | FnKind::ItemFn(_, _, header, ..) => header.constness, |
190 | FnKind::Method(_, m, ..) => m.header.constness, | |
476ff2be SL |
191 | _ => ast::Constness::NotConst |
192 | } | |
193 | } | |
194 | ||
8faf50e0 XL |
195 | pub fn asyncness(self) -> ast::IsAsync { |
196 | match self.kind() { | |
197 | FnKind::ItemFn(_, _, header, ..) => header.asyncness, | |
198 | FnKind::Method(_, m, ..) => m.header.asyncness, | |
199 | _ => ast::IsAsync::NotAsync | |
200 | } | |
201 | } | |
202 | ||
3b2f2976 XL |
203 | pub fn unsafety(self) -> ast::Unsafety { |
204 | match self.kind() { | |
8faf50e0 XL |
205 | FnKind::ItemFn(_, _, header, ..) => header.unsafety, |
206 | FnKind::Method(_, m, ..) => m.header.unsafety, | |
3b2f2976 XL |
207 | _ => ast::Unsafety::Normal |
208 | } | |
209 | } | |
210 | ||
e9174d1e SL |
211 | pub fn kind(self) -> FnKind<'a> { |
212 | let item = |p: ItemFnParts<'a>| -> FnKind<'a> { | |
8faf50e0 | 213 | FnKind::ItemFn(p.name, p.generics, p.header, p.vis, p.attrs) |
1a4d82fc | 214 | }; |
54a0048b SL |
215 | let closure = |c: ClosureParts<'a>| { |
216 | FnKind::Closure(c.attrs) | |
1a4d82fc | 217 | }; |
8faf50e0 XL |
218 | let method = |_, ident: Ident, sig: &'a ast::MethodSig, vis, _, _, attrs| { |
219 | FnKind::Method(ident, sig, vis, attrs) | |
1a4d82fc JJ |
220 | }; |
221 | self.handle(item, method, closure) | |
222 | } | |
223 | ||
224 | fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where | |
225 | I: FnOnce(ItemFnParts<'a>) -> A, | |
9346a6ac | 226 | M: FnOnce(NodeId, |
8faf50e0 | 227 | Ident, |
9346a6ac | 228 | &'a ast::MethodSig, |
54a0048b | 229 | Option<&'a ast::Visibility>, |
32a655c1 | 230 | ast::BodyId, |
54a0048b SL |
231 | Span, |
232 | &'a [Attribute]) | |
9346a6ac | 233 | -> A, |
1a4d82fc JJ |
234 | C: FnOnce(ClosureParts<'a>) -> A, |
235 | { | |
236 | match self.node { | |
b7449926 | 237 | map::Node::Item(i) => match i.node { |
8faf50e0 | 238 | ast::ItemKind::Fn(ref decl, header, ref generics, block) => |
62682a34 SL |
239 | item_fn(ItemFnParts { |
240 | id: i.id, | |
b039eaaf | 241 | name: i.name, |
7453a54e | 242 | decl: &decl, |
476ff2be | 243 | body: block, |
54a0048b | 244 | vis: &i.vis, |
54a0048b SL |
245 | span: i.span, |
246 | attrs: &i.attrs, | |
8faf50e0 XL |
247 | header, |
248 | generics, | |
1a4d82fc | 249 | }), |
54a0048b | 250 | _ => bug!("item FnLikeNode that is not fn-like"), |
1a4d82fc | 251 | }, |
b7449926 | 252 | map::Node::TraitItem(ti) => match ti.node { |
32a655c1 | 253 | ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => { |
8faf50e0 | 254 | method(ti.id, ti.ident, sig, None, body, ti.span, &ti.attrs) |
c34b1796 | 255 | } |
54a0048b | 256 | _ => bug!("trait method FnLikeNode that is not fn-like"), |
1a4d82fc | 257 | }, |
b7449926 | 258 | map::Node::ImplItem(ii) => { |
c34b1796 | 259 | match ii.node { |
476ff2be | 260 | ast::ImplItemKind::Method(ref sig, body) => { |
8faf50e0 | 261 | method(ii.id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) |
c34b1796 | 262 | } |
b7449926 | 263 | _ => bug!("impl method FnLikeNode that is not fn-like") |
1a4d82fc | 264 | } |
476ff2be | 265 | }, |
b7449926 | 266 | map::Node::Expr(e) => match e.node { |
8faf50e0 | 267 | ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => |
476ff2be | 268 | closure(ClosureParts::new(&decl, block, e.id, e.span, &e.attrs)), |
54a0048b | 269 | _ => bug!("expr FnLikeNode that is not fn-like"), |
1a4d82fc | 270 | }, |
54a0048b | 271 | _ => bug!("other FnLikeNode that is not fn-like"), |
1a4d82fc JJ |
272 | } |
273 | } | |
274 | } |