]> git.lizzy.rs Git - rust.git/blob - src/librustc/hir/map/blocks.rs
Auto merge of #68530 - estebank:abolish-ice, r=petrochenkov
[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::map::Map;
15 use rustc_hir as hir;
16 use rustc_hir::intravisit::FnKind;
17 use rustc_hir::{Expr, FnDecl, Node};
18 use rustc_span::Span;
19 use syntax::ast::{Attribute, Ident};
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> {
33     node: Node<'a>,
34 }
35
36 /// MaybeFnLike wraps a method that indicates if an object
37 /// corresponds to some FnLikeNode.
38 trait MaybeFnLike {
39     fn is_fn_like(&self) -> bool;
40 }
41
42 impl MaybeFnLike for hir::Item<'_> {
43     fn is_fn_like(&self) -> bool {
44         match self.kind {
45             hir::ItemKind::Fn(..) => true,
46             _ => false,
47         }
48     }
49 }
50
51 impl MaybeFnLike for hir::ImplItem<'_> {
52     fn is_fn_like(&self) -> bool {
53         match self.kind {
54             hir::ImplItemKind::Method(..) => true,
55             _ => false,
56         }
57     }
58 }
59
60 impl MaybeFnLike for hir::TraitItem<'_> {
61     fn is_fn_like(&self) -> bool {
62         match self.kind {
63             hir::TraitItemKind::Method(_, hir::TraitMethod::Provided(_)) => true,
64             _ => false,
65         }
66     }
67 }
68
69 impl MaybeFnLike for hir::Expr<'_> {
70     fn is_fn_like(&self) -> bool {
71         match self.kind {
72             hir::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<'a>),
85 }
86
87 impl<'a> Code<'a> {
88     pub fn id(&self) -> hir::HirId {
89         match *self {
90             Code::FnLike(node) => node.id(),
91             Code::Expr(block) => block.hir_id,
92         }
93     }
94
95     /// Attempts to construct a Code from presumed FnLike or Expr node input.
96     pub fn from_node(map: &Map<'a>, id: hir::HirId) -> Option<Code<'a>> {
97         match map.get(id) {
98             Node::Block(_) => {
99                 //  Use the parent, hopefully an expression node.
100                 Code::from_node(map, map.get_parent_node(id))
101             }
102             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     ident: Ident,
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,
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<'a>,
126     body: hir::BodyId,
127     id: hir::HirId,
128     span: Span,
129     attrs: &'a [Attribute],
130 }
131
132 impl<'a> ClosureParts<'a> {
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 {
140         ClosureParts { decl: d, body: b, id, span: s, attrs }
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             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(),
152             _ => false,
153         };
154         fn_like.then_some(FnLikeNode { node })
155     }
156
157     pub fn body(self) -> hir::BodyId {
158         self.handle(
159             |i: ItemFnParts<'a>| i.body,
160             |_, _, _: &'a hir::FnSig<'a>, _, body: hir::BodyId, _, _| body,
161             |c: ClosureParts<'a>| c.body,
162         )
163     }
164
165     pub fn decl(self) -> &'a FnDecl<'a> {
166         self.handle(
167             |i: ItemFnParts<'a>| &*i.decl,
168             |_, _, sig: &'a hir::FnSig<'a>, _, _, _, _| &sig.decl,
169             |c: ClosureParts<'a>| c.decl,
170         )
171     }
172
173     pub fn span(self) -> Span {
174         self.handle(
175             |i: ItemFnParts<'_>| i.span,
176             |_, _, _: &'a hir::FnSig<'a>, _, _, span, _| span,
177             |c: ClosureParts<'_>| c.span,
178         )
179     }
180
181     pub fn id(self) -> hir::HirId {
182         self.handle(
183             |i: ItemFnParts<'_>| i.id,
184             |id, _, _: &'a hir::FnSig<'a>, _, _, _, _| id,
185             |c: ClosureParts<'_>| c.id,
186         )
187     }
188
189     pub fn constness(self) -> hir::Constness {
190         self.kind().header().map_or(hir::Constness::NotConst, |header| header.constness)
191     }
192
193     pub fn asyncness(self) -> hir::IsAsync {
194         self.kind().header().map_or(hir::IsAsync::NotAsync, |header| header.asyncness)
195     }
196
197     pub fn unsafety(self) -> hir::Unsafety {
198         self.kind().header().map_or(hir::Unsafety::Normal, |header| header.unsafety)
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>| FnKind::Closure(c.attrs);
206         let method = |_, ident: Ident, sig: &'a hir::FnSig<'a>, vis, _, _, attrs| {
207             FnKind::Method(ident, sig, vis, attrs)
208         };
209         self.handle(item, method, closure)
210     }
211
212     fn handle<A, I, M, C>(self, item_fn: I, method: M, closure: C) -> A
213     where
214         I: FnOnce(ItemFnParts<'a>) -> A,
215         M: FnOnce(
216             hir::HirId,
217             Ident,
218             &'a hir::FnSig<'a>,
219             Option<&'a hir::Visibility<'a>>,
220             hir::BodyId,
221             Span,
222             &'a [Attribute],
223         ) -> A,
224         C: FnOnce(ClosureParts<'a>) -> A,
225     {
226         match self.node {
227             Node::Item(i) => match i.kind {
228                 hir::ItemKind::Fn(ref sig, ref generics, block) => item_fn(ItemFnParts {
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                 }),
239                 _ => bug!("item FnLikeNode that is not fn-like"),
240             },
241             Node::TraitItem(ti) => match ti.kind {
242                 hir::TraitItemKind::Method(ref sig, hir::TraitMethod::Provided(body)) => {
243                     method(ti.hir_id, ti.ident, sig, None, body, ti.span, &ti.attrs)
244                 }
245                 _ => bug!("trait method FnLikeNode that is not fn-like"),
246             },
247             Node::ImplItem(ii) => match ii.kind {
248                 hir::ImplItemKind::Method(ref sig, body) => {
249                     method(ii.hir_id, ii.ident, sig, Some(&ii.vis), body, ii.span, &ii.attrs)
250                 }
251                 _ => bug!("impl method FnLikeNode that is not fn-like"),
252             },
253             Node::Expr(e) => match e.kind {
254                 hir::ExprKind::Closure(_, ref decl, block, _fn_decl_span, _gen) => {
255                     closure(ClosureParts::new(&decl, block, e.hir_id, e.span, &e.attrs))
256                 }
257                 _ => bug!("expr FnLikeNode that is not fn-like"),
258             },
259             _ => bug!("other FnLikeNode that is not fn-like"),
260         }
261     }
262 }