]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/map/blocks.rs
1ab1c7d3fc5c433655b0c807997c5e1cebe26080
[rust.git] / src / librustc / hir / map / blocks.rs
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 use hir as ast;
25 use hir::map;
26 use hir::{Expr, FnDecl, Node};
27 use hir::intravisit::FnKind;
28 use syntax::ast::{Attribute, Ident, Name, NodeId};
29 use syntax_pos::Span;
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:
35 ///
36 ///   - A function item,
37 ///   - A closure expr (i.e. an ExprKind::Closure), or
38 ///   - The default implementation for a trait method.
39 ///
40 /// To construct one, use the `Code::from_node` function.
41 #[derive(Copy, Clone, Debug)]
42 pub struct FnLikeNode<'a> { node: Node<'a> }
43
44 /// MaybeFnLike wraps a method that indicates if an object
45 /// corresponds to some FnLikeNode.
46 trait MaybeFnLike { fn is_fn_like(&self) -> bool; }
47
48 impl MaybeFnLike for ast::Item {
49     fn is_fn_like(&self) -> bool {
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, }
57     }
58 }
59
60 impl MaybeFnLike for ast::TraitItem {
61     fn is_fn_like(&self) -> bool {
62         match self.node {
63             ast::TraitItemKind::Method(_, ast::TraitMethod::Provided(_)) => true,
64             _ => false,
65         }
66     }
67 }
68
69 impl MaybeFnLike for ast::Expr {
70     fn is_fn_like(&self) -> bool {
71         match self.node {
72             ast::ExprKind::Closure(..) => true,
73             _ => false,
74         }
75     }
76 }
77
78 /// Carries either an FnLikeNode or a Expr, as these are the two
79 /// constructs that correspond to "code" (as in, something from which
80 /// we can construct a control-flow graph).
81 #[derive(Copy, Clone)]
82 pub enum Code<'a> {
83     FnLike(FnLikeNode<'a>),
84     Expr(&'a Expr),
85 }
86
87 impl<'a> Code<'a> {
88     pub fn id(&self) -> NodeId {
89         match *self {
90             Code::FnLike(node) => node.id(),
91             Code::Expr(block) => block.id,
92         }
93     }
94
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) {
98             map::Node::Block(_) => {
99                 //  Use the parent, hopefully an expression node.
100                 Code::from_node(map, map.get_parent_node(id))
101             }
102             map::Node::Expr(expr) => Some(Code::Expr(expr)),
103             node => FnLikeNode::from_node(node).map(Code::FnLike)
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> {
111     name:     Name,
112     decl:     &'a ast::FnDecl,
113     header:   ast::FnHeader,
114     vis:      &'a ast::Visibility,
115     generics: &'a ast::Generics,
116     body:     ast::BodyId,
117     id:       NodeId,
118     span:     Span,
119     attrs:    &'a [Attribute],
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,
126     body: ast::BodyId,
127     id: NodeId,
128     span: Span,
129     attrs: &'a [Attribute],
130 }
131
132 impl<'a> ClosureParts<'a> {
133     fn new(d: &'a FnDecl, b: ast::BodyId, id: NodeId, s: Span, attrs: &'a [Attribute]) -> Self {
134         ClosureParts {
135             decl: d,
136             body: b,
137             id,
138             span: s,
139             attrs,
140         }
141     }
142 }
143
144 impl<'a> FnLikeNode<'a> {
145     /// Attempts to construct a FnLikeNode from presumed FnLike node input.
146     pub fn from_node(node: Node<'_>) -> Option<FnLikeNode<'_>> {
147         let fn_like = match node {
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(),
152             _ => false
153         };
154         if fn_like {
155             Some(FnLikeNode {
156                 node,
157             })
158         } else {
159             None
160         }
161     }
162
163     pub fn body(self) -> ast::BodyId {
164         self.handle(|i: ItemFnParts<'a>| i.body,
165                     |_, _, _: &'a ast::MethodSig, _, body: ast::BodyId, _, _| body,
166                     |c: ClosureParts<'a>| c.body)
167     }
168
169     pub fn decl(self) -> &'a FnDecl {
170         self.handle(|i: ItemFnParts<'a>| &*i.decl,
171                     |_, _, sig: &'a ast::MethodSig, _, _, _, _| &sig.decl,
172                     |c: ClosureParts<'a>| c.decl)
173     }
174
175     pub fn span(self) -> Span {
176         self.handle(|i: ItemFnParts<'_>| i.span,
177                     |_, _, _: &'a ast::MethodSig, _, _, span, _| span,
178                     |c: ClosureParts<'_>| c.span)
179     }
180
181     pub fn id(self) -> NodeId {
182         self.handle(|i: ItemFnParts<'_>| i.id,
183                     |id, _, _: &'a ast::MethodSig, _, _, _, _| id,
184                     |c: ClosureParts<'_>| c.id)
185     }
186
187     pub fn constness(self) -> ast::Constness {
188         match self.kind() {
189             FnKind::ItemFn(_, _, header, ..) => header.constness,
190             FnKind::Method(_, m, ..) => m.header.constness,
191             _ => ast::Constness::NotConst
192         }
193     }
194
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
203     pub fn unsafety(self) -> ast::Unsafety {
204         match self.kind() {
205             FnKind::ItemFn(_, _, header, ..) => header.unsafety,
206             FnKind::Method(_, m, ..) => m.header.unsafety,
207             _ => ast::Unsafety::Normal
208         }
209     }
210
211     pub fn kind(self) -> FnKind<'a> {
212         let item = |p: ItemFnParts<'a>| -> FnKind<'a> {
213             FnKind::ItemFn(p.name, p.generics, p.header, p.vis, p.attrs)
214         };
215         let closure = |c: ClosureParts<'a>| {
216             FnKind::Closure(c.attrs)
217         };
218         let method = |_, ident: Ident, sig: &'a ast::MethodSig, vis, _, _, attrs| {
219             FnKind::Method(ident, sig, vis, attrs)
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,
226         M: FnOnce(NodeId,
227                   Ident,
228                   &'a ast::MethodSig,
229                   Option<&'a ast::Visibility>,
230                   ast::BodyId,
231                   Span,
232                   &'a [Attribute])
233                   -> A,
234         C: FnOnce(ClosureParts<'a>) -> A,
235     {
236         match self.node {
237             map::Node::Item(i) => match i.node {
238                 ast::ItemKind::Fn(ref decl, header, ref generics, block) =>
239                     item_fn(ItemFnParts {
240                         id: i.id,
241                         name: i.name,
242                         decl: &decl,
243                         body: block,
244                         vis: &i.vis,
245                         span: i.span,
246                         attrs: &i.attrs,
247                         header,
248                         generics,
249                     }),
250                 _ => bug!("item FnLikeNode that is not fn-like"),
251             },
252             map::Node::TraitItem(ti) => match ti.node {
253                 ast::TraitItemKind::Method(ref sig, ast::TraitMethod::Provided(body)) => {
254                     method(ti.id, ti.ident, sig, None, body, ti.span, &ti.attrs)
255                 }
256                 _ => bug!("trait method FnLikeNode that is not fn-like"),
257             },
258             map::Node::ImplItem(ii) => {
259                 match ii.node {
260                     ast::ImplItemKind::Method(ref sig, body) => {
261                         method(ii.id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs)
262                     }
263                     _ => bug!("impl method FnLikeNode that is not fn-like")
264                 }
265             },
266             map::Node::Expr(e) => match e.node {
267                 ast::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) =>
268                     closure(ClosureParts::new(&decl, block, e.id, e.span, &e.attrs)),
269                 _ => bug!("expr FnLikeNode that is not fn-like"),
270             },
271             _ => bug!("other FnLikeNode that is not fn-like"),
272         }
273     }
274 }