diff options
Diffstat (limited to 'crates/ra_hir/src/expr.rs')
-rw-r--r-- | crates/ra_hir/src/expr.rs | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs new file mode 100644 index 000000000..5cdcca082 --- /dev/null +++ b/crates/ra_hir/src/expr.rs | |||
@@ -0,0 +1,507 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashMap; | ||
4 | |||
5 | use ra_arena::{Arena, RawId, impl_arena_id}; | ||
6 | use ra_db::{LocalSyntaxPtr, Cancelable}; | ||
7 | use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner}; | ||
8 | |||
9 | use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; | ||
10 | |||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
12 | pub struct ExprId(RawId); | ||
13 | impl_arena_id!(ExprId); | ||
14 | |||
15 | /// The body of an item (function, const etc.). | ||
16 | #[derive(Debug, Eq, PartialEq)] | ||
17 | pub struct Body { | ||
18 | exprs: Arena<ExprId, Expr>, | ||
19 | pats: Arena<PatId, Pat>, | ||
20 | /// The patterns for the function's arguments. While the argument types are | ||
21 | /// part of the function signature, the patterns are not (they don't change | ||
22 | /// the external type of the function). | ||
23 | /// | ||
24 | /// If this `ExprTable` is for the body of a constant, this will just be | ||
25 | /// empty. | ||
26 | args: Vec<PatId>, | ||
27 | /// The `ExprId` of the actual body expression. | ||
28 | body_expr: ExprId, | ||
29 | } | ||
30 | |||
31 | /// An item body together with the mapping from syntax nodes to HIR expression | ||
32 | /// IDs. This is needed to go from e.g. a position in a file to the HIR | ||
33 | /// expression containing it; but for type inference etc., we want to operate on | ||
34 | /// a structure that is agnostic to the actual positions of expressions in the | ||
35 | /// file, so that we don't recompute the type inference whenever some whitespace | ||
36 | /// is typed. | ||
37 | #[derive(Debug, Eq, PartialEq)] | ||
38 | pub struct BodySyntaxMapping { | ||
39 | body: Arc<Body>, | ||
40 | expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>, | ||
41 | expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>, | ||
42 | pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>, | ||
43 | pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>, | ||
44 | } | ||
45 | |||
46 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
47 | pub enum Expr { | ||
48 | /// This is produced if syntax tree does not have a required expression piece. | ||
49 | Missing, | ||
50 | Path(Path), | ||
51 | If { | ||
52 | condition: ExprId, | ||
53 | then_branch: ExprId, | ||
54 | else_branch: Option<ExprId>, | ||
55 | }, | ||
56 | Block { | ||
57 | statements: Vec<Statement>, | ||
58 | tail: Option<ExprId>, | ||
59 | }, | ||
60 | Loop { | ||
61 | body: ExprId, | ||
62 | }, | ||
63 | While { | ||
64 | condition: ExprId, | ||
65 | body: ExprId, | ||
66 | }, | ||
67 | For { | ||
68 | iterable: ExprId, | ||
69 | pat: PatId, | ||
70 | body: ExprId, | ||
71 | }, | ||
72 | Call { | ||
73 | callee: ExprId, | ||
74 | args: Vec<ExprId>, | ||
75 | }, | ||
76 | MethodCall { | ||
77 | receiver: ExprId, | ||
78 | method_name: Name, | ||
79 | args: Vec<ExprId>, | ||
80 | }, | ||
81 | Match { | ||
82 | expr: ExprId, | ||
83 | arms: Vec<MatchArm>, | ||
84 | }, | ||
85 | Continue, | ||
86 | Break { | ||
87 | expr: Option<ExprId>, | ||
88 | }, | ||
89 | Return { | ||
90 | expr: Option<ExprId>, | ||
91 | }, | ||
92 | StructLit { | ||
93 | path: Option<Path>, | ||
94 | fields: Vec<StructLitField>, | ||
95 | spread: Option<ExprId>, | ||
96 | }, | ||
97 | Field { | ||
98 | expr: ExprId, | ||
99 | name: Name, | ||
100 | }, | ||
101 | Try { | ||
102 | expr: ExprId, | ||
103 | }, | ||
104 | Cast { | ||
105 | expr: ExprId, | ||
106 | type_ref: TypeRef, | ||
107 | }, | ||
108 | Ref { | ||
109 | expr: ExprId, | ||
110 | mutability: Mutability, | ||
111 | }, | ||
112 | UnaryOp { | ||
113 | expr: ExprId, | ||
114 | op: Option<UnaryOp>, | ||
115 | }, | ||
116 | } | ||
117 | |||
118 | pub type UnaryOp = ast::PrefixOp; | ||
119 | |||
120 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
121 | pub struct MatchArm { | ||
122 | pats: Vec<PatId>, | ||
123 | // guard: Option<ExprId>, // TODO | ||
124 | expr: ExprId, | ||
125 | } | ||
126 | |||
127 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
128 | pub struct StructLitField { | ||
129 | name: Name, | ||
130 | expr: ExprId, | ||
131 | } | ||
132 | |||
133 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
134 | pub enum Statement { | ||
135 | Let { | ||
136 | pat: PatId, | ||
137 | type_ref: Option<TypeRef>, | ||
138 | initializer: Option<ExprId>, | ||
139 | }, | ||
140 | Expr(ExprId), | ||
141 | } | ||
142 | |||
143 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
144 | pub struct PatId(RawId); | ||
145 | impl_arena_id!(PatId); | ||
146 | |||
147 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
148 | pub struct Pat; | ||
149 | |||
150 | // Queries | ||
151 | |||
152 | pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> { | ||
153 | Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body)) | ||
154 | } | ||
155 | |||
156 | struct ExprCollector { | ||
157 | exprs: Arena<ExprId, Expr>, | ||
158 | pats: Arena<PatId, Pat>, | ||
159 | expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>, | ||
160 | expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>, | ||
161 | pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>, | ||
162 | pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>, | ||
163 | } | ||
164 | |||
165 | impl ExprCollector { | ||
166 | fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId { | ||
167 | let id = self.exprs.alloc(expr); | ||
168 | self.expr_syntax_mapping.insert(syntax_ptr, id); | ||
169 | self.expr_syntax_mapping_back.insert(id, syntax_ptr); | ||
170 | id | ||
171 | } | ||
172 | |||
173 | fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId { | ||
174 | let id = self.pats.alloc(pat); | ||
175 | self.pat_syntax_mapping.insert(syntax_ptr, id); | ||
176 | self.pat_syntax_mapping_back.insert(id, syntax_ptr); | ||
177 | id | ||
178 | } | ||
179 | |||
180 | fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { | ||
181 | let syntax_ptr = LocalSyntaxPtr::new(expr.syntax()); | ||
182 | match expr { | ||
183 | ast::Expr::IfExpr(e) => { | ||
184 | let condition = if let Some(condition) = e.condition() { | ||
185 | if condition.pat().is_none() { | ||
186 | self.collect_expr_opt(condition.expr()) | ||
187 | } else { | ||
188 | // TODO handle if let | ||
189 | return self.alloc_expr(Expr::Missing, syntax_ptr); | ||
190 | } | ||
191 | } else { | ||
192 | self.exprs.alloc(Expr::Missing) | ||
193 | }; | ||
194 | let then_branch = self.collect_block_opt(e.then_branch()); | ||
195 | let else_branch = e.else_branch().map(|e| self.collect_block(e)); | ||
196 | self.alloc_expr( | ||
197 | Expr::If { | ||
198 | condition, | ||
199 | then_branch, | ||
200 | else_branch, | ||
201 | }, | ||
202 | syntax_ptr, | ||
203 | ) | ||
204 | } | ||
205 | ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()), | ||
206 | ast::Expr::LoopExpr(e) => { | ||
207 | let body = self.collect_block_opt(e.loop_body()); | ||
208 | self.alloc_expr(Expr::Loop { body }, syntax_ptr) | ||
209 | } | ||
210 | ast::Expr::WhileExpr(e) => { | ||
211 | let condition = if let Some(condition) = e.condition() { | ||
212 | if condition.pat().is_none() { | ||
213 | self.collect_expr_opt(condition.expr()) | ||
214 | } else { | ||
215 | // TODO handle while let | ||
216 | return self.alloc_expr(Expr::Missing, syntax_ptr); | ||
217 | } | ||
218 | } else { | ||
219 | self.exprs.alloc(Expr::Missing) | ||
220 | }; | ||
221 | let body = self.collect_block_opt(e.loop_body()); | ||
222 | self.alloc_expr(Expr::While { condition, body }, syntax_ptr) | ||
223 | } | ||
224 | ast::Expr::ForExpr(e) => { | ||
225 | let iterable = self.collect_expr_opt(e.iterable()); | ||
226 | let pat = self.collect_pat_opt(e.pat()); | ||
227 | let body = self.collect_block_opt(e.loop_body()); | ||
228 | self.alloc_expr( | ||
229 | Expr::For { | ||
230 | iterable, | ||
231 | pat, | ||
232 | body, | ||
233 | }, | ||
234 | syntax_ptr, | ||
235 | ) | ||
236 | } | ||
237 | ast::Expr::CallExpr(e) => { | ||
238 | let callee = self.collect_expr_opt(e.expr()); | ||
239 | let args = if let Some(arg_list) = e.arg_list() { | ||
240 | arg_list.args().map(|e| self.collect_expr(e)).collect() | ||
241 | } else { | ||
242 | Vec::new() | ||
243 | }; | ||
244 | self.alloc_expr(Expr::Call { callee, args }, syntax_ptr) | ||
245 | } | ||
246 | ast::Expr::MethodCallExpr(e) => { | ||
247 | let receiver = self.collect_expr_opt(e.expr()); | ||
248 | let args = if let Some(arg_list) = e.arg_list() { | ||
249 | arg_list.args().map(|e| self.collect_expr(e)).collect() | ||
250 | } else { | ||
251 | Vec::new() | ||
252 | }; | ||
253 | let method_name = e | ||
254 | .name_ref() | ||
255 | .map(|nr| nr.as_name()) | ||
256 | .unwrap_or_else(Name::missing); | ||
257 | self.alloc_expr( | ||
258 | Expr::MethodCall { | ||
259 | receiver, | ||
260 | method_name, | ||
261 | args, | ||
262 | }, | ||
263 | syntax_ptr, | ||
264 | ) | ||
265 | } | ||
266 | ast::Expr::MatchExpr(e) => { | ||
267 | let expr = self.collect_expr_opt(e.expr()); | ||
268 | let arms = if let Some(match_arm_list) = e.match_arm_list() { | ||
269 | match_arm_list | ||
270 | .arms() | ||
271 | .map(|arm| MatchArm { | ||
272 | pats: arm.pats().map(|p| self.collect_pat(p)).collect(), | ||
273 | expr: self.collect_expr_opt(arm.expr()), | ||
274 | }) | ||
275 | .collect() | ||
276 | } else { | ||
277 | Vec::new() | ||
278 | }; | ||
279 | self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) | ||
280 | } | ||
281 | ast::Expr::PathExpr(e) => { | ||
282 | let path = e | ||
283 | .path() | ||
284 | .and_then(Path::from_ast) | ||
285 | .map(Expr::Path) | ||
286 | .unwrap_or(Expr::Missing); | ||
287 | self.alloc_expr(path, syntax_ptr) | ||
288 | } | ||
289 | ast::Expr::ContinueExpr(_e) => { | ||
290 | // TODO: labels | ||
291 | self.alloc_expr(Expr::Continue, syntax_ptr) | ||
292 | } | ||
293 | ast::Expr::BreakExpr(e) => { | ||
294 | let expr = e.expr().map(|e| self.collect_expr(e)); | ||
295 | self.alloc_expr(Expr::Break { expr }, syntax_ptr) | ||
296 | } | ||
297 | ast::Expr::ParenExpr(e) => { | ||
298 | let inner = self.collect_expr_opt(e.expr()); | ||
299 | // make the paren expr point to the inner expression as well | ||
300 | self.expr_syntax_mapping.insert(syntax_ptr, inner); | ||
301 | inner | ||
302 | } | ||
303 | ast::Expr::ReturnExpr(e) => { | ||
304 | let expr = e.expr().map(|e| self.collect_expr(e)); | ||
305 | self.alloc_expr(Expr::Return { expr }, syntax_ptr) | ||
306 | } | ||
307 | ast::Expr::StructLit(e) => { | ||
308 | let path = e.path().and_then(Path::from_ast); | ||
309 | let fields = if let Some(nfl) = e.named_field_list() { | ||
310 | nfl.fields() | ||
311 | .map(|field| StructLitField { | ||
312 | name: field | ||
313 | .name_ref() | ||
314 | .map(|nr| nr.as_name()) | ||
315 | .unwrap_or_else(Name::missing), | ||
316 | expr: if let Some(e) = field.expr() { | ||
317 | self.collect_expr(e) | ||
318 | } else if let Some(nr) = field.name_ref() { | ||
319 | // field shorthand | ||
320 | let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr))); | ||
321 | self.expr_syntax_mapping | ||
322 | .insert(LocalSyntaxPtr::new(nr.syntax()), id); | ||
323 | self.expr_syntax_mapping_back | ||
324 | .insert(id, LocalSyntaxPtr::new(nr.syntax())); | ||
325 | id | ||
326 | } else { | ||
327 | self.exprs.alloc(Expr::Missing) | ||
328 | }, | ||
329 | }) | ||
330 | .collect() | ||
331 | } else { | ||
332 | Vec::new() | ||
333 | }; | ||
334 | let spread = e.spread().map(|s| self.collect_expr(s)); | ||
335 | self.alloc_expr( | ||
336 | Expr::StructLit { | ||
337 | path, | ||
338 | fields, | ||
339 | spread, | ||
340 | }, | ||
341 | syntax_ptr, | ||
342 | ) | ||
343 | } | ||
344 | ast::Expr::FieldExpr(e) => { | ||
345 | let expr = self.collect_expr_opt(e.expr()); | ||
346 | let name = e | ||
347 | .name_ref() | ||
348 | .map(|nr| nr.as_name()) | ||
349 | .unwrap_or_else(Name::missing); | ||
350 | self.alloc_expr(Expr::Field { expr, name }, syntax_ptr) | ||
351 | } | ||
352 | ast::Expr::TryExpr(e) => { | ||
353 | let expr = self.collect_expr_opt(e.expr()); | ||
354 | self.alloc_expr(Expr::Try { expr }, syntax_ptr) | ||
355 | } | ||
356 | ast::Expr::CastExpr(e) => { | ||
357 | let expr = self.collect_expr_opt(e.expr()); | ||
358 | let type_ref = TypeRef::from_ast_opt(e.type_ref()); | ||
359 | self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr) | ||
360 | } | ||
361 | ast::Expr::RefExpr(e) => { | ||
362 | let expr = self.collect_expr_opt(e.expr()); | ||
363 | let mutability = Mutability::from_mutable(e.is_mut()); | ||
364 | self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr) | ||
365 | } | ||
366 | ast::Expr::PrefixExpr(e) => { | ||
367 | let expr = self.collect_expr_opt(e.expr()); | ||
368 | let op = e.op(); | ||
369 | self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr) | ||
370 | } | ||
371 | |||
372 | // We should never get to these because they're handled in MatchExpr resp. StructLit: | ||
373 | ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { | ||
374 | panic!("collect_expr called on {:?}", expr) | ||
375 | } | ||
376 | ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => { | ||
377 | panic!("collect_expr called on {:?}", expr) | ||
378 | } | ||
379 | |||
380 | // TODO implement HIR for these: | ||
381 | ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
382 | ast::Expr::LambdaExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
383 | ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
384 | ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
385 | ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
386 | ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
387 | ast::Expr::BinExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
388 | ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), | ||
389 | } | ||
390 | } | ||
391 | |||
392 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | ||
393 | if let Some(expr) = expr { | ||
394 | self.collect_expr(expr) | ||
395 | } else { | ||
396 | self.exprs.alloc(Expr::Missing) | ||
397 | } | ||
398 | } | ||
399 | |||
400 | fn collect_block(&mut self, block: ast::Block) -> ExprId { | ||
401 | let statements = block | ||
402 | .statements() | ||
403 | .map(|s| match s { | ||
404 | ast::Stmt::LetStmt(stmt) => { | ||
405 | let pat = self.collect_pat_opt(stmt.pat()); | ||
406 | let type_ref = stmt.type_ref().map(TypeRef::from_ast); | ||
407 | let initializer = stmt.initializer().map(|e| self.collect_expr(e)); | ||
408 | Statement::Let { | ||
409 | pat, | ||
410 | type_ref, | ||
411 | initializer, | ||
412 | } | ||
413 | } | ||
414 | ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())), | ||
415 | }) | ||
416 | .collect(); | ||
417 | let tail = block.expr().map(|e| self.collect_expr(e)); | ||
418 | self.alloc_expr( | ||
419 | Expr::Block { statements, tail }, | ||
420 | LocalSyntaxPtr::new(block.syntax()), | ||
421 | ) | ||
422 | } | ||
423 | |||
424 | fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId { | ||
425 | if let Some(block) = block { | ||
426 | self.collect_block(block) | ||
427 | } else { | ||
428 | self.exprs.alloc(Expr::Missing) | ||
429 | } | ||
430 | } | ||
431 | |||
432 | fn collect_pat(&mut self, pat: ast::Pat) -> PatId { | ||
433 | let syntax_ptr = LocalSyntaxPtr::new(pat.syntax()); | ||
434 | // TODO | ||
435 | self.alloc_pat(Pat, syntax_ptr) | ||
436 | } | ||
437 | |||
438 | fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId { | ||
439 | if let Some(pat) = pat { | ||
440 | self.collect_pat(pat) | ||
441 | } else { | ||
442 | // TODO | ||
443 | self.pats.alloc(Pat) | ||
444 | } | ||
445 | } | ||
446 | |||
447 | fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping { | ||
448 | let body = Body { | ||
449 | exprs: self.exprs, | ||
450 | pats: self.pats, | ||
451 | args, | ||
452 | body_expr, | ||
453 | }; | ||
454 | BodySyntaxMapping { | ||
455 | body: Arc::new(body), | ||
456 | expr_syntax_mapping: self.expr_syntax_mapping, | ||
457 | expr_syntax_mapping_back: self.expr_syntax_mapping_back, | ||
458 | pat_syntax_mapping: self.pat_syntax_mapping, | ||
459 | pat_syntax_mapping_back: self.pat_syntax_mapping_back, | ||
460 | } | ||
461 | } | ||
462 | } | ||
463 | |||
464 | pub(crate) fn body_syntax_mapping( | ||
465 | db: &impl HirDatabase, | ||
466 | def_id: DefId, | ||
467 | ) -> Cancelable<Arc<BodySyntaxMapping>> { | ||
468 | let def = def_id.resolve(db)?; | ||
469 | let mut collector = ExprCollector { | ||
470 | exprs: Arena::default(), | ||
471 | pats: Arena::default(), | ||
472 | expr_syntax_mapping: FxHashMap::default(), | ||
473 | expr_syntax_mapping_back: FxHashMap::default(), | ||
474 | pat_syntax_mapping: FxHashMap::default(), | ||
475 | pat_syntax_mapping_back: FxHashMap::default(), | ||
476 | }; | ||
477 | |||
478 | let (body, args) = match def { | ||
479 | Def::Function(f) => { | ||
480 | let node = f.syntax(db); | ||
481 | let node = node.borrowed(); | ||
482 | |||
483 | let args = if let Some(param_list) = node.param_list() { | ||
484 | let mut args = Vec::new(); | ||
485 | // TODO self param | ||
486 | for param in param_list.params() { | ||
487 | let pat = if let Some(pat) = param.pat() { | ||
488 | pat | ||
489 | } else { | ||
490 | continue; | ||
491 | }; | ||
492 | args.push(collector.collect_pat(pat)); | ||
493 | } | ||
494 | args | ||
495 | } else { | ||
496 | Vec::new() | ||
497 | }; | ||
498 | |||
499 | let body = collector.collect_block_opt(node.body()); | ||
500 | (body, args) | ||
501 | } | ||
502 | // TODO: consts, etc. | ||
503 | _ => panic!("Trying to get body for item type without body"), | ||
504 | }; | ||
505 | |||
506 | Ok(Arc::new(collector.into_body_syntax_mapping(args, body))) | ||
507 | } | ||