aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/db.rs19
-rw-r--r--crates/ra_hir/src/expr.rs745
-rw-r--r--crates/ra_hir/src/function.rs79
-rw-r--r--crates/ra_hir/src/function/scope.rs368
-rw-r--r--crates/ra_hir/src/lib.rs3
-rw-r--r--crates/ra_hir/src/mock.rs3
-rw-r--r--crates/ra_hir/src/name.rs14
-rw-r--r--crates/ra_hir/src/path.rs14
-rw-r--r--crates/ra_hir/src/query_definitions.rs11
-rw-r--r--crates/ra_hir/src/source_binder.rs12
-rw-r--r--crates/ra_hir/src/ty.rs19
-rw-r--r--crates/ra_hir/src/ty/tests.rs44
-rw-r--r--crates/ra_hir/src/type_ref.rs4
13 files changed, 1116 insertions, 219 deletions
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index 58296fc6f..96a3c60b9 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -7,7 +7,7 @@ use crate::{
7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, 7 DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId,
8 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
9 query_definitions, 9 query_definitions,
10 FnScopes, 10 FnSignature, FnScopes,
11 macros::MacroExpansion, 11 macros::MacroExpansion,
12 module::{ModuleId, ModuleTree, ModuleSource, 12 module::{ModuleId, ModuleTree, ModuleSource,
13 nameres::{ItemMap, InputModuleItems}}, 13 nameres::{ItemMap, InputModuleItems}},
@@ -31,7 +31,7 @@ pub trait HirDatabase: SyntaxDatabase
31 use fn crate::macros::expand_macro_invocation; 31 use fn crate::macros::expand_macro_invocation;
32 } 32 }
33 33
34 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { 34 fn fn_scopes(def_id: DefId) -> Cancelable<Arc<FnScopes>> {
35 type FnScopesQuery; 35 type FnScopesQuery;
36 use fn query_definitions::fn_scopes; 36 use fn query_definitions::fn_scopes;
37 } 37 }
@@ -93,6 +93,21 @@ pub trait HirDatabase: SyntaxDatabase
93 type ImplsInModuleQuery; 93 type ImplsInModuleQuery;
94 use fn crate::impl_block::impls_in_module; 94 use fn crate::impl_block::impls_in_module;
95 } 95 }
96
97 fn body_hir(def_id: DefId) -> Cancelable<Arc<crate::expr::Body>> {
98 type BodyHirQuery;
99 use fn crate::expr::body_hir;
100 }
101
102 fn body_syntax_mapping(def_id: DefId) -> Cancelable<Arc<crate::expr::BodySyntaxMapping>> {
103 type BodySyntaxMappingQuery;
104 use fn crate::expr::body_syntax_mapping;
105 }
106
107 fn fn_signature(def_id: DefId) -> Arc<FnSignature> {
108 type FnSignatureQuery;
109 use fn crate::function::fn_signature;
110 }
96} 111}
97 112
98} 113}
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
new file mode 100644
index 000000000..6866fc2ac
--- /dev/null
+++ b/crates/ra_hir/src/expr.rs
@@ -0,0 +1,745 @@
1use std::ops::Index;
2use std::sync::Arc;
3
4use rustc_hash::FxHashMap;
5
6use ra_arena::{Arena, RawId, impl_arena_id};
7use ra_db::{LocalSyntaxPtr, Cancelable};
8use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner};
9
10use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub struct ExprId(RawId);
14impl_arena_id!(ExprId);
15
16/// The body of an item (function, const etc.).
17#[derive(Debug, Eq, PartialEq)]
18pub struct Body {
19 exprs: Arena<ExprId, Expr>,
20 pats: Arena<PatId, Pat>,
21 /// The patterns for the function's arguments. While the argument types are
22 /// part of the function signature, the patterns are not (they don't change
23 /// the external type of the function).
24 ///
25 /// If this `Body` is for the body of a constant, this will just be
26 /// empty.
27 args: Vec<PatId>,
28 /// The `ExprId` of the actual body expression.
29 body_expr: ExprId,
30}
31
32/// An item body together with the mapping from syntax nodes to HIR expression
33/// IDs. This is needed to go from e.g. a position in a file to the HIR
34/// expression containing it; but for type inference etc., we want to operate on
35/// a structure that is agnostic to the actual positions of expressions in the
36/// file, so that we don't recompute the type inference whenever some whitespace
37/// is typed.
38#[derive(Debug, Eq, PartialEq)]
39pub struct BodySyntaxMapping {
40 body: Arc<Body>,
41 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
42 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
43 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
44 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
45}
46
47impl Body {
48 pub fn args(&self) -> &[PatId] {
49 &self.args
50 }
51
52 pub fn body_expr(&self) -> ExprId {
53 self.body_expr
54 }
55}
56
57impl Index<ExprId> for Body {
58 type Output = Expr;
59
60 fn index(&self, expr: ExprId) -> &Expr {
61 &self.exprs[expr]
62 }
63}
64
65impl Index<PatId> for Body {
66 type Output = Pat;
67
68 fn index(&self, pat: PatId) -> &Pat {
69 &self.pats[pat]
70 }
71}
72
73impl BodySyntaxMapping {
74 pub fn expr_syntax(&self, expr: ExprId) -> Option<LocalSyntaxPtr> {
75 self.expr_syntax_mapping_back.get(&expr).cloned()
76 }
77 pub fn syntax_expr(&self, ptr: LocalSyntaxPtr) -> Option<ExprId> {
78 self.expr_syntax_mapping.get(&ptr).cloned()
79 }
80 pub fn pat_syntax(&self, pat: PatId) -> Option<LocalSyntaxPtr> {
81 self.pat_syntax_mapping_back.get(&pat).cloned()
82 }
83 pub fn syntax_pat(&self, ptr: LocalSyntaxPtr) -> Option<PatId> {
84 self.pat_syntax_mapping.get(&ptr).cloned()
85 }
86
87 pub fn body(&self) -> &Arc<Body> {
88 &self.body
89 }
90}
91
92#[derive(Debug, Clone, Eq, PartialEq)]
93pub enum Expr {
94 /// This is produced if syntax tree does not have a required expression piece.
95 Missing,
96 Path(Path),
97 If {
98 condition: ExprId,
99 then_branch: ExprId,
100 else_branch: Option<ExprId>,
101 },
102 Block {
103 statements: Vec<Statement>,
104 tail: Option<ExprId>,
105 },
106 Loop {
107 body: ExprId,
108 },
109 While {
110 condition: ExprId,
111 body: ExprId,
112 },
113 For {
114 iterable: ExprId,
115 pat: PatId,
116 body: ExprId,
117 },
118 Call {
119 callee: ExprId,
120 args: Vec<ExprId>,
121 },
122 MethodCall {
123 receiver: ExprId,
124 method_name: Name,
125 args: Vec<ExprId>,
126 },
127 Match {
128 expr: ExprId,
129 arms: Vec<MatchArm>,
130 },
131 Continue,
132 Break {
133 expr: Option<ExprId>,
134 },
135 Return {
136 expr: Option<ExprId>,
137 },
138 StructLit {
139 path: Option<Path>,
140 fields: Vec<StructLitField>,
141 spread: Option<ExprId>,
142 },
143 Field {
144 expr: ExprId,
145 name: Name,
146 },
147 Try {
148 expr: ExprId,
149 },
150 Cast {
151 expr: ExprId,
152 type_ref: TypeRef,
153 },
154 Ref {
155 expr: ExprId,
156 mutability: Mutability,
157 },
158 UnaryOp {
159 expr: ExprId,
160 op: Option<UnaryOp>,
161 },
162 Lambda {
163 args: Vec<PatId>,
164 arg_types: Vec<Option<TypeRef>>,
165 body: ExprId,
166 },
167}
168
169pub type UnaryOp = ast::PrefixOp;
170
171#[derive(Debug, Clone, Eq, PartialEq)]
172pub struct MatchArm {
173 pub pats: Vec<PatId>,
174 // guard: Option<ExprId>, // TODO
175 pub expr: ExprId,
176}
177
178#[derive(Debug, Clone, Eq, PartialEq)]
179pub struct StructLitField {
180 pub name: Name,
181 pub expr: ExprId,
182}
183
184#[derive(Debug, Clone, Eq, PartialEq)]
185pub enum Statement {
186 Let {
187 pat: PatId,
188 type_ref: Option<TypeRef>,
189 initializer: Option<ExprId>,
190 },
191 Expr(ExprId),
192}
193
194impl Expr {
195 pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
196 match self {
197 Expr::Missing => {}
198 Expr::Path(_) => {}
199 Expr::If {
200 condition,
201 then_branch,
202 else_branch,
203 } => {
204 f(*condition);
205 f(*then_branch);
206 if let Some(else_branch) = else_branch {
207 f(*else_branch);
208 }
209 }
210 Expr::Block { statements, tail } => {
211 for stmt in statements {
212 match stmt {
213 Statement::Let { initializer, .. } => {
214 if let Some(expr) = initializer {
215 f(*expr);
216 }
217 }
218 Statement::Expr(e) => f(*e),
219 }
220 }
221 if let Some(expr) = tail {
222 f(*expr);
223 }
224 }
225 Expr::Loop { body } => f(*body),
226 Expr::While { condition, body } => {
227 f(*condition);
228 f(*body);
229 }
230 Expr::For { iterable, body, .. } => {
231 f(*iterable);
232 f(*body);
233 }
234 Expr::Call { callee, args } => {
235 f(*callee);
236 for arg in args {
237 f(*arg);
238 }
239 }
240 Expr::MethodCall { receiver, args, .. } => {
241 f(*receiver);
242 for arg in args {
243 f(*arg);
244 }
245 }
246 Expr::Match { expr, arms } => {
247 f(*expr);
248 for arm in arms {
249 f(arm.expr);
250 }
251 }
252 Expr::Continue => {}
253 Expr::Break { expr } | Expr::Return { expr } => {
254 if let Some(expr) = expr {
255 f(*expr);
256 }
257 }
258 Expr::StructLit { fields, spread, .. } => {
259 for field in fields {
260 f(field.expr);
261 }
262 if let Some(expr) = spread {
263 f(*expr);
264 }
265 }
266 Expr::Lambda { body, .. } => {
267 f(*body);
268 }
269 Expr::Field { expr, .. }
270 | Expr::Try { expr }
271 | Expr::Cast { expr, .. }
272 | Expr::Ref { expr, .. }
273 | Expr::UnaryOp { expr, .. } => {
274 f(*expr);
275 }
276 }
277 }
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
281pub struct PatId(RawId);
282impl_arena_id!(PatId);
283
284#[derive(Debug, Clone, Eq, PartialEq)]
285pub enum Pat {
286 Missing,
287 Bind {
288 name: Name,
289 },
290 TupleStruct {
291 path: Option<Path>,
292 args: Vec<PatId>,
293 },
294}
295
296impl Pat {
297 pub fn walk_child_pats(&self, f: impl FnMut(PatId)) {
298 match self {
299 Pat::Missing | Pat::Bind { .. } => {}
300 Pat::TupleStruct { args, .. } => {
301 args.iter().map(|pat| *pat).for_each(f);
302 }
303 }
304 }
305}
306
307// Queries
308
309pub(crate) fn body_hir(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<Body>> {
310 Ok(Arc::clone(&body_syntax_mapping(db, def_id)?.body))
311}
312
313struct ExprCollector {
314 exprs: Arena<ExprId, Expr>,
315 pats: Arena<PatId, Pat>,
316 expr_syntax_mapping: FxHashMap<LocalSyntaxPtr, ExprId>,
317 expr_syntax_mapping_back: FxHashMap<ExprId, LocalSyntaxPtr>,
318 pat_syntax_mapping: FxHashMap<LocalSyntaxPtr, PatId>,
319 pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
320}
321
322impl ExprCollector {
323 fn new() -> Self {
324 ExprCollector {
325 exprs: Arena::default(),
326 pats: Arena::default(),
327 expr_syntax_mapping: FxHashMap::default(),
328 expr_syntax_mapping_back: FxHashMap::default(),
329 pat_syntax_mapping: FxHashMap::default(),
330 pat_syntax_mapping_back: FxHashMap::default(),
331 }
332 }
333
334 fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId {
335 let id = self.exprs.alloc(expr);
336 self.expr_syntax_mapping.insert(syntax_ptr, id);
337 self.expr_syntax_mapping_back.insert(id, syntax_ptr);
338 id
339 }
340
341 fn alloc_pat(&mut self, pat: Pat, syntax_ptr: LocalSyntaxPtr) -> PatId {
342 let id = self.pats.alloc(pat);
343 self.pat_syntax_mapping.insert(syntax_ptr, id);
344 self.pat_syntax_mapping_back.insert(id, syntax_ptr);
345 id
346 }
347
348 fn empty_block(&mut self) -> ExprId {
349 let block = Expr::Block {
350 statements: Vec::new(),
351 tail: None,
352 };
353 self.exprs.alloc(block)
354 }
355
356 fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
357 let syntax_ptr = LocalSyntaxPtr::new(expr.syntax());
358 match expr {
359 ast::Expr::IfExpr(e) => {
360 if let Some(pat) = e.condition().and_then(|c| c.pat()) {
361 // if let -- desugar to match
362 let pat = self.collect_pat(pat);
363 let match_expr =
364 self.collect_expr_opt(e.condition().expect("checked above").expr());
365 let then_branch = self.collect_block_opt(e.then_branch());
366 let else_branch = e
367 .else_branch()
368 .map(|e| self.collect_block(e))
369 .unwrap_or_else(|| self.empty_block());
370 let placeholder_pat = self.pats.alloc(Pat::Missing);
371 let arms = vec![
372 MatchArm {
373 pats: vec![pat],
374 expr: then_branch,
375 },
376 MatchArm {
377 pats: vec![placeholder_pat],
378 expr: else_branch,
379 },
380 ];
381 self.alloc_expr(
382 Expr::Match {
383 expr: match_expr,
384 arms,
385 },
386 syntax_ptr,
387 )
388 } else {
389 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr()));
390 let then_branch = self.collect_block_opt(e.then_branch());
391 let else_branch = e.else_branch().map(|e| self.collect_block(e));
392 self.alloc_expr(
393 Expr::If {
394 condition,
395 then_branch,
396 else_branch,
397 },
398 syntax_ptr,
399 )
400 }
401 }
402 ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()),
403 ast::Expr::LoopExpr(e) => {
404 let body = self.collect_block_opt(e.loop_body());
405 self.alloc_expr(Expr::Loop { body }, syntax_ptr)
406 }
407 ast::Expr::WhileExpr(e) => {
408 let condition = if let Some(condition) = e.condition() {
409 if condition.pat().is_none() {
410 self.collect_expr_opt(condition.expr())
411 } else {
412 // TODO handle while let
413 return self.alloc_expr(Expr::Missing, syntax_ptr);
414 }
415 } else {
416 self.exprs.alloc(Expr::Missing)
417 };
418 let body = self.collect_block_opt(e.loop_body());
419 self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
420 }
421 ast::Expr::ForExpr(e) => {
422 let iterable = self.collect_expr_opt(e.iterable());
423 let pat = self.collect_pat_opt(e.pat());
424 let body = self.collect_block_opt(e.loop_body());
425 self.alloc_expr(
426 Expr::For {
427 iterable,
428 pat,
429 body,
430 },
431 syntax_ptr,
432 )
433 }
434 ast::Expr::CallExpr(e) => {
435 let callee = self.collect_expr_opt(e.expr());
436 let args = if let Some(arg_list) = e.arg_list() {
437 arg_list.args().map(|e| self.collect_expr(e)).collect()
438 } else {
439 Vec::new()
440 };
441 self.alloc_expr(Expr::Call { callee, args }, syntax_ptr)
442 }
443 ast::Expr::MethodCallExpr(e) => {
444 let receiver = self.collect_expr_opt(e.expr());
445 let args = if let Some(arg_list) = e.arg_list() {
446 arg_list.args().map(|e| self.collect_expr(e)).collect()
447 } else {
448 Vec::new()
449 };
450 let method_name = e
451 .name_ref()
452 .map(|nr| nr.as_name())
453 .unwrap_or_else(Name::missing);
454 self.alloc_expr(
455 Expr::MethodCall {
456 receiver,
457 method_name,
458 args,
459 },
460 syntax_ptr,
461 )
462 }
463 ast::Expr::MatchExpr(e) => {
464 let expr = self.collect_expr_opt(e.expr());
465 let arms = if let Some(match_arm_list) = e.match_arm_list() {
466 match_arm_list
467 .arms()
468 .map(|arm| MatchArm {
469 pats: arm.pats().map(|p| self.collect_pat(p)).collect(),
470 expr: self.collect_expr_opt(arm.expr()),
471 })
472 .collect()
473 } else {
474 Vec::new()
475 };
476 self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
477 }
478 ast::Expr::PathExpr(e) => {
479 let path = e
480 .path()
481 .and_then(Path::from_ast)
482 .map(Expr::Path)
483 .unwrap_or(Expr::Missing);
484 self.alloc_expr(path, syntax_ptr)
485 }
486 ast::Expr::ContinueExpr(_e) => {
487 // TODO: labels
488 self.alloc_expr(Expr::Continue, syntax_ptr)
489 }
490 ast::Expr::BreakExpr(e) => {
491 let expr = e.expr().map(|e| self.collect_expr(e));
492 self.alloc_expr(Expr::Break { expr }, syntax_ptr)
493 }
494 ast::Expr::ParenExpr(e) => {
495 let inner = self.collect_expr_opt(e.expr());
496 // make the paren expr point to the inner expression as well
497 self.expr_syntax_mapping.insert(syntax_ptr, inner);
498 inner
499 }
500 ast::Expr::ReturnExpr(e) => {
501 let expr = e.expr().map(|e| self.collect_expr(e));
502 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
503 }
504 ast::Expr::StructLit(e) => {
505 let path = e.path().and_then(Path::from_ast);
506 let fields = if let Some(nfl) = e.named_field_list() {
507 nfl.fields()
508 .map(|field| StructLitField {
509 name: field
510 .name_ref()
511 .map(|nr| nr.as_name())
512 .unwrap_or_else(Name::missing),
513 expr: if let Some(e) = field.expr() {
514 self.collect_expr(e)
515 } else if let Some(nr) = field.name_ref() {
516 // field shorthand
517 let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr)));
518 self.expr_syntax_mapping
519 .insert(LocalSyntaxPtr::new(nr.syntax()), id);
520 self.expr_syntax_mapping_back
521 .insert(id, LocalSyntaxPtr::new(nr.syntax()));
522 id
523 } else {
524 self.exprs.alloc(Expr::Missing)
525 },
526 })
527 .collect()
528 } else {
529 Vec::new()
530 };
531 let spread = e.spread().map(|s| self.collect_expr(s));
532 self.alloc_expr(
533 Expr::StructLit {
534 path,
535 fields,
536 spread,
537 },
538 syntax_ptr,
539 )
540 }
541 ast::Expr::FieldExpr(e) => {
542 let expr = self.collect_expr_opt(e.expr());
543 let name = e
544 .name_ref()
545 .map(|nr| nr.as_name())
546 .unwrap_or_else(Name::missing);
547 self.alloc_expr(Expr::Field { expr, name }, syntax_ptr)
548 }
549 ast::Expr::TryExpr(e) => {
550 let expr = self.collect_expr_opt(e.expr());
551 self.alloc_expr(Expr::Try { expr }, syntax_ptr)
552 }
553 ast::Expr::CastExpr(e) => {
554 let expr = self.collect_expr_opt(e.expr());
555 let type_ref = TypeRef::from_ast_opt(e.type_ref());
556 self.alloc_expr(Expr::Cast { expr, type_ref }, syntax_ptr)
557 }
558 ast::Expr::RefExpr(e) => {
559 let expr = self.collect_expr_opt(e.expr());
560 let mutability = Mutability::from_mutable(e.is_mut());
561 self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
562 }
563 ast::Expr::PrefixExpr(e) => {
564 let expr = self.collect_expr_opt(e.expr());
565 let op = e.op();
566 self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
567 }
568 ast::Expr::LambdaExpr(e) => {
569 let mut args = Vec::new();
570 let mut arg_types = Vec::new();
571 if let Some(pl) = e.param_list() {
572 for param in pl.params() {
573 let pat = self.collect_pat_opt(param.pat());
574 let type_ref = param.type_ref().map(TypeRef::from_ast);
575 args.push(pat);
576 arg_types.push(type_ref);
577 }
578 }
579 let body = self.collect_expr_opt(e.body());
580 self.alloc_expr(
581 Expr::Lambda {
582 args,
583 arg_types,
584 body,
585 },
586 syntax_ptr,
587 )
588 }
589
590 // TODO implement HIR for these:
591 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
592 ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
593 ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
594 ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
595 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
596 ast::Expr::BinExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
597 ast::Expr::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
598 }
599 }
600
601 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
602 if let Some(expr) = expr {
603 self.collect_expr(expr)
604 } else {
605 self.exprs.alloc(Expr::Missing)
606 }
607 }
608
609 fn collect_block(&mut self, block: ast::Block) -> ExprId {
610 let statements = block
611 .statements()
612 .map(|s| match s {
613 ast::Stmt::LetStmt(stmt) => {
614 let pat = self.collect_pat_opt(stmt.pat());
615 let type_ref = stmt.type_ref().map(TypeRef::from_ast);
616 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
617 Statement::Let {
618 pat,
619 type_ref,
620 initializer,
621 }
622 }
623 ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
624 })
625 .collect();
626 let tail = block.expr().map(|e| self.collect_expr(e));
627 self.alloc_expr(
628 Expr::Block { statements, tail },
629 LocalSyntaxPtr::new(block.syntax()),
630 )
631 }
632
633 fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId {
634 if let Some(block) = block {
635 self.collect_block(block)
636 } else {
637 self.exprs.alloc(Expr::Missing)
638 }
639 }
640
641 fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
642 let syntax_ptr = LocalSyntaxPtr::new(pat.syntax());
643 match pat {
644 ast::Pat::BindPat(bp) => {
645 let name = bp
646 .name()
647 .map(|nr| nr.as_name())
648 .unwrap_or_else(Name::missing);
649 self.alloc_pat(Pat::Bind { name }, syntax_ptr)
650 }
651 ast::Pat::TupleStructPat(p) => {
652 let path = p.path().and_then(Path::from_ast);
653 let args = p.args().map(|p| self.collect_pat(p)).collect();
654 self.alloc_pat(Pat::TupleStruct { path, args }, syntax_ptr)
655 }
656 _ => {
657 // TODO
658 self.alloc_pat(Pat::Missing, syntax_ptr)
659 }
660 }
661 }
662
663 fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
664 if let Some(pat) = pat {
665 self.collect_pat(pat)
666 } else {
667 self.pats.alloc(Pat::Missing)
668 }
669 }
670
671 fn into_body_syntax_mapping(self, args: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping {
672 let body = Body {
673 exprs: self.exprs,
674 pats: self.pats,
675 args,
676 body_expr,
677 };
678 BodySyntaxMapping {
679 body: Arc::new(body),
680 expr_syntax_mapping: self.expr_syntax_mapping,
681 expr_syntax_mapping_back: self.expr_syntax_mapping_back,
682 pat_syntax_mapping: self.pat_syntax_mapping,
683 pat_syntax_mapping_back: self.pat_syntax_mapping_back,
684 }
685 }
686}
687
688pub(crate) fn collect_fn_body_syntax(node: ast::FnDef) -> BodySyntaxMapping {
689 let mut collector = ExprCollector::new();
690
691 let args = if let Some(param_list) = node.param_list() {
692 let mut args = Vec::new();
693
694 if let Some(self_param) = param_list.self_param() {
695 let self_param = LocalSyntaxPtr::new(
696 self_param
697 .self_kw()
698 .expect("self param without self keyword")
699 .syntax(),
700 );
701 let arg = collector.alloc_pat(
702 Pat::Bind {
703 name: Name::self_param(),
704 },
705 self_param,
706 );
707 args.push(arg);
708 }
709
710 for param in param_list.params() {
711 let pat = if let Some(pat) = param.pat() {
712 pat
713 } else {
714 continue;
715 };
716 args.push(collector.collect_pat(pat));
717 }
718 args
719 } else {
720 Vec::new()
721 };
722
723 let body = collector.collect_block_opt(node.body());
724 collector.into_body_syntax_mapping(args, body)
725}
726
727pub(crate) fn body_syntax_mapping(
728 db: &impl HirDatabase,
729 def_id: DefId,
730) -> Cancelable<Arc<BodySyntaxMapping>> {
731 let def = def_id.resolve(db)?;
732
733 let body_syntax_mapping = match def {
734 Def::Function(f) => {
735 let node = f.syntax(db);
736 let node = node.borrowed();
737
738 collect_fn_body_syntax(node)
739 }
740 // TODO: consts, etc.
741 _ => panic!("Trying to get body for item type without body"),
742 };
743
744 Ok(Arc::new(body_syntax_mapping))
745}
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs
index 75ef308ae..4627be071 100644
--- a/crates/ra_hir/src/function.rs
+++ b/crates/ra_hir/src/function.rs
@@ -11,9 +11,9 @@ use ra_syntax::{
11 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 11 ast::{self, AstNode, DocCommentsOwner, NameOwner},
12}; 12};
13 13
14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock}; 14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}, type_ref::{TypeRef, Mutability}, Name};
15 15
16pub use self::scope::FnScopes; 16pub use self::scope::{FnScopes, ScopesWithSyntaxMapping};
17 17
18#[derive(Debug, Clone, PartialEq, Eq)] 18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Function { 19pub struct Function {
@@ -36,8 +36,25 @@ impl Function {
36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned() 36 ast::FnDef::cast(syntax.borrowed()).unwrap().owned()
37 } 37 }
38 38
39 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> { 39 pub fn body(&self, db: &impl HirDatabase) -> Cancelable<Arc<Body>> {
40 db.fn_scopes(self.def_id) 40 db.body_hir(self.def_id)
41 }
42
43 pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Cancelable<Arc<BodySyntaxMapping>> {
44 db.body_syntax_mapping(self.def_id)
45 }
46
47 pub fn scopes(&self, db: &impl HirDatabase) -> Cancelable<ScopesWithSyntaxMapping> {
48 let scopes = db.fn_scopes(self.def_id)?;
49 let syntax_mapping = db.body_syntax_mapping(self.def_id)?;
50 Ok(ScopesWithSyntaxMapping {
51 scopes,
52 syntax_mapping,
53 })
54 }
55
56 pub fn signature(&self, db: &impl HirDatabase) -> Arc<FnSignature> {
57 db.fn_signature(self.def_id)
41 } 58 }
42 59
43 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { 60 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {
@@ -63,6 +80,60 @@ impl Function {
63 } 80 }
64} 81}
65 82
83/// The declared signature of a function.
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct FnSignature {
86 args: Vec<TypeRef>,
87 ret_type: TypeRef,
88}
89
90impl FnSignature {
91 pub fn args(&self) -> &[TypeRef] {
92 &self.args
93 }
94
95 pub fn ret_type(&self) -> &TypeRef {
96 &self.ret_type
97 }
98}
99
100pub(crate) fn fn_signature(db: &impl HirDatabase, def_id: DefId) -> Arc<FnSignature> {
101 let func = Function::new(def_id);
102 let syntax = func.syntax(db);
103 let node = syntax.borrowed();
104 let mut args = Vec::new();
105 if let Some(param_list) = node.param_list() {
106 if let Some(self_param) = param_list.self_param() {
107 let self_type = if let Some(type_ref) = self_param.type_ref() {
108 TypeRef::from_ast(type_ref)
109 } else {
110 let self_type = TypeRef::Path(Name::self_type().into());
111 match self_param.flavor() {
112 ast::SelfParamFlavor::Owned => self_type,
113 ast::SelfParamFlavor::Ref => {
114 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
115 }
116 ast::SelfParamFlavor::MutRef => {
117 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
118 }
119 }
120 };
121 args.push(self_type);
122 }
123 for param in param_list.params() {
124 let type_ref = TypeRef::from_ast_opt(param.type_ref());
125 args.push(type_ref);
126 }
127 }
128 let ret_type = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
129 TypeRef::from_ast(type_ref)
130 } else {
131 TypeRef::unit()
132 };
133 let sig = FnSignature { args, ret_type };
134 Arc::new(sig)
135}
136
66#[derive(Debug, Clone)] 137#[derive(Debug, Clone)]
67pub struct FnSignatureInfo { 138pub struct FnSignatureInfo {
68 pub name: String, 139 pub name: String,
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 42bfe4f32..0a12f0b35 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,14 +1,16 @@
1use std::sync::Arc;
2
1use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::{FxHashMap, FxHashSet};
2 4
3use ra_syntax::{ 5use ra_syntax::{
4 AstNode, SyntaxNodeRef, TextUnit, TextRange, 6 AstNode, SyntaxNodeRef, TextUnit, TextRange,
5 algo::generate, 7 algo::generate,
6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, 8 ast,
7}; 9};
8use ra_arena::{Arena, RawId, impl_arena_id}; 10use ra_arena::{Arena, RawId, impl_arena_id};
9use ra_db::LocalSyntaxPtr; 11use ra_db::LocalSyntaxPtr;
10 12
11use crate::{Name, AsName}; 13use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}};
12 14
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct ScopeId(RawId); 16pub struct ScopeId(RawId);
@@ -16,15 +18,15 @@ impl_arena_id!(ScopeId);
16 18
17#[derive(Debug, PartialEq, Eq)] 19#[derive(Debug, PartialEq, Eq)]
18pub struct FnScopes { 20pub struct FnScopes {
19 pub self_param: Option<LocalSyntaxPtr>, 21 body: Arc<Body>,
20 scopes: Arena<ScopeId, ScopeData>, 22 scopes: Arena<ScopeId, ScopeData>,
21 scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, 23 scope_for: FxHashMap<ExprId, ScopeId>,
22} 24}
23 25
24#[derive(Debug, PartialEq, Eq)] 26#[derive(Debug, PartialEq, Eq)]
25pub struct ScopeEntry { 27pub struct ScopeEntry {
26 name: Name, 28 name: Name,
27 ptr: LocalSyntaxPtr, 29 pat: PatId,
28} 30}
29 31
30#[derive(Debug, PartialEq, Eq)] 32#[derive(Debug, PartialEq, Eq)]
@@ -34,28 +36,100 @@ pub struct ScopeData {
34} 36}
35 37
36impl FnScopes { 38impl FnScopes {
37 pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { 39 pub(crate) fn new(body: Arc<Body>) -> FnScopes {
38 let mut scopes = FnScopes { 40 let mut scopes = FnScopes {
39 self_param: fn_def 41 body: body.clone(),
40 .param_list()
41 .and_then(|it| it.self_param())
42 .map(|it| LocalSyntaxPtr::new(it.syntax())),
43 scopes: Arena::default(), 42 scopes: Arena::default(),
44 scope_for: FxHashMap::default(), 43 scope_for: FxHashMap::default(),
45 }; 44 };
46 let root = scopes.root_scope(); 45 let root = scopes.root_scope();
47 scopes.add_params_bindings(root, fn_def.param_list()); 46 scopes.add_params_bindings(root, body.args());
48 if let Some(body) = fn_def.body() { 47 compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
49 compute_block_scopes(body, &mut scopes, root)
50 }
51 scopes 48 scopes
52 } 49 }
53 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { 50 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
54 &self.scopes[scope].entries 51 &self.scopes[scope].entries
55 } 52 }
53 pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
54 generate(self.scope_for(expr), move |&scope| {
55 self.scopes[scope].parent
56 })
57 }
58
59 pub fn resolve_local_name<'a>(
60 &'a self,
61 context_expr: ExprId,
62 name: Name,
63 ) -> Option<&'a ScopeEntry> {
64 let mut shadowed = FxHashSet::default();
65 let ret = self
66 .scope_chain_for(context_expr)
67 .flat_map(|scope| self.entries(scope).iter())
68 .filter(|entry| shadowed.insert(entry.name()))
69 .find(|entry| entry.name() == &name);
70 ret
71 }
72
73 fn root_scope(&mut self) -> ScopeId {
74 self.scopes.alloc(ScopeData {
75 parent: None,
76 entries: vec![],
77 })
78 }
79 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
80 self.scopes.alloc(ScopeData {
81 parent: Some(parent),
82 entries: vec![],
83 })
84 }
85 fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
86 match &body[pat] {
87 Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry {
88 name: name.clone(),
89 pat,
90 }),
91 p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
92 }
93 }
94 fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
95 let body = Arc::clone(&self.body);
96 params
97 .into_iter()
98 .for_each(|pat| self.add_bindings(&body, scope, *pat));
99 }
100 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
101 self.scope_for.insert(node, scope);
102 }
103 fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
104 self.scope_for.get(&expr).map(|&scope| scope)
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct ScopesWithSyntaxMapping {
110 pub syntax_mapping: Arc<BodySyntaxMapping>,
111 pub scopes: Arc<FnScopes>,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct ScopeEntryWithSyntax {
116 name: Name,
117 ptr: LocalSyntaxPtr,
118}
119
120impl ScopeEntryWithSyntax {
121 pub fn name(&self) -> &Name {
122 &self.name
123 }
124 pub fn ptr(&self) -> LocalSyntaxPtr {
125 self.ptr
126 }
127}
128
129impl ScopesWithSyntaxMapping {
56 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { 130 pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
57 generate(self.scope_for(node), move |&scope| { 131 generate(self.scope_for(node), move |&scope| {
58 self.scopes[scope].parent 132 self.scopes.scopes[scope].parent
59 }) 133 })
60 } 134 }
61 pub fn scope_chain_for_offset<'a>( 135 pub fn scope_chain_for_offset<'a>(
@@ -63,26 +137,30 @@ impl FnScopes {
63 offset: TextUnit, 137 offset: TextUnit,
64 ) -> impl Iterator<Item = ScopeId> + 'a { 138 ) -> impl Iterator<Item = ScopeId> + 'a {
65 let scope = self 139 let scope = self
140 .scopes
66 .scope_for 141 .scope_for
67 .iter() 142 .iter()
68 // find containin scope 143 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
144 // find containing scope
69 .min_by_key(|(ptr, _scope)| { 145 .min_by_key(|(ptr, _scope)| {
70 ( 146 (
71 !(ptr.range().start() <= offset && offset <= ptr.range().end()), 147 !(ptr.range().start() <= offset && offset <= ptr.range().end()),
72 ptr.range().len(), 148 ptr.range().len(),
73 ) 149 )
74 }) 150 })
75 .map(|(ptr, scope)| self.adjust(*ptr, *scope, offset)); 151 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
76 152
77 generate(scope, move |&scope| self.scopes[scope].parent) 153 generate(scope, move |&scope| self.scopes.scopes[scope].parent)
78 } 154 }
79 // XXX: during completion, cursor might be outside of any particular 155 // XXX: during completion, cursor might be outside of any particular
80 // expression. Try to figure out the correct scope... 156 // expression. Try to figure out the correct scope...
81 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { 157 fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
82 let r = ptr.range(); 158 let r = ptr.range();
83 let child_scopes = self 159 let child_scopes = self
160 .scopes
84 .scope_for 161 .scope_for
85 .iter() 162 .iter()
163 .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
86 .map(|(ptr, scope)| (ptr.range(), scope)) 164 .map(|(ptr, scope)| (ptr.range(), scope))
87 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); 165 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
88 166
@@ -100,22 +178,27 @@ impl FnScopes {
100 .unwrap_or(original_scope) 178 .unwrap_or(original_scope)
101 } 179 }
102 180
103 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { 181 pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> {
104 let mut shadowed = FxHashSet::default(); 182 let mut shadowed = FxHashSet::default();
105 let name = name_ref.as_name(); 183 let name = name_ref.as_name();
106 let ret = self 184 let ret = self
107 .scope_chain(name_ref.syntax()) 185 .scope_chain(name_ref.syntax())
108 .flat_map(|scope| self.entries(scope).iter()) 186 .flat_map(|scope| self.scopes.entries(scope).iter())
109 .filter(|entry| shadowed.insert(entry.name())) 187 .filter(|entry| shadowed.insert(entry.name()))
110 .filter(|entry| entry.name() == &name) 188 .filter(|entry| entry.name() == &name)
111 .nth(0); 189 .nth(0);
112 ret 190 ret.and_then(|entry| {
191 Some(ScopeEntryWithSyntax {
192 name: entry.name().clone(),
193 ptr: self.syntax_mapping.pat_syntax(entry.pat())?,
194 })
195 })
113 } 196 }
114 197
115 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { 198 pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
116 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); 199 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
117 let name_ptr = LocalSyntaxPtr::new(pat.syntax()); 200 let name_ptr = LocalSyntaxPtr::new(pat.syntax());
118 let refs: Vec<_> = fn_def 201 fn_def
119 .syntax() 202 .syntax()
120 .descendants() 203 .descendants()
121 .filter_map(ast::NameRef::cast) 204 .filter_map(ast::NameRef::cast)
@@ -127,203 +210,94 @@ impl FnScopes {
127 name: name_ref.syntax().text().to_string(), 210 name: name_ref.syntax().text().to_string(),
128 range: name_ref.syntax().range(), 211 range: name_ref.syntax().range(),
129 }) 212 })
130 .collect(); 213 .collect()
131
132 refs
133 } 214 }
134 215
135 fn root_scope(&mut self) -> ScopeId {
136 self.scopes.alloc(ScopeData {
137 parent: None,
138 entries: vec![],
139 })
140 }
141 fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
142 self.scopes.alloc(ScopeData {
143 parent: Some(parent),
144 entries: vec![],
145 })
146 }
147 fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
148 let entries = pat
149 .syntax()
150 .descendants()
151 .filter_map(ast::BindPat::cast)
152 .filter_map(ScopeEntry::new);
153 self.scopes[scope].entries.extend(entries);
154 }
155 fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
156 params
157 .into_iter()
158 .flat_map(|it| it.params())
159 .filter_map(|it| it.pat())
160 .for_each(|it| self.add_bindings(scope, it));
161 }
162 fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
163 self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
164 }
165 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { 216 fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
166 node.ancestors() 217 node.ancestors()
167 .map(LocalSyntaxPtr::new) 218 .map(LocalSyntaxPtr::new)
168 .filter_map(|it| self.scope_for.get(&it).map(|&scope| scope)) 219 .filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
169 .next() 220 .find_map(|it| self.scopes.scope_for(it))
170 } 221 }
171} 222}
172 223
173impl ScopeEntry { 224impl ScopeEntry {
174 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
175 let name = pat.name()?.as_name();
176 let res = ScopeEntry {
177 name,
178 ptr: LocalSyntaxPtr::new(pat.syntax()),
179 };
180 Some(res)
181 }
182 pub fn name(&self) -> &Name { 225 pub fn name(&self) -> &Name {
183 &self.name 226 &self.name
184 } 227 }
185 pub fn ptr(&self) -> LocalSyntaxPtr { 228 pub fn pat(&self) -> PatId {
186 self.ptr 229 self.pat
187 } 230 }
188} 231}
189 232
190fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { 233fn compute_block_scopes(
191 // A hack for completion :( 234 statements: &[Statement],
192 scopes.set_scope(block.syntax(), scope); 235 tail: Option<ExprId>,
193 for stmt in block.statements() { 236 body: &Body,
237 scopes: &mut FnScopes,
238 mut scope: ScopeId,
239) {
240 for stmt in statements {
194 match stmt { 241 match stmt {
195 ast::Stmt::LetStmt(stmt) => { 242 Statement::Let {
196 if let Some(expr) = stmt.initializer() { 243 pat, initializer, ..
197 scopes.set_scope(expr.syntax(), scope); 244 } => {
198 compute_expr_scopes(expr, scopes, scope); 245 if let Some(expr) = initializer {
246 scopes.set_scope(*expr, scope);
247 compute_expr_scopes(*expr, body, scopes, scope);
199 } 248 }
200 scope = scopes.new_scope(scope); 249 scope = scopes.new_scope(scope);
201 if let Some(pat) = stmt.pat() { 250 scopes.add_bindings(body, scope, *pat);
202 scopes.add_bindings(scope, pat);
203 }
204 } 251 }
205 ast::Stmt::ExprStmt(expr_stmt) => { 252 Statement::Expr(expr) => {
206 if let Some(expr) = expr_stmt.expr() { 253 scopes.set_scope(*expr, scope);
207 scopes.set_scope(expr.syntax(), scope); 254 compute_expr_scopes(*expr, body, scopes, scope);
208 compute_expr_scopes(expr, scopes, scope);
209 }
210 } 255 }
211 } 256 }
212 } 257 }
213 if let Some(expr) = block.expr() { 258 if let Some(expr) = tail {
214 scopes.set_scope(expr.syntax(), scope); 259 compute_expr_scopes(expr, body, scopes, scope);
215 compute_expr_scopes(expr, scopes, scope);
216 } 260 }
217} 261}
218 262
219fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { 263fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) {
220 match expr { 264 scopes.set_scope(expr, scope);
221 ast::Expr::IfExpr(e) => { 265 match &body[expr] {
222 let cond_scope = e 266 Expr::Block { statements, tail } => {
223 .condition() 267 compute_block_scopes(&statements, *tail, body, scopes, scope);
224 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
225 if let Some(block) = e.then_branch() {
226 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
227 }
228 if let Some(block) = e.else_branch() {
229 compute_block_scopes(block, scopes, scope);
230 }
231 }
232 ast::Expr::BlockExpr(e) => {
233 if let Some(block) = e.block() {
234 compute_block_scopes(block, scopes, scope);
235 }
236 }
237 ast::Expr::LoopExpr(e) => {
238 if let Some(block) = e.loop_body() {
239 compute_block_scopes(block, scopes, scope);
240 }
241 }
242 ast::Expr::WhileExpr(e) => {
243 let cond_scope = e
244 .condition()
245 .and_then(|cond| compute_cond_scopes(cond, scopes, scope));
246 if let Some(block) = e.loop_body() {
247 compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
248 }
249 } 268 }
250 ast::Expr::ForExpr(e) => { 269 Expr::For {
251 if let Some(expr) = e.iterable() { 270 iterable,
252 compute_expr_scopes(expr, scopes, scope); 271 pat,
253 } 272 body: body_expr,
254 let mut scope = scope; 273 } => {
255 if let Some(pat) = e.pat() { 274 compute_expr_scopes(*iterable, body, scopes, scope);
256 scope = scopes.new_scope(scope);
257 scopes.add_bindings(scope, pat);
258 }
259 if let Some(block) = e.loop_body() {
260 compute_block_scopes(block, scopes, scope);
261 }
262 }
263 ast::Expr::LambdaExpr(e) => {
264 let scope = scopes.new_scope(scope); 275 let scope = scopes.new_scope(scope);
265 scopes.add_params_bindings(scope, e.param_list()); 276 scopes.add_bindings(body, scope, *pat);
266 if let Some(body) = e.body() { 277 compute_expr_scopes(*body_expr, body, scopes, scope);
267 scopes.set_scope(body.syntax(), scope);
268 compute_expr_scopes(body, scopes, scope);
269 }
270 } 278 }
271 ast::Expr::CallExpr(e) => { 279 Expr::Lambda {
272 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 280 args,
273 } 281 body: body_expr,
274 ast::Expr::MethodCallExpr(e) => { 282 ..
275 compute_call_scopes(e.expr(), e.arg_list(), scopes, scope); 283 } => {
284 let scope = scopes.new_scope(scope);
285 scopes.add_params_bindings(scope, &args);
286 compute_expr_scopes(*body_expr, body, scopes, scope);
276 } 287 }
277 ast::Expr::MatchExpr(e) => { 288 Expr::Match { expr, arms } => {
278 if let Some(expr) = e.expr() { 289 compute_expr_scopes(*expr, body, scopes, scope);
279 compute_expr_scopes(expr, scopes, scope); 290 for arm in arms {
280 }
281 for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
282 let scope = scopes.new_scope(scope); 291 let scope = scopes.new_scope(scope);
283 for pat in arm.pats() { 292 for pat in &arm.pats {
284 scopes.add_bindings(scope, pat); 293 scopes.add_bindings(body, scope, *pat);
285 }
286 if let Some(expr) = arm.expr() {
287 compute_expr_scopes(expr, scopes, scope);
288 } 294 }
295 scopes.set_scope(arm.expr, scope);
296 compute_expr_scopes(arm.expr, body, scopes, scope);
289 } 297 }
290 } 298 }
291 _ => expr 299 e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
292 .syntax()
293 .children()
294 .filter_map(ast::Expr::cast)
295 .for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
296 }; 300 };
297
298 fn compute_call_scopes(
299 receiver: Option<ast::Expr>,
300 arg_list: Option<ast::ArgList>,
301 scopes: &mut FnScopes,
302 scope: ScopeId,
303 ) {
304 arg_list
305 .into_iter()
306 .flat_map(|it| it.args())
307 .chain(receiver)
308 .for_each(|expr| compute_expr_scopes(expr, scopes, scope));
309 }
310
311 fn compute_cond_scopes(
312 cond: ast::Condition,
313 scopes: &mut FnScopes,
314 scope: ScopeId,
315 ) -> Option<ScopeId> {
316 if let Some(expr) = cond.expr() {
317 compute_expr_scopes(expr, scopes, scope);
318 }
319 if let Some(pat) = cond.pat() {
320 let s = scopes.new_scope(scope);
321 scopes.add_bindings(s, pat);
322 Some(s)
323 } else {
324 None
325 }
326 }
327} 301}
328 302
329#[derive(Debug)] 303#[derive(Debug)]
@@ -338,6 +312,8 @@ mod tests {
338 use ra_syntax::SourceFileNode; 312 use ra_syntax::SourceFileNode;
339 use test_utils::{extract_offset, assert_eq_text}; 313 use test_utils::{extract_offset, assert_eq_text};
340 314
315 use crate::expr;
316
341 use super::*; 317 use super::*;
342 318
343 fn do_check(code: &str, expected: &[&str]) { 319 fn do_check(code: &str, expected: &[&str]) {
@@ -353,15 +329,20 @@ mod tests {
353 let file = SourceFileNode::parse(&code); 329 let file = SourceFileNode::parse(&code);
354 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 330 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
355 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 331 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
356 let scopes = FnScopes::new(fn_def); 332 let body_hir = expr::collect_fn_body_syntax(fn_def);
333 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
334 let scopes = ScopesWithSyntaxMapping {
335 scopes: Arc::new(scopes),
336 syntax_mapping: Arc::new(body_hir),
337 };
357 let actual = scopes 338 let actual = scopes
358 .scope_chain(marker.syntax()) 339 .scope_chain(marker.syntax())
359 .flat_map(|scope| scopes.entries(scope)) 340 .flat_map(|scope| scopes.scopes.entries(scope))
360 .map(|it| it.name().to_string()) 341 .map(|it| it.name().to_string())
361 .collect::<Vec<_>>() 342 .collect::<Vec<_>>()
362 .join("\n"); 343 .join("\n");
363 let expected = expected.join("\n"); 344 let expected = expected.join("\n");
364 assert_eq_text!(&actual, &expected); 345 assert_eq_text!(&expected, &actual);
365 } 346 }
366 347
367 #[test] 348 #[test]
@@ -389,7 +370,7 @@ mod tests {
389 } 370 }
390 371
391 #[test] 372 #[test]
392 fn test_metod_call_scope() { 373 fn test_method_call_scope() {
393 do_check( 374 do_check(
394 r" 375 r"
395 fn quux() { 376 fn quux() {
@@ -445,10 +426,15 @@ mod tests {
445 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 426 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
446 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 427 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
447 428
448 let scopes = FnScopes::new(fn_def); 429 let body_hir = expr::collect_fn_body_syntax(fn_def);
430 let scopes = FnScopes::new(Arc::clone(body_hir.body()));
431 let scopes = ScopesWithSyntaxMapping {
432 scopes: Arc::new(scopes),
433 syntax_mapping: Arc::new(body_hir),
434 };
449 435
450 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); 436 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
451 let local_name = local_name_entry.ptr().resolve(&file); 437 let local_name = local_name_entry.ptr();
452 let expected_name = 438 let expected_name =
453 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); 439 find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
454 assert_eq!(local_name.range(), expected_name.syntax().range()); 440 assert_eq!(local_name.range(), expected_name.syntax().range());
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 2abcec441..d600b91df 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -32,6 +32,7 @@ mod adt;
32mod type_ref; 32mod type_ref;
33mod ty; 33mod ty;
34mod impl_block; 34mod impl_block;
35mod expr;
35 36
36use crate::{ 37use crate::{
37 db::HirDatabase, 38 db::HirDatabase,
@@ -46,7 +47,7 @@ pub use self::{
46 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc}, 47 ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
47 macros::{MacroDef, MacroInput, MacroExpansion}, 48 macros::{MacroDef, MacroInput, MacroExpansion},
48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 49 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
49 function::{Function, FnScopes}, 50 function::{Function, FnSignature, FnScopes, ScopesWithSyntaxMapping},
50 adt::{Struct, Enum}, 51 adt::{Struct, Enum},
51 ty::Ty, 52 ty::Ty,
52 impl_block::{ImplBlock, ImplItem}, 53 impl_block::{ImplBlock, ImplItem},
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index a9db932ff..8d176662c 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -208,6 +208,9 @@ salsa::database_storage! {
208 fn struct_data() for db::StructDataQuery; 208 fn struct_data() for db::StructDataQuery;
209 fn enum_data() for db::EnumDataQuery; 209 fn enum_data() for db::EnumDataQuery;
210 fn impls_in_module() for db::ImplsInModuleQuery; 210 fn impls_in_module() for db::ImplsInModuleQuery;
211 fn body_hir() for db::BodyHirQuery;
212 fn body_syntax_mapping() for db::BodySyntaxMappingQuery;
213 fn fn_signature() for db::FnSignatureQuery;
211 } 214 }
212 } 215 }
213} 216}
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
index 017caf442..90229bc54 100644
--- a/crates/ra_hir/src/name.rs
+++ b/crates/ra_hir/src/name.rs
@@ -31,6 +31,14 @@ impl Name {
31 Name::new("[missing name]".into()) 31 Name::new("[missing name]".into())
32 } 32 }
33 33
34 pub(crate) fn self_param() -> Name {
35 Name::new("self".into())
36 }
37
38 pub(crate) fn self_type() -> Name {
39 Name::new("Self".into())
40 }
41
34 pub(crate) fn tuple_field_name(idx: usize) -> Name { 42 pub(crate) fn tuple_field_name(idx: usize) -> Name {
35 Name::new(idx.to_string().into()) 43 Name::new(idx.to_string().into())
36 } 44 }
@@ -51,7 +59,8 @@ impl Name {
51 "u128" => KnownName::U128, 59 "u128" => KnownName::U128,
52 "f32" => KnownName::F32, 60 "f32" => KnownName::F32,
53 "f64" => KnownName::F64, 61 "f64" => KnownName::F64,
54 "Self" => KnownName::Self_, 62 "Self" => KnownName::SelfType,
63 "self" => KnownName::SelfParam,
55 _ => return None, 64 _ => return None,
56 }; 65 };
57 Some(name) 66 Some(name)
@@ -104,5 +113,6 @@ pub(crate) enum KnownName {
104 F32, 113 F32,
105 F64, 114 F64,
106 115
107 Self_, 116 SelfType,
117 SelfParam,
108} 118}
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 9fdfa0d13..dcf4cf8b6 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -65,6 +65,11 @@ impl Path {
65 } 65 }
66 } 66 }
67 67
68 /// Converts an `ast::NameRef` into a single-identifier `Path`.
69 pub fn from_name_ref(name_ref: ast::NameRef) -> Path {
70 name_ref.as_name().into()
71 }
72
68 /// `true` is this path is a single identifier, like `foo` 73 /// `true` is this path is a single identifier, like `foo`
69 pub fn is_ident(&self) -> bool { 74 pub fn is_ident(&self) -> bool {
70 self.kind == PathKind::Plain && self.segments.len() == 1 75 self.kind == PathKind::Plain && self.segments.len() == 1
@@ -84,6 +89,15 @@ impl Path {
84 } 89 }
85} 90}
86 91
92impl From<Name> for Path {
93 fn from(name: Name) -> Path {
94 Path {
95 kind: PathKind::Plain,
96 segments: vec![name],
97 }
98 }
99}
100
87fn expand_use_tree( 101fn expand_use_tree(
88 prefix: Option<Path>, 102 prefix: Option<Path>,
89 tree: ast::UseTree, 103 tree: ast::UseTree,
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index a5d99beda..d9ee9d37f 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11use ra_db::{SourceRootId, Cancelable,}; 11use ra_db::{SourceRootId, Cancelable,};
12 12
13use crate::{ 13use crate::{
14 SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId, 14 SourceFileItems, SourceItemId, DefKind, DefId, Name, AsName, HirFileId,
15 MacroCallLoc, 15 MacroCallLoc,
16 db::HirDatabase, 16 db::HirDatabase,
17 function::FnScopes, 17 function::FnScopes,
@@ -23,11 +23,10 @@ use crate::{
23 adt::{StructData, EnumData}, 23 adt::{StructData, EnumData},
24}; 24};
25 25
26pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> { 26pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<FnScopes>> {
27 let function = Function::new(def_id); 27 let body = db.body_hir(def_id)?;
28 let syntax = function.syntax(db); 28 let res = FnScopes::new(body);
29 let res = FnScopes::new(syntax.borrowed()); 29 Ok(Arc::new(res))
30 Arc::new(res)
31} 30}
32 31
33pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { 32pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 85bd84469..29a3960e9 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -87,6 +87,18 @@ fn module_from_source(
87 Ok(Some(Module::new(db, source_root_id, module_id)?)) 87 Ok(Some(Module::new(db, source_root_id, module_id)?))
88} 88}
89 89
90pub fn function_from_position(
91 db: &impl HirDatabase,
92 position: FilePosition,
93) -> Cancelable<Option<Function>> {
94 let file = db.source_file(position.file_id);
95 let fn_def = ctry!(find_node_at_offset::<ast::FnDef>(
96 file.syntax(),
97 position.offset
98 ));
99 function_from_source(db, position.file_id, fn_def)
100}
101
90pub fn function_from_source( 102pub fn function_from_source(
91 db: &impl HirDatabase, 103 db: &impl HirDatabase,
92 file_id: FileId, 104 file_id: FileId,
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index e33762e0d..8c320a705 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -31,10 +31,11 @@ use ra_syntax::{
31}; 31};
32 32
33use crate::{ 33use crate::{
34 Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock, 34 Def, DefId, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock,
35 db::HirDatabase, 35 db::HirDatabase,
36 type_ref::{TypeRef, Mutability}, 36 type_ref::{TypeRef, Mutability},
37 name::KnownName, 37 name::KnownName,
38 ScopesWithSyntaxMapping,
38}; 39};
39 40
40/// The ID of a type variable. 41/// The ID of a type variable.
@@ -305,7 +306,7 @@ impl Ty {
305 return Ok(Ty::Uint(uint_ty)); 306 return Ok(Ty::Uint(uint_ty));
306 } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { 307 } else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
307 return Ok(Ty::Float(float_ty)); 308 return Ok(Ty::Float(float_ty));
308 } else if name.as_known_name() == Some(KnownName::Self_) { 309 } else if name.as_known_name() == Some(KnownName::SelfType) {
309 return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); 310 return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type()));
310 } 311 }
311 } 312 }
@@ -515,7 +516,7 @@ impl InferenceResult {
515#[derive(Clone, Debug)] 516#[derive(Clone, Debug)]
516struct InferenceContext<'a, D: HirDatabase> { 517struct InferenceContext<'a, D: HirDatabase> {
517 db: &'a D, 518 db: &'a D,
518 scopes: Arc<FnScopes>, 519 scopes: ScopesWithSyntaxMapping,
519 /// The self param for the current method, if it exists. 520 /// The self param for the current method, if it exists.
520 self_param: Option<LocalSyntaxPtr>, 521 self_param: Option<LocalSyntaxPtr>,
521 module: Module, 522 module: Module,
@@ -529,7 +530,7 @@ struct InferenceContext<'a, D: HirDatabase> {
529impl<'a, D: HirDatabase> InferenceContext<'a, D> { 530impl<'a, D: HirDatabase> InferenceContext<'a, D> {
530 fn new( 531 fn new(
531 db: &'a D, 532 db: &'a D,
532 scopes: Arc<FnScopes>, 533 scopes: ScopesWithSyntaxMapping,
533 module: Module, 534 module: Module,
534 impl_block: Option<ImplBlock>, 535 impl_block: Option<ImplBlock>,
535 ) -> Self { 536 ) -> Self {
@@ -826,10 +827,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
826 self.infer_expr_opt(e.expr(), &Expectation::none())?; 827 self.infer_expr_opt(e.expr(), &Expectation::none())?;
827 Ty::Never 828 Ty::Never
828 } 829 }
829 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
830 // Can this even occur outside of a match expression?
831 Ty::Unknown
832 }
833 ast::Expr::StructLit(e) => { 830 ast::Expr::StructLit(e) => {
834 let (ty, def_id) = self.resolve_variant(e.path())?; 831 let (ty, def_id) = self.resolve_variant(e.path())?;
835 if let Some(nfl) = e.named_field_list() { 832 if let Some(nfl) = e.named_field_list() {
@@ -845,10 +842,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
845 } 842 }
846 ty 843 ty
847 } 844 }
848 ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
849 // Can this even occur outside of a struct literal?
850 Ty::Unknown
851 }
852 ast::Expr::IndexExpr(_e) => Ty::Unknown, 845 ast::Expr::IndexExpr(_e) => Ty::Unknown,
853 ast::Expr::FieldExpr(e) => { 846 ast::Expr::FieldExpr(e) => {
854 let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?; 847 let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
@@ -1016,7 +1009,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1016 1009
1017pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> { 1010pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
1018 let function = Function::new(def_id); // TODO: consts also need inference 1011 let function = Function::new(def_id); // TODO: consts also need inference
1019 let scopes = function.scopes(db); 1012 let scopes = function.scopes(db)?;
1020 let module = function.module(db)?; 1013 let module = function.module(db)?;
1021 let impl_block = function.impl_block(db)?; 1014 let impl_block = function.impl_block(db)?;
1022 let mut ctx = InferenceContext::new(db, scopes, module, impl_block); 1015 let mut ctx = InferenceContext::new(db, scopes, module, impl_block);
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index fb53fcf0b..515c66e85 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -1,7 +1,10 @@
1use std::sync::Arc;
1use std::fmt::Write; 2use std::fmt::Write;
2use std::path::{PathBuf, Path}; 3use std::path::{PathBuf, Path};
3use std::fs; 4use std::fs;
4 5
6use salsa::Database;
7
5use ra_db::{SyntaxDatabase}; 8use ra_db::{SyntaxDatabase};
6use ra_syntax::ast::{self, AstNode}; 9use ra_syntax::ast::{self, AstNode};
7use test_utils::{project_dir, assert_eq_text, read_text}; 10use test_utils::{project_dir, assert_eq_text, read_text};
@@ -217,3 +220,44 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
217fn test_data_dir() -> PathBuf { 220fn test_data_dir() -> PathBuf {
218 project_dir().join("crates/ra_hir/src/ty/tests/data") 221 project_dir().join("crates/ra_hir/src/ty/tests/data")
219} 222}
223
224#[test]
225#[should_panic] // TODO this should work once hir::Expr is used
226fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
227 let (mut db, pos) = MockDatabase::with_position(
228 "
229 //- /lib.rs
230 fn foo() -> i32 {
231 <|>1 + 1
232 }
233 ",
234 );
235 let func = source_binder::function_from_position(&db, pos)
236 .unwrap()
237 .unwrap();
238 {
239 let events = db.log_executed(|| {
240 func.infer(&db).unwrap();
241 });
242 assert!(format!("{:?}", events).contains("infer"))
243 }
244
245 let new_text = "
246 fn foo() -> i32 {
247 1
248 +
249 1
250 }
251 "
252 .to_string();
253
254 db.query_mut(ra_db::FileTextQuery)
255 .set(pos.file_id, Arc::new(new_text));
256
257 {
258 let events = db.log_executed(|| {
259 func.infer(&db).unwrap();
260 });
261 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
262 }
263}
diff --git a/crates/ra_hir/src/type_ref.rs b/crates/ra_hir/src/type_ref.rs
index b36bb35d8..859f330c2 100644
--- a/crates/ra_hir/src/type_ref.rs
+++ b/crates/ra_hir/src/type_ref.rs
@@ -107,4 +107,8 @@ impl TypeRef {
107 TypeRef::Error 107 TypeRef::Error
108 } 108 }
109 } 109 }
110
111 pub fn unit() -> TypeRef {
112 TypeRef::Tuple(Vec::new())
113 }
110} 114}