aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/body.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_def/src/body.rs')
-rw-r--r--crates/ra_hir_def/src/body.rs361
1 files changed, 0 insertions, 361 deletions
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
deleted file mode 100644
index d5f18b920..000000000
--- a/crates/ra_hir_def/src/body.rs
+++ /dev/null
@@ -1,361 +0,0 @@
1//! Defines `Body`: a lowered representation of bodies of functions, statics and
2//! consts.
3mod lower;
4pub mod scope;
5
6use std::{mem, ops::Index, sync::Arc};
7
8use drop_bomb::DropBomb;
9use either::Either;
10use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
11use ra_arena::{map::ArenaMap, Arena};
12use ra_cfg::CfgOptions;
13use ra_db::CrateId;
14use ra_prof::profile;
15use ra_syntax::{ast, AstNode, AstPtr};
16use rustc_hash::FxHashMap;
17use test_utils::mark;
18
19pub(crate) use lower::LowerCtx;
20
21use crate::{
22 attr::Attrs,
23 db::DefDatabase,
24 expr::{Expr, ExprId, Pat, PatId},
25 item_scope::BuiltinShadowMode,
26 item_scope::ItemScope,
27 nameres::CrateDefMap,
28 path::{ModPath, Path},
29 src::HasSource,
30 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
31};
32
33/// A subset of Expander that only deals with cfg attributes. We only need it to
34/// avoid cyclic queries in crate def map during enum processing.
35pub(crate) struct CfgExpander {
36 cfg_options: CfgOptions,
37 hygiene: Hygiene,
38}
39
40pub(crate) struct Expander {
41 cfg_expander: CfgExpander,
42 crate_def_map: Arc<CrateDefMap>,
43 current_file_id: HirFileId,
44 ast_id_map: Arc<AstIdMap>,
45 module: ModuleId,
46 recursion_limit: usize,
47}
48
49#[cfg(test)]
50const EXPANSION_RECURSION_LIMIT: usize = 32;
51
52#[cfg(not(test))]
53const EXPANSION_RECURSION_LIMIT: usize = 128;
54
55impl CfgExpander {
56 pub(crate) fn new(
57 db: &dyn DefDatabase,
58 current_file_id: HirFileId,
59 krate: CrateId,
60 ) -> CfgExpander {
61 let hygiene = Hygiene::new(db.upcast(), current_file_id);
62 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
63 CfgExpander { cfg_options, hygiene }
64 }
65
66 pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
67 Attrs::new(owner, &self.hygiene)
68 }
69
70 pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
71 let attrs = self.parse_attrs(owner);
72 attrs.is_cfg_enabled(&self.cfg_options)
73 }
74}
75
76impl Expander {
77 pub(crate) fn new(
78 db: &dyn DefDatabase,
79 current_file_id: HirFileId,
80 module: ModuleId,
81 ) -> Expander {
82 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
83 let crate_def_map = db.crate_def_map(module.krate);
84 let ast_id_map = db.ast_id_map(current_file_id);
85 Expander {
86 cfg_expander,
87 crate_def_map,
88 current_file_id,
89 ast_id_map,
90 module,
91 recursion_limit: 0,
92 }
93 }
94
95 pub(crate) fn enter_expand<T: ast::AstNode>(
96 &mut self,
97 db: &dyn DefDatabase,
98 local_scope: Option<&ItemScope>,
99 macro_call: ast::MacroCall,
100 ) -> Option<(Mark, T)> {
101 self.recursion_limit += 1;
102 if self.recursion_limit > EXPANSION_RECURSION_LIMIT {
103 mark::hit!(your_stack_belongs_to_me);
104 return None;
105 }
106
107 let macro_call = InFile::new(self.current_file_id, &macro_call);
108
109 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
110 if let Some(local_scope) = local_scope {
111 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
112 return Some(def);
113 }
114 }
115 self.resolve_path_as_macro(db, &path)
116 }) {
117 let file_id = call_id.as_file();
118 if let Some(node) = db.parse_or_expand(file_id) {
119 if let Some(expr) = T::cast(node) {
120 log::debug!("macro expansion {:#?}", expr.syntax());
121
122 let mark = Mark {
123 file_id: self.current_file_id,
124 ast_id_map: mem::take(&mut self.ast_id_map),
125 bomb: DropBomb::new("expansion mark dropped"),
126 };
127 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
128 self.current_file_id = file_id;
129 self.ast_id_map = db.ast_id_map(file_id);
130 return Some((mark, expr));
131 }
132 }
133 }
134
135 // FIXME: Instead of just dropping the error from expansion
136 // report it
137 None
138 }
139
140 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
141 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
142 self.current_file_id = mark.file_id;
143 self.ast_id_map = mem::take(&mut mark.ast_id_map);
144 self.recursion_limit -= 1;
145 mark.bomb.defuse();
146 }
147
148 pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
149 InFile { file_id: self.current_file_id, value }
150 }
151
152 pub(crate) fn is_cfg_enabled(&self, owner: &dyn ast::AttrsOwner) -> bool {
153 self.cfg_expander.is_cfg_enabled(owner)
154 }
155
156 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
157 Path::from_src(path, &self.cfg_expander.hygiene)
158 }
159
160 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
161 self.crate_def_map
162 .resolve_path(db, self.module.local_id, path, BuiltinShadowMode::Other)
163 .0
164 .take_macros()
165 }
166
167 fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
168 let file_local_id = self.ast_id_map.ast_id(item);
169 AstId::new(self.current_file_id, file_local_id)
170 }
171}
172
173pub(crate) struct Mark {
174 file_id: HirFileId,
175 ast_id_map: Arc<AstIdMap>,
176 bomb: DropBomb,
177}
178
179/// The body of an item (function, const etc.).
180#[derive(Debug, Eq, PartialEq)]
181pub struct Body {
182 pub exprs: Arena<Expr>,
183 pub pats: Arena<Pat>,
184 /// The patterns for the function's parameters. While the parameter types are
185 /// part of the function signature, the patterns are not (they don't change
186 /// the external type of the function).
187 ///
188 /// If this `Body` is for the body of a constant, this will just be
189 /// empty.
190 pub params: Vec<PatId>,
191 /// The `ExprId` of the actual body expression.
192 pub body_expr: ExprId,
193 pub item_scope: ItemScope,
194}
195
196pub type ExprPtr = AstPtr<ast::Expr>;
197pub type ExprSource = InFile<ExprPtr>;
198
199pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
200pub type PatSource = InFile<PatPtr>;
201
202/// An item body together with the mapping from syntax nodes to HIR expression
203/// IDs. This is needed to go from e.g. a position in a file to the HIR
204/// expression containing it; but for type inference etc., we want to operate on
205/// a structure that is agnostic to the actual positions of expressions in the
206/// file, so that we don't recompute types whenever some whitespace is typed.
207///
208/// One complication here is that, due to macro expansion, a single `Body` might
209/// be spread across several files. So, for each ExprId and PatId, we record
210/// both the HirFileId and the position inside the file. However, we only store
211/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
212/// this properly for macros.
213#[derive(Default, Debug, Eq, PartialEq)]
214pub struct BodySourceMap {
215 expr_map: FxHashMap<ExprSource, ExprId>,
216 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>,
217 pat_map: FxHashMap<PatSource, PatId>,
218 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>,
219 field_map: FxHashMap<(ExprId, usize), InFile<AstPtr<ast::RecordExprField>>>,
220 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
221}
222
223#[derive(Default, Debug, Eq, PartialEq, Clone, Copy)]
224pub struct SyntheticSyntax;
225
226impl Body {
227 pub(crate) fn body_with_source_map_query(
228 db: &dyn DefDatabase,
229 def: DefWithBodyId,
230 ) -> (Arc<Body>, Arc<BodySourceMap>) {
231 let _p = profile("body_with_source_map_query");
232 let mut params = None;
233
234 let (file_id, module, body) = match def {
235 DefWithBodyId::FunctionId(f) => {
236 let f = f.lookup(db);
237 let src = f.source(db);
238 params = src.value.param_list();
239 (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
240 }
241 DefWithBodyId::ConstId(c) => {
242 let c = c.lookup(db);
243 let src = c.source(db);
244 (src.file_id, c.module(db), src.value.body())
245 }
246 DefWithBodyId::StaticId(s) => {
247 let s = s.lookup(db);
248 let src = s.source(db);
249 (src.file_id, s.module(db), src.value.body())
250 }
251 };
252 let expander = Expander::new(db, file_id, module);
253 let (body, source_map) = Body::new(db, def, expander, params, body);
254 (Arc::new(body), Arc::new(source_map))
255 }
256
257 pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
258 db.body_with_source_map(def).0
259 }
260
261 fn new(
262 db: &dyn DefDatabase,
263 def: DefWithBodyId,
264 expander: Expander,
265 params: Option<ast::ParamList>,
266 body: Option<ast::Expr>,
267 ) -> (Body, BodySourceMap) {
268 lower::lower(db, def, expander, params, body)
269 }
270}
271
272impl Index<ExprId> for Body {
273 type Output = Expr;
274
275 fn index(&self, expr: ExprId) -> &Expr {
276 &self.exprs[expr]
277 }
278}
279
280impl Index<PatId> for Body {
281 type Output = Pat;
282
283 fn index(&self, pat: PatId) -> &Pat {
284 &self.pats[pat]
285 }
286}
287
288impl BodySourceMap {
289 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
290 self.expr_map_back[expr].clone()
291 }
292
293 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
294 let src = node.map(|it| AstPtr::new(it));
295 self.expr_map.get(&src).cloned()
296 }
297
298 pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> {
299 let src = node.map(|it| AstPtr::new(it));
300 self.expansions.get(&src).cloned()
301 }
302
303 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
304 self.pat_map_back[pat].clone()
305 }
306
307 pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
308 let src = node.map(|it| Either::Left(AstPtr::new(it)));
309 self.pat_map.get(&src).cloned()
310 }
311
312 pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> {
313 let src = node.map(|it| Either::Right(AstPtr::new(it)));
314 self.pat_map.get(&src).cloned()
315 }
316
317 pub fn field_syntax(&self, expr: ExprId, field: usize) -> InFile<AstPtr<ast::RecordExprField>> {
318 self.field_map[&(expr, field)].clone()
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use ra_db::{fixture::WithFixture, SourceDatabase};
325 use test_utils::mark;
326
327 use crate::ModuleDefId;
328
329 use super::*;
330
331 fn lower(ra_fixture: &str) -> Arc<Body> {
332 let (db, file_id) = crate::test_db::TestDB::with_single_file(ra_fixture);
333
334 let krate = db.crate_graph().iter().next().unwrap();
335 let def_map = db.crate_def_map(krate);
336 let module = def_map.modules_for_file(file_id).next().unwrap();
337 let module = &def_map[module];
338 let fn_def = match module.scope.declarations().next().unwrap() {
339 ModuleDefId::FunctionId(it) => it,
340 _ => panic!(),
341 };
342
343 db.body(fn_def.into())
344 }
345
346 #[test]
347 fn your_stack_belongs_to_me() {
348 mark::check!(your_stack_belongs_to_me);
349 lower(
350 "
351macro_rules! n_nuple {
352 ($e:tt) => ();
353 ($($rest:tt)*) => {{
354 (n_nuple!($($rest)*)None,)
355 }};
356}
357fn main() { n_nuple!(1,2,3); }
358",
359 );
360 }
361}