• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Defines `Body`: a lowered representation of bodies of functions, statics and
2 //! consts.
3 mod lower;
4 #[cfg(test)]
5 mod tests;
6 pub mod scope;
7 mod pretty;
8 
9 use std::ops::Index;
10 
11 use base_db::CrateId;
12 use cfg::{CfgExpr, CfgOptions};
13 use either::Either;
14 use hir_expand::{name::Name, HirFileId, InFile};
15 use la_arena::{Arena, ArenaMap};
16 use profile::Count;
17 use rustc_hash::FxHashMap;
18 use syntax::{ast, AstPtr, SyntaxNodePtr};
19 use triomphe::Arc;
20 
21 use crate::{
22     db::DefDatabase,
23     expander::Expander,
24     hir::{
25         dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
26     },
27     nameres::DefMap,
28     path::{ModPath, Path},
29     src::{HasChildSource, HasSource},
30     BlockId, DefWithBodyId, HasModule, Lookup,
31 };
32 
33 /// The body of an item (function, const etc.).
34 #[derive(Debug, Eq, PartialEq)]
35 pub struct Body {
36     pub exprs: Arena<Expr>,
37     pub pats: Arena<Pat>,
38     pub bindings: Arena<Binding>,
39     pub labels: Arena<Label>,
40     /// Id of the closure/generator that owns the corresponding binding. If a binding is owned by the
41     /// top level expression, it will not be listed in here.
42     pub binding_owners: FxHashMap<BindingId, ExprId>,
43     /// The patterns for the function's parameters. While the parameter types are
44     /// part of the function signature, the patterns are not (they don't change
45     /// the external type of the function).
46     ///
47     /// If this `Body` is for the body of a constant, this will just be
48     /// empty.
49     pub params: Vec<PatId>,
50     /// The `ExprId` of the actual body expression.
51     pub body_expr: ExprId,
52     /// Block expressions in this body that may contain inner items.
53     block_scopes: Vec<BlockId>,
54     _c: Count<Self>,
55 }
56 
57 pub type ExprPtr = AstPtr<ast::Expr>;
58 pub type ExprSource = InFile<ExprPtr>;
59 
60 pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
61 pub type PatSource = InFile<PatPtr>;
62 
63 pub type LabelPtr = AstPtr<ast::Label>;
64 pub type LabelSource = InFile<LabelPtr>;
65 
66 pub type FieldPtr = AstPtr<ast::RecordExprField>;
67 pub type FieldSource = InFile<FieldPtr>;
68 
69 /// An item body together with the mapping from syntax nodes to HIR expression
70 /// IDs. This is needed to go from e.g. a position in a file to the HIR
71 /// expression containing it; but for type inference etc., we want to operate on
72 /// a structure that is agnostic to the actual positions of expressions in the
73 /// file, so that we don't recompute types whenever some whitespace is typed.
74 ///
75 /// One complication here is that, due to macro expansion, a single `Body` might
76 /// be spread across several files. So, for each ExprId and PatId, we record
77 /// both the HirFileId and the position inside the file. However, we only store
78 /// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
79 /// this properly for macros.
80 #[derive(Default, Debug, Eq, PartialEq)]
81 pub struct BodySourceMap {
82     expr_map: FxHashMap<ExprSource, ExprId>,
83     expr_map_back: ArenaMap<ExprId, ExprSource>,
84 
85     pat_map: FxHashMap<PatSource, PatId>,
86     pat_map_back: ArenaMap<PatId, PatSource>,
87 
88     label_map: FxHashMap<LabelSource, LabelId>,
89     label_map_back: ArenaMap<LabelId, LabelSource>,
90 
91     /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
92     /// Instead, we use id of expression (`92`) to identify the field.
93     field_map: FxHashMap<FieldSource, ExprId>,
94     field_map_back: FxHashMap<ExprId, FieldSource>,
95 
96     expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
97 
98     /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in
99     /// the source map (since they're just as volatile).
100     diagnostics: Vec<BodyDiagnostic>,
101 }
102 
103 #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
104 pub struct SyntheticSyntax;
105 
106 #[derive(Debug, Eq, PartialEq)]
107 pub enum BodyDiagnostic {
108     InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
109     MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
110     UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
111     UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
112     UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
113     UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
114 }
115 
116 impl Body {
body_with_source_map_query( db: &dyn DefDatabase, def: DefWithBodyId, ) -> (Arc<Body>, Arc<BodySourceMap>)117     pub(crate) fn body_with_source_map_query(
118         db: &dyn DefDatabase,
119         def: DefWithBodyId,
120     ) -> (Arc<Body>, Arc<BodySourceMap>) {
121         let _p = profile::span("body_with_source_map_query");
122         let mut params = None;
123 
124         let mut is_async_fn = false;
125         let InFile { file_id, value: body } = {
126             match def {
127                 DefWithBodyId::FunctionId(f) => {
128                     let data = db.function_data(f);
129                     let f = f.lookup(db);
130                     let src = f.source(db);
131                     params = src.value.param_list().map(|param_list| {
132                         let item_tree = f.id.item_tree(db);
133                         let func = &item_tree[f.id.value];
134                         let krate = f.container.module(db).krate;
135                         let crate_graph = db.crate_graph();
136                         (
137                             param_list,
138                             func.params.clone().map(move |param| {
139                                 item_tree
140                                     .attrs(db, krate, param.into())
141                                     .is_cfg_enabled(&crate_graph[krate].cfg_options)
142                             }),
143                         )
144                     });
145                     is_async_fn = data.has_async_kw();
146                     src.map(|it| it.body().map(ast::Expr::from))
147                 }
148                 DefWithBodyId::ConstId(c) => {
149                     let c = c.lookup(db);
150                     let src = c.source(db);
151                     src.map(|it| it.body())
152                 }
153                 DefWithBodyId::StaticId(s) => {
154                     let s = s.lookup(db);
155                     let src = s.source(db);
156                     src.map(|it| it.body())
157                 }
158                 DefWithBodyId::VariantId(v) => {
159                     let src = v.parent.child_source(db);
160                     src.map(|it| it[v.local_id].expr())
161                 }
162                 DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
163             }
164         };
165         let module = def.module(db);
166         let expander = Expander::new(db, file_id, module);
167         let (mut body, source_map) =
168             Body::new(db, def, expander, params, body, module.krate, is_async_fn);
169         body.shrink_to_fit();
170 
171         (Arc::new(body), Arc::new(source_map))
172     }
173 
body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body>174     pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
175         db.body_with_source_map(def).0
176     }
177 
178     /// Returns an iterator over all block expressions in this body that define inner items.
blocks<'a>( &'a self, db: &'a dyn DefDatabase, ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_179     pub fn blocks<'a>(
180         &'a self,
181         db: &'a dyn DefDatabase,
182     ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
183         self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
184     }
185 
pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String186     pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
187         pretty::print_body_hir(db, self, owner)
188     }
189 
pretty_print_expr( &self, db: &dyn DefDatabase, owner: DefWithBodyId, expr: ExprId, ) -> String190     pub fn pretty_print_expr(
191         &self,
192         db: &dyn DefDatabase,
193         owner: DefWithBodyId,
194         expr: ExprId,
195     ) -> String {
196         pretty::print_expr_hir(db, self, owner, expr)
197     }
198 
new( db: &dyn DefDatabase, owner: DefWithBodyId, expander: Expander, params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, body: Option<ast::Expr>, krate: CrateId, is_async_fn: bool, ) -> (Body, BodySourceMap)199     fn new(
200         db: &dyn DefDatabase,
201         owner: DefWithBodyId,
202         expander: Expander,
203         params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
204         body: Option<ast::Expr>,
205         krate: CrateId,
206         is_async_fn: bool,
207     ) -> (Body, BodySourceMap) {
208         lower::lower(db, owner, expander, params, body, krate, is_async_fn)
209     }
210 
shrink_to_fit(&mut self)211     fn shrink_to_fit(&mut self) {
212         let Self {
213             _c: _,
214             body_expr: _,
215             block_scopes,
216             exprs,
217             labels,
218             params,
219             pats,
220             bindings,
221             binding_owners,
222         } = self;
223         block_scopes.shrink_to_fit();
224         exprs.shrink_to_fit();
225         labels.shrink_to_fit();
226         params.shrink_to_fit();
227         pats.shrink_to_fit();
228         bindings.shrink_to_fit();
229         binding_owners.shrink_to_fit();
230     }
231 
walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId))232     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
233         self.walk_pats(pat_id, &mut |pat| {
234             if let Pat::Bind { id, .. } = &self[pat] {
235                 f(*id);
236             }
237         });
238     }
239 
walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId))240     pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
241         let pat = &self[pat_id];
242         match pat {
243             Pat::Range { .. }
244             | Pat::Lit(..)
245             | Pat::Path(..)
246             | Pat::ConstBlock(..)
247             | Pat::Wild
248             | Pat::Missing => {}
249             &Pat::Bind { subpat, .. } => {
250                 if let Some(subpat) = subpat {
251                     f(subpat);
252                 }
253             }
254             Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
255                 args.iter().copied().for_each(|p| f(p));
256             }
257             Pat::Ref { pat, .. } => f(*pat),
258             Pat::Slice { prefix, slice, suffix } => {
259                 let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
260                 total_iter.copied().for_each(|p| f(p));
261             }
262             Pat::Record { args, .. } => {
263                 args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
264             }
265             Pat::Box { inner } => f(*inner),
266         }
267     }
268 
walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId))269     pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
270         f(pat_id);
271         self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
272     }
273 
is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool274     pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
275         match self.binding_owners.get(&binding) {
276             Some(x) => {
277                 // We assign expression ids in a way that outer closures will receive
278                 // a lower id
279                 x.into_raw() < relative_to.into_raw()
280             }
281             None => true,
282         }
283     }
284 }
285 
286 impl Default for Body {
default() -> Self287     fn default() -> Self {
288         Self {
289             body_expr: dummy_expr_id(),
290             exprs: Default::default(),
291             pats: Default::default(),
292             bindings: Default::default(),
293             labels: Default::default(),
294             params: Default::default(),
295             block_scopes: Default::default(),
296             binding_owners: Default::default(),
297             _c: Default::default(),
298         }
299     }
300 }
301 
302 impl Index<ExprId> for Body {
303     type Output = Expr;
304 
index(&self, expr: ExprId) -> &Expr305     fn index(&self, expr: ExprId) -> &Expr {
306         &self.exprs[expr]
307     }
308 }
309 
310 impl Index<PatId> for Body {
311     type Output = Pat;
312 
index(&self, pat: PatId) -> &Pat313     fn index(&self, pat: PatId) -> &Pat {
314         &self.pats[pat]
315     }
316 }
317 
318 impl Index<LabelId> for Body {
319     type Output = Label;
320 
index(&self, label: LabelId) -> &Label321     fn index(&self, label: LabelId) -> &Label {
322         &self.labels[label]
323     }
324 }
325 
326 impl Index<BindingId> for Body {
327     type Output = Binding;
328 
index(&self, b: BindingId) -> &Binding329     fn index(&self, b: BindingId) -> &Binding {
330         &self.bindings[b]
331     }
332 }
333 
334 // FIXME: Change `node_` prefix to something more reasonable.
335 // Perhaps `expr_syntax` and `expr_id`?
336 impl BodySourceMap {
expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax>337     pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
338         self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
339     }
340 
node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId>341     pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
342         let src = node.map(AstPtr::new);
343         self.expr_map.get(&src).cloned()
344     }
345 
node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId>346     pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
347         let src = node.map(AstPtr::new);
348         self.expansions.get(&src).cloned()
349     }
350 
pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax>351     pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
352         self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
353     }
354 
node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId>355     pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
356         let src = node.map(|it| Either::Left(AstPtr::new(it)));
357         self.pat_map.get(&src).cloned()
358     }
359 
node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId>360     pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
361         let src = node.map(|it| Either::Right(AstPtr::new(it)));
362         self.pat_map.get(&src).cloned()
363     }
364 
label_syntax(&self, label: LabelId) -> LabelSource365     pub fn label_syntax(&self, label: LabelId) -> LabelSource {
366         self.label_map_back[label].clone()
367     }
368 
node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId>369     pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
370         let src = node.map(AstPtr::new);
371         self.label_map.get(&src).cloned()
372     }
373 
field_syntax(&self, expr: ExprId) -> FieldSource374     pub fn field_syntax(&self, expr: ExprId) -> FieldSource {
375         self.field_map_back[&expr].clone()
376     }
377 
node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId>378     pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
379         let src = node.map(AstPtr::new);
380         self.field_map.get(&src).cloned()
381     }
382 
macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId>383     pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
384         let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
385         self.expr_map.get(&src).copied()
386     }
387 
388     /// Get a reference to the body source map's diagnostics.
diagnostics(&self) -> &[BodyDiagnostic]389     pub fn diagnostics(&self) -> &[BodyDiagnostic] {
390         &self.diagnostics
391     }
392 }
393