]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | //! This module provides a simplified abstraction for working with |
9fa01778 | 2 | //! code blocks identified by their integer `NodeId`. In particular, |
1a4d82fc | 3 | //! it captures a common set of attributes that all "function-like |
9fa01778 | 4 | //! things" (represented by `FnLike` instances) share. For example, |
1a4d82fc | 5 | //! all `FnLike` instances have a type signature (be it explicit or |
9fa01778 | 6 | //! inferred). And all `FnLike` instances have a body, i.e., the code |
1a4d82fc JJ |
7 | //! that is run when the function-like thing it represents is invoked. |
8 | //! | |
9 | //! With the above abstraction in place, one can treat the program | |
10 | //! text as a collection of blocks of code (and most such blocks are | |
11 | //! nested within a uniquely determined `FnLike`), and users can ask | |
12 | //! for the `Code` associated with a particular NodeId. | |
13 | ||
dfeec247 | 14 | use crate::hir::map::Map; |
74b04a01 | 15 | use rustc_ast::ast::{Attribute, Ident}; |
dfeec247 XL |
16 | use rustc_hir as hir; |
17 | use rustc_hir::intravisit::FnKind; | |
18 | use rustc_hir::{Expr, FnDecl, Node}; | |
19 | use rustc_span::Span; | |
1a4d82fc JJ |
20 | |
21 | /// An FnLikeNode is a Node that is like a fn, in that it has a decl | |
22 | /// and a body (as well as a NodeId, a span, etc). | |
23 | /// | |
24 | /// More specifically, it is one of either: | |
ff7c6d11 | 25 | /// |
1a4d82fc | 26 | /// - A function item, |
0731742a | 27 | /// - A closure expr (i.e., an ExprKind::Closure), or |
1a4d82fc JJ |
28 | /// - The default implementation for a trait method. |
29 | /// | |
30 | /// To construct one, use the `Code::from_node` function. | |
32a655c1 | 31 | #[derive(Copy, Clone, Debug)] |
60c5eb7d XL |
32 | pub struct FnLikeNode<'a> { |
33 | node: Node<'a>, | |
34 | } | |
1a4d82fc JJ |
35 | |
36 | /// MaybeFnLike wraps a method that indicates if an object | |
37 | /// corresponds to some FnLikeNode. | |
60c5eb7d XL |
38 | trait MaybeFnLike { |
39 | fn is_fn_like(&self) -> bool; | |
40 | } | |
1a4d82fc | 41 | |
dfeec247 | 42 | impl MaybeFnLike for hir::Item<'_> { |
1a4d82fc | 43 | fn is_fn_like(&self) -> bool { |
e74abb32 | 44 | match self.kind { |
dfeec247 | 45 | hir::ItemKind::Fn(..) => true, |
e74abb32 XL |
46 | _ => false, |
47 | } | |
8faf50e0 XL |
48 | } |
49 | } | |
50 | ||
dfeec247 | 51 | impl MaybeFnLike for hir::ImplItem<'_> { |
8faf50e0 | 52 | fn is_fn_like(&self) -> bool { |
e74abb32 | 53 | match self.kind { |
dfeec247 | 54 | hir::ImplItemKind::Method(..) => true, |
e74abb32 XL |
55 | _ => false, |
56 | } | |
1a4d82fc JJ |
57 | } |
58 | } | |
59 | ||
dfeec247 | 60 | impl MaybeFnLike for hir::TraitItem<'_> { |
1a4d82fc | 61 | fn is_fn_like(&self) -> bool { |
e74abb32 | 62 | match self.kind { |
dfeec247 | 63 | hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => true, |
32a655c1 SL |
64 | _ => false, |
65 | } | |
1a4d82fc JJ |
66 | } |
67 | } | |
68 | ||
dfeec247 | 69 | impl MaybeFnLike for hir::Expr<'_> { |
1a4d82fc | 70 | fn is_fn_like(&self) -> bool { |
e74abb32 | 71 | match self.kind { |
dfeec247 | 72 | hir::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 | 83 | FnLike(FnLikeNode<'a>), |
dfeec247 | 84 | Expr(&'a Expr<'a>), |
1a4d82fc JJ |
85 | } |
86 | ||
87 | impl<'a> Code<'a> { | |
dfeec247 | 88 | pub fn id(&self) -> hir::HirId { |
1a4d82fc | 89 | match *self { |
476ff2be | 90 | Code::FnLike(node) => node.id(), |
532ac7d7 | 91 | Code::Expr(block) => block.hir_id, |
1a4d82fc JJ |
92 | } |
93 | } | |
94 | ||
476ff2be | 95 | /// Attempts to construct a Code from presumed FnLike or Expr node input. |
dfeec247 | 96 | pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option<Code<'a>> { |
476ff2be | 97 | match map.get(id) { |
dfeec247 | 98 | Node::Block(_) => { |
476ff2be SL |
99 | // Use the parent, hopefully an expression node. |
100 | Code::from_node(map, map.get_parent_node(id)) | |
101 | } | |
dfeec247 | 102 | Node::Expr(expr) => Some(Code::Expr(expr)), |
60c5eb7d | 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> { | |
60c5eb7d | 111 | ident: Ident, |
dfeec247 XL |
112 | decl: &'a hir::FnDecl<'a>, |
113 | header: hir::FnHeader, | |
114 | vis: &'a hir::Visibility<'a>, | |
115 | generics: &'a hir::Generics<'a>, | |
116 | body: hir::BodyId, | |
117 | id: hir::HirId, | |
60c5eb7d XL |
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> { | |
dfeec247 XL |
125 | decl: &'a FnDecl<'a>, |
126 | body: hir::BodyId, | |
127 | id: hir::HirId, | |
54a0048b SL |
128 | span: Span, |
129 | attrs: &'a [Attribute], | |
1a4d82fc JJ |
130 | } |
131 | ||
132 | impl<'a> ClosureParts<'a> { | |
dfeec247 XL |
133 | fn new( |
134 | d: &'a FnDecl<'a>, | |
135 | b: hir::BodyId, | |
136 | id: hir::HirId, | |
137 | s: Span, | |
138 | attrs: &'a [Attribute], | |
139 | ) -> Self { | |
60c5eb7d | 140 | ClosureParts { decl: d, body: b, id, span: s, attrs } |
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 { |
dfeec247 XL |
148 | Node::Item(item) => item.is_fn_like(), |
149 | Node::TraitItem(tm) => tm.is_fn_like(), | |
150 | Node::ImplItem(it) => it.is_fn_like(), | |
151 | Node::Expr(e) => e.is_fn_like(), | |
60c5eb7d | 152 | _ => false, |
62682a34 | 153 | }; |
60c5eb7d | 154 | fn_like.then_some(FnLikeNode { node }) |
62682a34 SL |
155 | } |
156 | ||
dfeec247 | 157 | pub fn body(self) -> hir::BodyId { |
60c5eb7d XL |
158 | self.handle( |
159 | |i: ItemFnParts<'a>| i.body, | |
dfeec247 | 160 | |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body, |
60c5eb7d XL |
161 | |c: ClosureParts<'a>| c.body, |
162 | ) | |
1a4d82fc JJ |
163 | } |
164 | ||
dfeec247 | 165 | pub fn decl(self) -> &'a FnDecl<'a> { |
60c5eb7d XL |
166 | self.handle( |
167 | |i: ItemFnParts<'a>| &*i.decl, | |
dfeec247 | 168 | |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl, |
60c5eb7d XL |
169 | |c: ClosureParts<'a>| c.decl, |
170 | ) | |
1a4d82fc JJ |
171 | } |
172 | ||
173 | pub fn span(self) -> Span { | |
60c5eb7d XL |
174 | self.handle( |
175 | |i: ItemFnParts<'_>| i.span, | |
dfeec247 | 176 | |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span, |
60c5eb7d XL |
177 | |c: ClosureParts<'_>| c.span, |
178 | ) | |
1a4d82fc JJ |
179 | } |
180 | ||
dfeec247 | 181 | pub fn id(self) -> hir::HirId { |
60c5eb7d XL |
182 | self.handle( |
183 | |i: ItemFnParts<'_>| i.id, | |
dfeec247 | 184 | |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id, |
60c5eb7d XL |
185 | |c: ClosureParts<'_>| c.id, |
186 | ) | |
1a4d82fc JJ |
187 | } |
188 | ||
dfeec247 XL |
189 | pub fn constness(self) -> hir::Constness { |
190 | self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness) | |
476ff2be SL |
191 | } |
192 | ||
dfeec247 XL |
193 | pub fn asyncness(self) -> hir::IsAsync { |
194 | self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness) | |
8faf50e0 XL |
195 | } |
196 | ||
dfeec247 XL |
197 | pub fn unsafety(self) -> hir::Unsafety { |
198 | self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety) | |
3b2f2976 XL |
199 | } |
200 | ||
e9174d1e SL |
201 | pub fn kind(self) -> FnKind<'a> { |
202 | let item = |p: ItemFnParts<'a>| -> FnKind<'a> { | |
0731742a | 203 | FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs) |
1a4d82fc | 204 | }; |
60c5eb7d | 205 | let closure = |c: ClosureParts<'a>| FnKind::Closure(c.attrs); |
dfeec247 | 206 | let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| { |
8faf50e0 | 207 | FnKind::Method(ident, sig, vis, attrs) |
1a4d82fc JJ |
208 | }; |
209 | self.handle(item, method, closure) | |
210 | } | |
211 | ||
60c5eb7d XL |
212 | fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A |
213 | where | |
1a4d82fc | 214 | I: FnOnce(ItemFnParts<'a>) -> A, |
60c5eb7d | 215 | M: FnOnce( |
dfeec247 | 216 | hir::HirId, |
60c5eb7d | 217 | Ident, |
dfeec247 XL |
218 | &'a hir::FnSig<'a>, |
219 | Option<&'a hir::Visibility<'a>>, | |
220 | hir::BodyId, | |
60c5eb7d XL |
221 | Span, |
222 | &'a [Attribute], | |
223 | ) -> A, | |
1a4d82fc JJ |
224 | C: FnOnce(ClosureParts<'a>) -> A, |
225 | { | |
226 | match self.node { | |
dfeec247 XL |
227 | Node::Item(i) => match i.kind { |
228 | hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts { | |
60c5eb7d XL |
229 | id: i.hir_id, |
230 | ident: i.ident, | |
231 | decl: &sig.decl, | |
232 | body: block, | |
233 | vis: &i.vis, | |
234 | span: i.span, | |
235 | attrs: &i.attrs, | |
236 | header: sig.header, | |
237 | generics, | |
238 | }), | |
54a0048b | 239 | _ => bug!("item FnLikeNode that is not fn-like"), |
1a4d82fc | 240 | }, |
dfeec247 XL |
241 | Node::TraitItem(ti) => match ti.kind { |
242 | hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => { | |
532ac7d7 | 243 | method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs) |
c34b1796 | 244 | } |
54a0048b | 245 | _ => bug!("trait method FnLikeNode that is not fn-like"), |
1a4d82fc | 246 | }, |
dfeec247 XL |
247 | Node::ImplItem(ii) => match ii.kind { |
248 | hir::ImplItemKind::Method(ref sig, body) => { | |
60c5eb7d | 249 | method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs) |
1a4d82fc | 250 | } |
60c5eb7d | 251 | _ => bug!("impl method FnLikeNode that is not fn-like"), |
476ff2be | 252 | }, |
dfeec247 XL |
253 | Node::Expr(e) => match e.kind { |
254 | hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => { | |
60c5eb7d XL |
255 | closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)) |
256 | } | |
54a0048b | 257 | _ => bug!("expr FnLikeNode that is not fn-like"), |
1a4d82fc | 258 | }, |
54a0048b | 259 | _ => bug!("other FnLikeNode that is not fn-like"), |
1a4d82fc JJ |
260 | } |
261 | } | |
262 | } |