]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/map/blocks.rs
a9388825f168f107ca6a697a3e2f82fa43b502cd
[rust.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};
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.kind {
41             ast::ItemKind::Fn(..) => true,
42             _ => false,
43         }
44     }
45 }
46
47 impl MaybeFnLike for ast::ImplItem {
48     fn is_fn_like(&self) -> bool {
49         match self.kind {
50             ast::ImplItemKind::Method(..) => true,
51             _ => false,
52         }
53     }
54 }
55
56 impl MaybeFnLike for ast::TraitItem<'_> {
57     fn is_fn_like(&self) -> bool {
58         match self.kind {
59             ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true,
60             _ => false,
61         }
62     }
63 }
64
65 impl MaybeFnLike for ast::Expr {
66     fn is_fn_like(&self) -> bool {
67         match self.kind {
68             ast::ExprKind::Closure(..) => true,
69             _ => false,
70         }
71     }
72 }
73
74 /// Carries either an FnLikeNode or a Expr, as these are the two
75 /// constructs that correspond to "code" (as in, something from which
76 /// we can construct a control-flow graph).
77 #[derive(Copy, Clone)]
78 pub enum Code<'a> {
79     FnLike(FnLikeNode<'a>),
80     Expr(&'a Expr),
81 }
82
83 impl<'a> Code<'a> {
84     pub fn id(&self) -> ast::HirId {
85         match *self {
86             Code::FnLike(node) => node.id(),
87             Code::Expr(block) => block.hir_id,
88         }
89     }
90
91     /// Attempts to construct a Code from presumed FnLike or Expr node input.
92     pub fn from_node(map: &map::Map<'a>, id: ast::HirId) -> Option<Code<'a>> {
93         match map.get(id) {
94             map::Node::Block(_) => {
95                 //  Use the parent, hopefully an expression node.
96                 Code::from_node(map, map.get_parent_node(id))
97             }
98             map::Node::Expr(expr) => Some(Code::Expr(expr)),
99             node => FnLikeNode::from_node(node).map(Code::FnLike)
100         }
101     }
102 }
103
104 /// These are all the components one can extract from a fn item for
105 /// use when implementing FnLikeNode operations.
106 struct ItemFnParts<'a> {
107     ident:    Ident,
108     decl:     &'a ast::FnDecl,
109     header:   ast::FnHeader,
110     vis:      &'a ast::Visibility,
111     generics: &'a ast::Generics,
112     body:     ast::BodyId,
113     id:       ast::HirId,
114     span:     Span,
115     attrs:    &'a [Attribute],
116 }
117
118 /// These are all the components one can extract from a closure expr
119 /// for use when implementing FnLikeNode operations.
120 struct ClosureParts<'a> {
121     decl: &'a FnDecl,
122     body: ast::BodyId,
123     id: ast::HirId,
124     span: Span,
125     attrs: &'a [Attribute],
126 }
127
128 impl<'a> ClosureParts<'a> {
129     fn new(d: &'a FnDecl, b: ast::BodyId, id: ast::HirId, s: Span, attrs: &'a [Attribute]) -> Self {
130         ClosureParts {
131             decl: d,
132             body: b,
133             id,
134             span: s,
135             attrs,
136         }
137     }
138 }
139
140 impl<'a> FnLikeNode<'a> {
141     /// Attempts to construct a FnLikeNode from presumed FnLike node input.
142     pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> {
143         let fn_like = match node {
144             map::Node::Item(item) => item.is_fn_like(),
145             map::Node::TraitItem(tm) => tm.is_fn_like(),
146             map::Node::ImplItem(it) => it.is_fn_like(),
147             map::Node::Expr(e) => e.is_fn_like(),
148             _ => false
149         };
150         fn_like.then_some(FnLikeNode { node })
151     }
152
153     pub fn body(self) -> ast::BodyId {
154         self.handle(|i: ItemFnParts<'a>| i.body,
155                     |_, _, _: &'a ast::FnSig, _, 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::FnSig, _, _, _, _| &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::FnSig, _, _, span, _| span,
168                     |c: ClosureParts<'_>| c.span)
169     }
170
171     pub fn id(self) -> ast::HirId {
172         self.handle(|i: ItemFnParts<'_>| i.id,
173                     |id, _, _: &'a ast::FnSig, _, _, _, _| id,
174                     |c: ClosureParts<'_>| c.id)
175     }
176
177     pub fn constness(self) -> ast::Constness {
178         self.kind().header().map_or(ast::Constness::NotConst, |header| header.constness)
179     }
180
181     pub fn asyncness(self) -> ast::IsAsync {
182         self.kind().header().map_or(ast::IsAsync::NotAsync, |header| header.asyncness)
183     }
184
185     pub fn unsafety(self) -> ast::Unsafety {
186         self.kind().header().map_or(ast::Unsafety::Normal, |header| header.unsafety)
187     }
188
189     pub fn kind(self) -> FnKind<'a> {
190         let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
191             FnKind::ItemFn(p.ident, p.generics, p.header, p.vis, p.attrs)
192         };
193         let closure = |c: ClosureParts<'a>| {
194             FnKind::Closure(c.attrs)
195         };
196         let method = |_, ident: Ident, sig: &'a ast::FnSig, vis, _, _, attrs| {
197             FnKind::Method(ident, sig, vis, attrs)
198         };
199         self.handle(item, method, closure)
200     }
201
202     fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A where
203         I: FnOnce(ItemFnParts<'a>) -> A,
204         M: FnOnce(ast::HirId,
205                   Ident,
206                   &'a ast::FnSig,
207                   Option<&'a ast::Visibility>,
208                   ast::BodyId,
209                   Span,
210                   &'a [Attribute])
211                   -> A,
212         C: FnOnce(ClosureParts<'a>) -> A,
213     {
214         match self.node {
215             map::Node::Item(i) => match i.kind {
216                 ast::ItemKind::Fn(ref sig, ref generics, block) =>
217                     item_fn(ItemFnParts {
218                         id: i.hir_id,
219                         ident: i.ident,
220                         decl: &sig.decl,
221                         body: block,
222                         vis: &i.vis,
223                         span: i.span,
224                         attrs: &i.attrs,
225                         header: sig.header,
226                         generics,
227                     }),
228                 _ => bug!("item FnLikeNode that is not fn-like"),
229             },
230             map::Node::TraitItem(ti) => match ti.kind {
231                 ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => {
232                     method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs)
233                 }
234                 _ => bug!("trait method FnLikeNode that is not fn-like"),
235             },
236             map::Node::ImplItem(ii) => {
237                 match ii.kind {
238                     ast::ImplItemKind::Method(ref sig, body) => {
239                         method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs)
240                     }
241                     _ => bug!("impl method FnLikeNode that is not fn-like")
242                 }
243             },
244             map::Node::Expr(e) => match e.kind {
245                 ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) =>
246                     closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs)),
247                 _ => bug!("expr FnLikeNode that is not fn-like"),
248             },
249             _ => bug!("other FnLikeNode that is not fn-like"),
250         }
251     }
252 }