diff options
Diffstat (limited to 'crates/ra_hir_def/src/body.rs')
-rw-r--r-- | crates/ra_hir_def/src/body.rs | 361 |
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. | ||
3 | mod lower; | ||
4 | pub mod scope; | ||
5 | |||
6 | use std::{mem, ops::Index, sync::Arc}; | ||
7 | |||
8 | use drop_bomb::DropBomb; | ||
9 | use either::Either; | ||
10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; | ||
11 | use ra_arena::{map::ArenaMap, Arena}; | ||
12 | use ra_cfg::CfgOptions; | ||
13 | use ra_db::CrateId; | ||
14 | use ra_prof::profile; | ||
15 | use ra_syntax::{ast, AstNode, AstPtr}; | ||
16 | use rustc_hash::FxHashMap; | ||
17 | use test_utils::mark; | ||
18 | |||
19 | pub(crate) use lower::LowerCtx; | ||
20 | |||
21 | use 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. | ||
35 | pub(crate) struct CfgExpander { | ||
36 | cfg_options: CfgOptions, | ||
37 | hygiene: Hygiene, | ||
38 | } | ||
39 | |||
40 | pub(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)] | ||
50 | const EXPANSION_RECURSION_LIMIT: usize = 32; | ||
51 | |||
52 | #[cfg(not(test))] | ||
53 | const EXPANSION_RECURSION_LIMIT: usize = 128; | ||
54 | |||
55 | impl 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 | |||
76 | impl 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, ¯o_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 | |||
173 | pub(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)] | ||
181 | pub 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 | |||
196 | pub type ExprPtr = AstPtr<ast::Expr>; | ||
197 | pub type ExprSource = InFile<ExprPtr>; | ||
198 | |||
199 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; | ||
200 | pub 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)] | ||
214 | pub 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)] | ||
224 | pub struct SyntheticSyntax; | ||
225 | |||
226 | impl 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 | |||
272 | impl Index<ExprId> for Body { | ||
273 | type Output = Expr; | ||
274 | |||
275 | fn index(&self, expr: ExprId) -> &Expr { | ||
276 | &self.exprs[expr] | ||
277 | } | ||
278 | } | ||
279 | |||
280 | impl Index<PatId> for Body { | ||
281 | type Output = Pat; | ||
282 | |||
283 | fn index(&self, pat: PatId) -> &Pat { | ||
284 | &self.pats[pat] | ||
285 | } | ||
286 | } | ||
287 | |||
288 | impl 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)] | ||
323 | mod 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 | " | ||
351 | macro_rules! n_nuple { | ||
352 | ($e:tt) => (); | ||
353 | ($($rest:tt)*) => {{ | ||
354 | (n_nuple!($($rest)*)None,) | ||
355 | }}; | ||
356 | } | ||
357 | fn main() { n_nuple!(1,2,3); } | ||
358 | ", | ||
359 | ); | ||
360 | } | ||
361 | } | ||