]> git.proxmox.com Git - rustc.git/blob - src/librustc/hir/map/blocks.rs
New upstream version 1.34.2+dfsg1
[rustc.git] / src / librustc / hir / map / blocks.rs
1 //! This module provides a simplified abstraction for working with
2 //! code blocks identified by their integer `NodeId`. In particular,
3 //! it captures a common set of attributes that all "function-like
4 //! things" (represented by `FnLike` instances) share. For example,
5 //! all `FnLike` instances have a type signature (be it explicit or
6 //! inferred). And all `FnLike` instances have a body, i.e., the code
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
14 use crate::hir as ast;
15 use crate::hir::map;
16 use crate::hir::{Expr, FnDecl, Node};
17 use crate::hir::intravisit::FnKind;
18 use syntax::ast::{Attribute, Ident, NodeId};
19 use syntax_pos::Span;
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:
25 ///
26 /// - A function item,
27 /// - A closure expr (i.e., an ExprKind::Closure), or
28 /// - The default implementation for a trait method.
29 ///
30 /// To construct one, use the `Code::from_node` function.
31 #[derive(Copy, Clone, Debug)]
32 pub struct FnLikeNode<'a> { node: Node<'a> }
33
34 /// MaybeFnLike wraps a method that indicates if an object
35 /// corresponds to some FnLikeNode.
36 trait MaybeFnLike { fn is_fn_like(&self) -> bool; }
37
38 impl MaybeFnLike for ast::Item {
39 fn is_fn_like(&self) -> bool {
40 match self.node { ast::ItemKind::Fn(..) => true, _ => false, }
41 }
42 }
43
44 impl MaybeFnLike for ast::ImplItem {
45 fn is_fn_like(&self) -> bool {
46 match self.node { ast::ImplItemKind::Method(..) => true, _ => false, }
47 }
48 }
49
50 impl MaybeFnLike for ast::TraitItem {
51 fn is_fn_like(&self) -> bool {
52 match self.node {
53 ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true,
54 _ => false,
55 }
56 }
57 }
58
59 impl MaybeFnLike for ast::Expr {
60 fn is_fn_like(&self) -> bool {
61 match self.node {
62 ast::ExprKind::Closure(..) => true,
63 _ => false,
64 }
65 }
66 }
67
68 /// Carries either an FnLikeNode or a Expr, as these are the two
69 /// constructs that correspond to "code" (as in, something from which
70 /// we can construct a control-flow graph).
71 #[derive(Copy, Clone)]
72 pub enum Code<'a> {
73 FnLike(FnLikeNode<'a>),
74 Expr(&'a Expr),
75 }
76
77 impl<'a> Code<'a> {
78 pub fn id(&self) -> NodeId {
79 match *self {
80 Code::FnLike(node) => node.id(),
81 Code::Expr(block) => block.id,
82 }
83 }
84
85 /// Attempts to construct a Code from presumed FnLike or Expr node input.
86 pub fn from_node(map: &map::Map<'a>, id: NodeId) -> Option<Code<'a>> {
87 match map.get(id) {
88 map::Node::Block(_) => {
89 // Use the parent, hopefully an expression node.
90 Code::from_node(map, map.get_parent_node(id))
91 }
92 map::Node::Expr(expr) => Some(Code::Expr(expr)),
93 node => FnLikeNode::from_node(node).map(Code::FnLike)
94 }
95 }
96 }
97
98 /// These are all the components one can extract from a fn item for
99 /// use when implementing FnLikeNode operations.
100 struct ItemFnParts<'a> {
101 ident: Ident,
102 decl: &'a ast::FnDecl,
103 header: ast::FnHeader,
104 vis: &'a ast::Visibility,
105 generics: &'a ast::Generics,
106 body: ast::BodyId,
107 id: NodeId,
108 span: Span,
109 attrs: &'a [Attribute],
110 }
111
112 /// These are all the components one can extract from a closure expr
113 /// for use when implementing FnLikeNode operations.
114 struct ClosureParts<'a> {
115 decl: &'a FnDecl,
116 body: ast::BodyId,
117 id: NodeId,
118 span: Span,
119 attrs: &'a [Attribute],
120 }
121
122 impl<'a> ClosureParts<'a> {
123 fn new(d: &'a FnDecl, b: ast::BodyId, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self {
124 ClosureParts {
125 decl: d,
126 body: b,
127 id,
128 span: s,
129 attrs,
130 }
131 }
132 }
133
134 impl<'a> FnLikeNode<'a> {
135 /// Attempts to construct a FnLikeNode from presumed FnLike node input.
136 pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> {
137 let fn_like = match node {
138 map::Node::Item(item) => item.is_fn_like(),
139 map::Node::TraitItem(tm) => tm.is_fn_like(),
140 map::Node::ImplItem(it) => it.is_fn_like(),
141 map::Node::Expr(e) => e.is_fn_like(),
142 _ => false
143 };
144 if fn_like {
145 Some(FnLikeNode {
146 node,
147 })
148 } else {
149 None
150 }
151 }
152
153 pub fn body(self) -> ast::BodyId {
154 self.handle(|i: ItemFnParts<'a>| i.body,
155 |_, _, _: &'a ast::MethodSig, _, body: ast::BodyId, _, _| body,
156 |c: ClosureParts<'a>| c.body)
157 }
158
159 pub fn decl(self) -> &'a FnDecl {
160 self.handle(|i: ItemFnParts<'a>| &*i.decl,
161 |_, _, sig: &'a ast::MethodSig, _, _, _, _| &sig.decl,
162 |c: ClosureParts<'a>| c.decl)
163 }
164
165 pub fn span(self) -> Span {
166 self.handle(|i: ItemFnParts<'_>| i.span,
167 |_, _, _: &'a ast::MethodSig, _, _, span, _| span,
168 |c: ClosureParts<'_>| c.span)
169 }
170
171 pub fn id(self) -> NodeId {
172 self.handle(|i: ItemFnParts<'_>| i.id,
173 |id, _, _: &'a ast::MethodSig, _, _, _, _| id,
174 |c: ClosureParts<'_>| c.id)
175 }
176
177 pub fn constness(self) -> ast::Constness {
178 match self.kind() {
179 FnKind::ItemFn(_, _, header, ..) => header.constness,
180 FnKind::Method(_, m, ..) => m.header.constness,
181 _ => ast::Constness::NotConst
182 }
183 }
184
185 pub fn asyncness(self) -> ast::IsAsync {
186 match self.kind() {
187 FnKind::ItemFn(_, _, header, ..) => header.asyncness,
188 FnKind::Method(_, m, ..) => m.header.asyncness,
189 _ => ast::IsAsync::NotAsync
190 }
191 }
192
193 pub fn unsafety(self) -> ast::Unsafety {
194 match self.kind() {
195 FnKind::ItemFn(_, _, header, ..) => header.unsafety,
196 FnKind::Method(_, m, ..) => m.header.unsafety,
197 _ => ast::Unsafety::Normal
198 }
199 }
200
201 pub fn kind(self) -> FnKind<'a> {
202 let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
203 FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs)
204 };
205 let closure = |c: ClosureParts<'a>| {
206 FnKind::Closure(c.attrs)
207 };
208 let method = |_, ident: Ident, sig: &'a ast::MethodSig, vis, _, _, attrs| {
209 FnKind::Method(ident, sig, vis, attrs)
210 };
211 self.handle(item, method, closure)
212 }
213
214 fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where
215 I: FnOnce(ItemFnParts<'a>) -> A,
216 M: FnOnce(NodeId,
217 Ident,
218 &'a ast::MethodSig,
219 Option<&'a ast::Visibility>,
220 ast::BodyId,
221 Span,
222 &'a [Attribute])
223 -> A,
224 C: FnOnce(ClosureParts<'a>) -> A,
225 {
226 match self.node {
227 map::Node::Item(i) => match i.node {
228 ast::ItemKind::Fn(ref decl, header, ref generics, block) =>
229 item_fn(ItemFnParts {
230 id: i.id,
231 ident: i.ident,
232 decl: &decl,
233 body: block,
234 vis: &i.vis,
235 span: i.span,
236 attrs: &i.attrs,
237 header,
238 generics,
239 }),
240 _ => bug!("item FnLikeNode that is not fn-like"),
241 },
242 map::Node::TraitItem(ti) => match ti.node {
243 ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => {
244 method(ti.id, ti.ident, sig, None, body, ti.span, &ti.attrs)
245 }
246 _ => bug!("trait method FnLikeNode that is not fn-like"),
247 },
248 map::Node::ImplItem(ii) => {
249 match ii.node {
250 ast::ImplItemKind::Method(ref sig, body) => {
251 method(ii.id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs)
252 }
253 _ => bug!("impl method FnLikeNode that is not fn-like")
254 }
255 },
256 map::Node::Expr(e) => match e.node {
257 ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) =>
258 closure(ClosureParts::new(&decl, block, e.id, e.span, &e.attrs)),
259 _ => bug!("expr FnLikeNode that is not fn-like"),
260 },
261 _ => bug!("other FnLikeNode that is not fn-like"),
262 }
263 }
264 }