aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-04-13 12:17:48 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-04-13 12:17:48 +0100
commit8887782c4ab97d22f3d5c10e142407e4371c5c61 (patch)
tree80b60b2c0c2f6104b98e16648b95d99d9b1d3463 /crates/ra_hir/src
parent34a05b7fea4add78446b2d93a64538982abacb9f (diff)
parent2facb5e061971afbf6bd2fabe3966d5de9d46489 (diff)
Merge #1129
1129: introduce SourceAnalyzer API for ides r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r--crates/ra_hir/src/code_model_api.rs48
-rw-r--r--crates/ra_hir/src/expr.rs30
-rw-r--r--crates/ra_hir/src/expr/scope.rs232
-rw-r--r--crates/ra_hir/src/impl_block.rs2
-rw-r--r--crates/ra_hir/src/lib.rs8
-rw-r--r--crates/ra_hir/src/resolve.rs18
-rw-r--r--crates/ra_hir/src/source_binder.rs406
-rw-r--r--crates/ra_hir/src/ty.rs14
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs2
-rw-r--r--crates/ra_hir/src/ty/tests.rs62
10 files changed, 408 insertions, 414 deletions
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs
index f13a6b37a..5d8cf57b6 100644
--- a/crates/ra_hir/src/code_model_api.rs
+++ b/crates/ra_hir/src/code_model_api.rs
@@ -4,7 +4,7 @@ use ra_db::{CrateId, SourceRootId, Edition};
4use ra_syntax::{ast::self, TreeArc}; 4use ra_syntax::{ast::self, TreeArc};
5 5
6use crate::{ 6use crate::{
7 Name, ScopesWithSourceMap, Ty, HirFileId, Either, 7 Name, Ty, HirFileId, Either,
8 HirDatabase, DefDatabase, 8 HirDatabase, DefDatabase,
9 type_ref::TypeRef, 9 type_ref::TypeRef,
10 nameres::{ModuleScope, Namespace, ImportId, CrateModuleId}, 10 nameres::{ModuleScope, Namespace, ImportId, CrateModuleId},
@@ -189,7 +189,7 @@ impl Module {
189 } 189 }
190 } 190 }
191 191
192 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 192 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
193 let def_map = db.crate_def_map(self.krate); 193 let def_map = db.crate_def_map(self.krate);
194 Resolver::default().push_module_scope(def_map, self.module_id) 194 Resolver::default().push_module_scope(def_map, self.module_id)
195 } 195 }
@@ -313,7 +313,7 @@ impl Struct {
313 313
314 // FIXME move to a more general type 314 // FIXME move to a more general type
315 /// Builds a resolver for type references inside this struct. 315 /// Builds a resolver for type references inside this struct.
316 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 316 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
317 // take the outer scope... 317 // take the outer scope...
318 let r = self.module(db).resolver(db); 318 let r = self.module(db).resolver(db);
319 // ...and add generic params, if present 319 // ...and add generic params, if present
@@ -373,7 +373,7 @@ impl Enum {
373 373
374 // FIXME: move to a more general type 374 // FIXME: move to a more general type
375 /// Builds a resolver for type references inside this struct. 375 /// Builds a resolver for type references inside this struct.
376 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 376 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
377 // take the outer scope... 377 // take the outer scope...
378 let r = self.module(db).resolver(db); 378 let r = self.module(db).resolver(db);
379 // ...and add generic params, if present 379 // ...and add generic params, if present
@@ -450,28 +450,22 @@ impl DefWithBody {
450 db.infer(*self) 450 db.infer(*self)
451 } 451 }
452 452
453 pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
454 db.body_with_source_map(*self).1
455 }
456
457 pub fn body(&self, db: &impl HirDatabase) -> Arc<Body> { 453 pub fn body(&self, db: &impl HirDatabase) -> Arc<Body> {
458 db.body_hir(*self) 454 db.body_hir(*self)
459 } 455 }
460 456
457 pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
458 db.body_with_source_map(*self).1
459 }
460
461 /// Builds a resolver for code inside this item. 461 /// Builds a resolver for code inside this item.
462 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 462 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
463 match *self { 463 match *self {
464 DefWithBody::Const(ref c) => c.resolver(db), 464 DefWithBody::Const(ref c) => c.resolver(db),
465 DefWithBody::Function(ref f) => f.resolver(db), 465 DefWithBody::Function(ref f) => f.resolver(db),
466 DefWithBody::Static(ref s) => s.resolver(db), 466 DefWithBody::Static(ref s) => s.resolver(db),
467 } 467 }
468 } 468 }
469
470 pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSourceMap {
471 let scopes = db.expr_scopes(*self);
472 let source_map = db.body_with_source_map(*self).1;
473 ScopesWithSourceMap { scopes, source_map }
474 }
475} 469}
476 470
477#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 471#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -523,7 +517,7 @@ impl Function {
523 self.signature(db).name.clone() 517 self.signature(db).name.clone()
524 } 518 }
525 519
526 pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { 520 pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
527 db.body_with_source_map((*self).into()).1 521 db.body_with_source_map((*self).into()).1
528 } 522 }
529 523
@@ -535,12 +529,6 @@ impl Function {
535 db.type_for_def((*self).into(), Namespace::Values) 529 db.type_for_def((*self).into(), Namespace::Values)
536 } 530 }
537 531
538 pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSourceMap {
539 let scopes = db.expr_scopes((*self).into());
540 let source_map = db.body_with_source_map((*self).into()).1;
541 ScopesWithSourceMap { scopes, source_map }
542 }
543
544 pub fn signature(&self, db: &impl HirDatabase) -> Arc<FnSignature> { 532 pub fn signature(&self, db: &impl HirDatabase) -> Arc<FnSignature> {
545 db.fn_signature(*self) 533 db.fn_signature(*self)
546 } 534 }
@@ -561,7 +549,7 @@ impl Function {
561 549
562 // FIXME: move to a more general type for 'body-having' items 550 // FIXME: move to a more general type for 'body-having' items
563 /// Builds a resolver for code inside this item. 551 /// Builds a resolver for code inside this item.
564 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 552 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
565 // take the outer scope... 553 // take the outer scope...
566 let r = self 554 let r = self
567 .impl_block(db) 555 .impl_block(db)
@@ -606,10 +594,6 @@ impl Const {
606 db.infer((*self).into()) 594 db.infer((*self).into())
607 } 595 }
608 596
609 pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
610 db.body_with_source_map((*self).into()).1
611 }
612
613 /// The containing impl block, if this is a method. 597 /// The containing impl block, if this is a method.
614 pub fn impl_block(&self, db: &impl DefDatabase) -> Option<ImplBlock> { 598 pub fn impl_block(&self, db: &impl DefDatabase) -> Option<ImplBlock> {
615 let module_impls = db.impls_in_module(self.module(db)); 599 let module_impls = db.impls_in_module(self.module(db));
@@ -618,7 +602,7 @@ impl Const {
618 602
619 // FIXME: move to a more general type for 'body-having' items 603 // FIXME: move to a more general type for 'body-having' items
620 /// Builds a resolver for code inside this item. 604 /// Builds a resolver for code inside this item.
621 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 605 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
622 // take the outer scope... 606 // take the outer scope...
623 let r = self 607 let r = self
624 .impl_block(db) 608 .impl_block(db)
@@ -670,7 +654,7 @@ impl Static {
670 } 654 }
671 655
672 /// Builds a resolver for code inside this item. 656 /// Builds a resolver for code inside this item.
673 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 657 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
674 // take the outer scope... 658 // take the outer scope...
675 self.module(db).resolver(db) 659 self.module(db).resolver(db)
676 } 660 }
@@ -678,10 +662,6 @@ impl Static {
678 pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> { 662 pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> {
679 db.infer((*self).into()) 663 db.infer((*self).into())
680 } 664 }
681
682 pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> {
683 db.body_with_source_map((*self).into()).1
684 }
685} 665}
686 666
687impl Docs for Static { 667impl Docs for Static {
@@ -756,7 +736,7 @@ impl TypeAlias {
756 } 736 }
757 737
758 /// Builds a resolver for the type references in this type alias. 738 /// Builds a resolver for the type references in this type alias.
759 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 739 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
760 // take the outer scope... 740 // take the outer scope...
761 let r = self 741 let r = self
762 .impl_block(db) 742 .impl_block(db)
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index a8e115e47..817e660f9 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -14,9 +14,9 @@ use crate::{
14 name::AsName, 14 name::AsName,
15 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
16}; 16};
17use crate::{ path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; 17use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}};
18 18
19pub use self::scope::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}; 19pub use self::scope::ExprScopes;
20 20
21pub(crate) mod scope; 21pub(crate) mod scope;
22 22
@@ -81,19 +81,23 @@ impl Body {
81} 81}
82 82
83// needs arbitrary_self_types to be a method... or maybe move to the def? 83// needs arbitrary_self_types to be a method... or maybe move to the def?
84pub fn resolver_for_expr(body: Arc<Body>, db: &impl HirDatabase, expr_id: ExprId) -> Resolver { 84pub(crate) fn resolver_for_expr(
85 body: Arc<Body>,
86 db: &impl HirDatabase,
87 expr_id: ExprId,
88) -> Resolver {
85 let scopes = db.expr_scopes(body.owner); 89 let scopes = db.expr_scopes(body.owner);
86 resolver_for_scope(body, db, scopes.scope_for(expr_id)) 90 resolver_for_scope(body, db, scopes.scope_for(expr_id))
87} 91}
88 92
89pub fn resolver_for_scope( 93pub(crate) fn resolver_for_scope(
90 body: Arc<Body>, 94 body: Arc<Body>,
91 db: &impl HirDatabase, 95 db: &impl HirDatabase,
92 scope_id: Option<scope::ScopeId>, 96 scope_id: Option<scope::ScopeId>,
93) -> Resolver { 97) -> Resolver {
94 let mut r = body.owner.resolver(db); 98 let mut r = body.owner.resolver(db);
95 let scopes = db.expr_scopes(body.owner); 99 let scopes = db.expr_scopes(body.owner);
96 let scope_chain = scopes.scope_chain_for(scope_id).collect::<Vec<_>>(); 100 let scope_chain = scopes.scope_chain(scope_id).collect::<Vec<_>>();
97 for scope in scope_chain.into_iter().rev() { 101 for scope in scope_chain.into_iter().rev() {
98 r = r.push_expr_scope(Arc::clone(&scopes), scope); 102 r = r.push_expr_scope(Arc::clone(&scopes), scope);
99 } 103 }
@@ -117,31 +121,27 @@ impl Index<PatId> for Body {
117} 121}
118 122
119impl BodySourceMap { 123impl BodySourceMap {
120 pub fn expr_syntax(&self, expr: ExprId) -> Option<SyntaxNodePtr> { 124 pub(crate) fn expr_syntax(&self, expr: ExprId) -> Option<SyntaxNodePtr> {
121 self.expr_map_back.get(expr).cloned() 125 self.expr_map_back.get(expr).cloned()
122 } 126 }
123 127
124 pub fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option<ExprId> { 128 pub(crate) fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option<ExprId> {
125 self.expr_map.get(&ptr).cloned() 129 self.expr_map.get(&ptr).cloned()
126 } 130 }
127 131
128 pub fn node_expr(&self, node: &ast::Expr) -> Option<ExprId> { 132 pub(crate) fn node_expr(&self, node: &ast::Expr) -> Option<ExprId> {
129 self.expr_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() 133 self.expr_map.get(&SyntaxNodePtr::new(node.syntax())).cloned()
130 } 134 }
131 135
132 pub fn pat_syntax(&self, pat: PatId) -> Option<PatPtr> { 136 pub(crate) fn pat_syntax(&self, pat: PatId) -> Option<PatPtr> {
133 self.pat_map_back.get(pat).cloned() 137 self.pat_map_back.get(pat).cloned()
134 } 138 }
135 139
136 pub fn syntax_pat(&self, ptr: PatPtr) -> Option<PatId> { 140 pub(crate) fn node_pat(&self, node: &ast::Pat) -> Option<PatId> {
137 self.pat_map.get(&ptr).cloned()
138 }
139
140 pub fn node_pat(&self, node: &ast::Pat) -> Option<PatId> {
141 self.pat_map.get(&Either::A(AstPtr::new(node))).cloned() 141 self.pat_map.get(&Either::A(AstPtr::new(node))).cloned()
142 } 142 }
143 143
144 pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> { 144 pub(crate) fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> {
145 self.field_map[&(expr, field)].clone() 145 self.field_map[&(expr, field)].clone()
146 } 146 }
147} 147}
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs
index 725b6c00e..58f365128 100644
--- a/crates/ra_hir/src/expr/scope.rs
+++ b/crates/ra_hir/src/expr/scope.rs
@@ -1,17 +1,11 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use rustc_hash::{FxHashMap, FxHashSet}; 3use rustc_hash::FxHashMap;
4
5use ra_syntax::{
6 AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr,
7 algo::generate,
8 ast,
9};
10use ra_arena::{Arena, RawId, impl_arena_id}; 4use ra_arena::{Arena, RawId, impl_arena_id};
11 5
12use crate::{ 6use crate::{
13 Name, AsName,DefWithBody, Either, 7 Name, DefWithBody,
14 expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySourceMap}, 8 expr::{PatId, ExprId, Pat, Expr, Body, Statement},
15 HirDatabase, 9 HirDatabase,
16}; 10};
17 11
@@ -23,23 +17,32 @@ impl_arena_id!(ScopeId);
23pub struct ExprScopes { 17pub struct ExprScopes {
24 body: Arc<Body>, 18 body: Arc<Body>,
25 scopes: Arena<ScopeId, ScopeData>, 19 scopes: Arena<ScopeId, ScopeData>,
26 scope_for: FxHashMap<ExprId, ScopeId>, 20 scope_by_expr: FxHashMap<ExprId, ScopeId>,
27} 21}
28 22
29#[derive(Debug, PartialEq, Eq)] 23#[derive(Debug, PartialEq, Eq)]
30pub struct ScopeEntry { 24pub(crate) struct ScopeEntry {
31 name: Name, 25 name: Name,
32 pat: PatId, 26 pat: PatId,
33} 27}
34 28
29impl ScopeEntry {
30 pub(crate) fn name(&self) -> &Name {
31 &self.name
32 }
33
34 pub(crate) fn pat(&self) -> PatId {
35 self.pat
36 }
37}
38
35#[derive(Debug, PartialEq, Eq)] 39#[derive(Debug, PartialEq, Eq)]
36pub struct ScopeData { 40pub(crate) struct ScopeData {
37 parent: Option<ScopeId>, 41 parent: Option<ScopeId>,
38 entries: Vec<ScopeEntry>, 42 entries: Vec<ScopeEntry>,
39} 43}
40 44
41impl ExprScopes { 45impl ExprScopes {
42 // FIXME: This should take something more general than Function
43 pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> { 46 pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc<ExprScopes> {
44 let body = db.body_hir(def); 47 let body = db.body_hir(def);
45 let res = ExprScopes::new(body); 48 let res = ExprScopes::new(body);
@@ -50,7 +53,7 @@ impl ExprScopes {
50 let mut scopes = ExprScopes { 53 let mut scopes = ExprScopes {
51 body: body.clone(), 54 body: body.clone(),
52 scopes: Arena::default(), 55 scopes: Arena::default(),
53 scope_for: FxHashMap::default(), 56 scope_by_expr: FxHashMap::default(),
54 }; 57 };
55 let root = scopes.root_scope(); 58 let root = scopes.root_scope();
56 scopes.add_params_bindings(root, body.params()); 59 scopes.add_params_bindings(root, body.params());
@@ -58,19 +61,23 @@ impl ExprScopes {
58 scopes 61 scopes
59 } 62 }
60 63
61 pub fn body(&self) -> Arc<Body> { 64 pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
62 self.body.clone()
63 }
64
65 pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
66 &self.scopes[scope].entries 65 &self.scopes[scope].entries
67 } 66 }
68 67
69 pub fn scope_chain_for<'a>( 68 pub(crate) fn scope_chain<'a>(
70 &'a self, 69 &'a self,
71 scope: Option<ScopeId>, 70 scope: Option<ScopeId>,
72 ) -> impl Iterator<Item = ScopeId> + 'a { 71 ) -> impl Iterator<Item = ScopeId> + 'a {
73 generate(scope, move |&scope| self.scopes[scope].parent) 72 std::iter::successors(scope, move |&scope| self.scopes[scope].parent)
73 }
74
75 pub(crate) fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
76 self.scope_by_expr.get(&expr).map(|&scope| scope)
77 }
78
79 pub(crate) fn scope_by_expr(&self) -> &FxHashMap<ExprId, ScopeId> {
80 &self.scope_by_expr
74 } 81 }
75 82
76 fn root_scope(&mut self) -> ScopeId { 83 fn root_scope(&mut self) -> ScopeId {
@@ -99,130 +106,7 @@ impl ExprScopes {
99 } 106 }
100 107
101 fn set_scope(&mut self, node: ExprId, scope: ScopeId) { 108 fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
102 self.scope_for.insert(node, scope); 109 self.scope_by_expr.insert(node, scope);
103 }
104
105 pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
106 self.scope_for.get(&expr).map(|&scope| scope)
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct ScopesWithSourceMap {
112 pub source_map: Arc<BodySourceMap>,
113 pub scopes: Arc<ExprScopes>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct ScopeEntryWithSyntax {
118 name: Name,
119 ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
120}
121
122impl ScopeEntryWithSyntax {
123 pub fn name(&self) -> &Name {
124 &self.name
125 }
126
127 pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> {
128 self.ptr
129 }
130}
131
132impl ScopesWithSourceMap {
133 fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator<Item = ScopeId> + 'a {
134 generate(self.scope_for(node), move |&scope| self.scopes.scopes[scope].parent)
135 }
136
137 pub fn scope_for_offset(&self, offset: TextUnit) -> Option<ScopeId> {
138 self.scopes
139 .scope_for
140 .iter()
141 .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope)))
142 // find containing scope
143 .min_by_key(|(ptr, _scope)| {
144 (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len())
145 })
146 .map(|(ptr, scope)| self.adjust(ptr, *scope, offset))
147 }
148
149 // XXX: during completion, cursor might be outside of any particular
150 // expression. Try to figure out the correct scope...
151 // FIXME: move this to source binder?
152 fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
153 let r = ptr.range();
154 let child_scopes = self
155 .scopes
156 .scope_for
157 .iter()
158 .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope)))
159 .map(|(ptr, scope)| (ptr.range(), scope))
160 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
161
162 child_scopes
163 .max_by(|(r1, _), (r2, _)| {
164 if r2.is_subrange(&r1) {
165 std::cmp::Ordering::Greater
166 } else if r1.is_subrange(&r2) {
167 std::cmp::Ordering::Less
168 } else {
169 r1.start().cmp(&r2.start())
170 }
171 })
172 .map(|(_ptr, scope)| *scope)
173 .unwrap_or(original_scope)
174 }
175
176 pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
177 let mut shadowed = FxHashSet::default();
178 let name = name_ref.as_name();
179 let ret = self
180 .scope_chain(name_ref.syntax())
181 .flat_map(|scope| self.scopes.entries(scope).iter())
182 .filter(|entry| shadowed.insert(entry.name()))
183 .filter(|entry| entry.name() == &name)
184 .nth(0);
185 ret.and_then(|entry| {
186 Some(ScopeEntryWithSyntax {
187 name: entry.name().clone(),
188 ptr: self.source_map.pat_syntax(entry.pat())?,
189 })
190 })
191 }
192
193 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
194 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
195 let ptr = Either::A(AstPtr::new(pat.into()));
196 fn_def
197 .syntax()
198 .descendants()
199 .filter_map(ast::NameRef::cast)
200 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
201 None => false,
202 Some(entry) => entry.ptr() == ptr,
203 })
204 .map(|name_ref| ReferenceDescriptor {
205 name: name_ref.syntax().text().to_string(),
206 range: name_ref.syntax().range(),
207 })
208 .collect()
209 }
210
211 pub fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
212 node.ancestors()
213 .map(SyntaxNodePtr::new)
214 .filter_map(|ptr| self.source_map.syntax_expr(ptr))
215 .find_map(|it| self.scopes.scope_for(it))
216 }
217}
218
219impl ScopeEntry {
220 pub fn name(&self) -> &Name {
221 &self.name
222 }
223
224 pub fn pat(&self) -> PatId {
225 self.pat
226 } 110 }
227} 111}
228 112
@@ -286,22 +170,13 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
286 }; 170 };
287} 171}
288 172
289#[derive(Debug)]
290pub struct ReferenceDescriptor {
291 pub range: TextRange,
292 pub name: String,
293}
294
295#[cfg(test)] 173#[cfg(test)]
296mod tests { 174mod tests {
297 use ra_db::salsa::InternKey; 175 use ra_db::SourceDatabase;
298 use ra_syntax::{SourceFile, algo::find_node_at_offset}; 176 use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr, ast};
299 use test_utils::{extract_offset, assert_eq_text}; 177 use test_utils::{extract_offset, assert_eq_text};
300 use crate::Function;
301
302 use crate::expr::{ExprCollector};
303 178
304 use super::*; 179 use crate::{source_binder::SourceAnalyzer, mock::MockDatabase};
305 180
306 fn do_check(code: &str, expected: &[&str]) { 181 fn do_check(code: &str, expected: &[&str]) {
307 let (off, code) = extract_offset(code); 182 let (off, code) = extract_offset(code);
@@ -313,18 +188,20 @@ mod tests {
313 buf.push_str(&code[off..]); 188 buf.push_str(&code[off..]);
314 buf 189 buf
315 }; 190 };
316 let file = SourceFile::parse(&code); 191
192 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
193 let file = db.parse(file_id);
317 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 194 let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
318 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 195 let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
319 let irrelevant_function = 196
320 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) }; 197 let scopes = analyzer.scopes();
321 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def); 198 let expr_id =
322 let scopes = ExprScopes::new(Arc::new(body)); 199 analyzer.body_source_map().syntax_expr(SyntaxNodePtr::new(marker.syntax())).unwrap();
323 let scopes = 200 let scope = scopes.scope_for(expr_id);
324 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) }; 201
325 let actual = scopes 202 let actual = scopes
326 .scope_chain(marker.syntax()) 203 .scope_chain(scope)
327 .flat_map(|scope| scopes.scopes.entries(scope)) 204 .flat_map(|scope| scopes.entries(scope))
328 .map(|it| it.name().to_string()) 205 .map(|it| it.name().to_string())
329 .collect::<Vec<_>>() 206 .collect::<Vec<_>>()
330 .join("\n"); 207 .join("\n");
@@ -407,28 +284,17 @@ mod tests {
407 ); 284 );
408 } 285 }
409 286
410 fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> (Body, BodySourceMap) {
411 let mut collector = ExprCollector::new(DefWithBody::Function(function));
412 collector.collect_fn_body(node);
413 collector.finish()
414 }
415
416 fn do_check_local_name(code: &str, expected_offset: u32) { 287 fn do_check_local_name(code: &str, expected_offset: u32) {
417 let (off, code) = extract_offset(code); 288 let (off, code) = extract_offset(code);
418 let file = SourceFile::parse(&code); 289
290 let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
291 let file = db.parse(file_id);
419 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()) 292 let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
420 .expect("failed to find a name at the target offset"); 293 .expect("failed to find a name at the target offset");
421
422 let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
423 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 294 let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
295 let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
424 296
425 let irrelevant_function = 297 let local_name_entry = analyzer.resolve_local_name(name_ref).unwrap();
426 Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) };
427 let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def);
428 let scopes = ExprScopes::new(Arc::new(body));
429 let scopes =
430 ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) };
431 let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
432 let local_name = 298 let local_name =
433 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); 299 local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
434 assert_eq!(local_name.range(), expected_name.syntax().range()); 300 assert_eq!(local_name.range(), expected_name.syntax().range());
diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs
index 40d368cd9..b306874cc 100644
--- a/crates/ra_hir/src/impl_block.rs
+++ b/crates/ra_hir/src/impl_block.rs
@@ -105,7 +105,7 @@ impl ImplBlock {
105 db.generic_params((*self).into()) 105 db.generic_params((*self).into())
106 } 106 }
107 107
108 pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { 108 pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver {
109 let r = self.module().resolver(db); 109 let r = self.module().resolver(db);
110 // add generic params, if present 110 // add generic params, if present
111 let p = self.generic_params(db); 111 let p = self.generic_params(db);
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 0881939a2..a9db23060 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -51,6 +51,7 @@ use crate::{
51 db::{HirDatabase, DefDatabase}, 51 db::{HirDatabase, DefDatabase},
52 name::{AsName, KnownName}, 52 name::{AsName, KnownName},
53 source_id::{FileAstId, AstId}, 53 source_id::{FileAstId, AstId},
54 resolve::Resolver,
54}; 55};
55 56
56pub use self::{ 57pub use self::{
@@ -60,12 +61,13 @@ pub use self::{
60 source_id::{AstIdMap, ErasedFileAstId}, 61 source_id::{AstIdMap, ErasedFileAstId},
61 ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc}, 62 ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc},
62 nameres::{PerNs, Namespace, ImportId}, 63 nameres::{PerNs, Namespace, ImportId},
63 ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, 64 ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay, CallableDef},
64 impl_block::{ImplBlock, ImplItem}, 65 impl_block::{ImplBlock, ImplItem},
65 docs::{Docs, Documentation}, 66 docs::{Docs, Documentation},
66 adt::AdtDef, 67 adt::AdtDef,
67 expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, 68 expr::ExprScopes,
68 resolve::{Resolver, Resolution}, 69 resolve::Resolution,
70 source_binder::{SourceAnalyzer, PathResolution, ScopeEntryWithSyntax},
69}; 71};
70 72
71pub use self::code_model_api::{ 73pub use self::code_model_api::{
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs
index 2609585b1..685f4b8b1 100644
--- a/crates/ra_hir/src/resolve.rs
+++ b/crates/ra_hir/src/resolve.rs
@@ -9,13 +9,13 @@ use crate::{
9 name::{Name, KnownName}, 9 name::{Name, KnownName},
10 nameres::{PerNs, CrateDefMap, CrateModuleId}, 10 nameres::{PerNs, CrateDefMap, CrateModuleId},
11 generics::GenericParams, 11 generics::GenericParams,
12 expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, 12 expr::{scope::{ExprScopes, ScopeId}, PatId},
13 impl_block::ImplBlock, 13 impl_block::ImplBlock,
14 path::Path, Trait 14 path::Path, Trait
15}; 15};
16 16
17#[derive(Debug, Clone, Default)] 17#[derive(Debug, Clone, Default)]
18pub struct Resolver { 18pub(crate) struct Resolver {
19 scopes: Vec<Scope>, 19 scopes: Vec<Scope>,
20} 20}
21 21
@@ -117,7 +117,7 @@ pub enum Resolution {
117} 117}
118 118
119impl Resolver { 119impl Resolver {
120 pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> { 120 pub(crate) fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
121 let mut resolution = PerNs::none(); 121 let mut resolution = PerNs::none();
122 for scope in self.scopes.iter().rev() { 122 for scope in self.scopes.iter().rev() {
123 resolution = resolution.or(scope.resolve_name(db, name)); 123 resolution = resolution.or(scope.resolve_name(db, name));
@@ -154,12 +154,12 @@ impl Resolver {
154 154
155 /// Returns the fully resolved path if we were able to resolve it. 155 /// Returns the fully resolved path if we were able to resolve it.
156 /// otherwise returns `PerNs::none` 156 /// otherwise returns `PerNs::none`
157 pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> { 157 pub(crate) fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
158 // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise 158 // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise
159 self.resolve_path_segments(db, path).into_fully_resolved() 159 self.resolve_path_segments(db, path).into_fully_resolved()
160 } 160 }
161 161
162 pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> { 162 pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
163 let mut names = FxHashMap::default(); 163 let mut names = FxHashMap::default();
164 for scope in self.scopes.iter().rev() { 164 for scope in self.scopes.iter().rev() {
165 scope.collect_names(db, &mut |name, res| { 165 scope.collect_names(db, &mut |name, res| {
@@ -197,14 +197,6 @@ impl Resolver {
197 _ => None, 197 _ => None,
198 }) 198 })
199 } 199 }
200
201 /// The body from which any `LocalBinding` resolutions in this resolver come.
202 pub fn body(&self) -> Option<Arc<Body>> {
203 self.scopes.iter().rev().find_map(|scope| match scope {
204 Scope::ExprScope(expr_scope) => Some(expr_scope.expr_scopes.body()),
205 _ => None,
206 })
207 }
208} 200}
209 201
210impl Resolver { 202impl Resolver {
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 182ed4c91..bd035ced9 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -5,16 +5,21 @@
5/// 5///
6/// So, this modules should not be used during hir construction, it exists 6/// So, this modules should not be used during hir construction, it exists
7/// purely for "IDE needs". 7/// purely for "IDE needs".
8use std::sync::Arc;
9
10use rustc_hash::{FxHashSet, FxHashMap};
8use ra_db::{FileId, FilePosition}; 11use ra_db::{FileId, FilePosition};
9use ra_syntax::{ 12use ra_syntax::{
10 SyntaxNode, 13 SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, TextRange,
11 ast::{self, AstNode, NameOwner}, 14 ast::{self, AstNode, NameOwner},
12 algo::{find_node_at_offset, find_token_at_offset}, 15 algo::find_node_at_offset,
16 SyntaxKind::*,
13}; 17};
14 18
15use crate::{ 19use crate::{
16 HirDatabase, Function, Struct, Enum,Const,Static, 20 HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name,
17 AsName, Module, HirFileId, Crate, Trait, Resolver, 21 AsName, Module, HirFileId, Crate, Trait, Resolver,
22 expr::{BodySourceMap, scope::{ScopeId, ExprScopes}},
18 ids::LocationCtx, 23 ids::LocationCtx,
19 expr, AstId 24 expr, AstId
20}; 25};
@@ -87,63 +92,6 @@ fn module_from_source(
87 ) 92 )
88} 93}
89 94
90pub fn const_from_source(
91 db: &impl HirDatabase,
92 file_id: FileId,
93 const_def: &ast::ConstDef,
94) -> Option<Const> {
95 let module = module_from_child_node(db, file_id, const_def.syntax())?;
96 let res = const_from_module(db, module, const_def);
97 Some(res)
98}
99
100pub fn const_from_module(
101 db: &impl HirDatabase,
102 module: Module,
103 const_def: &ast::ConstDef,
104) -> Const {
105 let (file_id, _) = module.definition_source(db);
106 let file_id = file_id.into();
107 let ctx = LocationCtx::new(db, module, file_id);
108 Const { id: ctx.to_def(const_def) }
109}
110
111pub fn function_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Function> {
112 let file = db.parse(position.file_id);
113 let fn_def = find_node_at_offset::<ast::FnDef>(file.syntax(), position.offset)?;
114 function_from_source(db, position.file_id, fn_def)
115}
116
117pub fn function_from_source(
118 db: &impl HirDatabase,
119 file_id: FileId,
120 fn_def: &ast::FnDef,
121) -> Option<Function> {
122 let module = module_from_child_node(db, file_id, fn_def.syntax())?;
123 let res = function_from_module(db, module, fn_def);
124 Some(res)
125}
126
127pub fn function_from_module(
128 db: &impl HirDatabase,
129 module: Module,
130 fn_def: &ast::FnDef,
131) -> Function {
132 let (file_id, _) = module.definition_source(db);
133 let file_id = file_id.into();
134 let ctx = LocationCtx::new(db, module, file_id);
135 Function { id: ctx.to_def(fn_def) }
136}
137
138pub fn function_from_child_node(
139 db: &impl HirDatabase,
140 file_id: FileId,
141 node: &SyntaxNode,
142) -> Option<Function> {
143 let fn_def = node.ancestors().find_map(ast::FnDef::cast)?;
144 function_from_source(db, file_id, fn_def)
145}
146
147pub fn struct_from_module( 95pub fn struct_from_module(
148 db: &impl HirDatabase, 96 db: &impl HirDatabase,
149 module: Module, 97 module: Module,
@@ -155,27 +103,6 @@ pub fn struct_from_module(
155 Struct { id: ctx.to_def(struct_def) } 103 Struct { id: ctx.to_def(struct_def) }
156} 104}
157 105
158pub fn static_from_source(
159 db: &impl HirDatabase,
160 file_id: FileId,
161 static_def: &ast::StaticDef,
162) -> Option<Static> {
163 let module = module_from_child_node(db, file_id, static_def.syntax())?;
164 let res = static_from_module(db, module, static_def);
165 Some(res)
166}
167
168pub fn static_from_module(
169 db: &impl HirDatabase,
170 module: Module,
171 static_def: &ast::StaticDef,
172) -> Static {
173 let (file_id, _) = module.definition_source(db);
174 let file_id = file_id.into();
175 let ctx = LocationCtx::new(db, module, file_id);
176 Static { id: ctx.to_def(static_def) }
177}
178
179pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum { 106pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum {
180 let (file_id, _) = module.definition_source(db); 107 let (file_id, _) = module.definition_source(db);
181 let file_id = file_id.into(); 108 let file_id = file_id.into();
@@ -194,48 +121,6 @@ pub fn trait_from_module(
194 Trait { id: ctx.to_def(trait_def) } 121 Trait { id: ctx.to_def(trait_def) }
195} 122}
196 123
197pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver {
198 let file_id = position.file_id;
199 let file = db.parse(file_id);
200 find_token_at_offset(file.syntax(), position.offset)
201 .find_map(|token| {
202 token.parent().ancestors().find_map(|node| {
203 if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() {
204 if let Some(func) = function_from_child_node(db, file_id, node) {
205 let scopes = func.scopes(db);
206 let scope = scopes.scope_for_offset(position.offset);
207 Some(expr::resolver_for_scope(func.body(db), db, scope))
208 } else {
209 // FIXME const/static/array length
210 None
211 }
212 } else {
213 try_get_resolver_for_node(db, file_id, node)
214 }
215 })
216 })
217 .unwrap_or_default()
218}
219
220pub fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver {
221 node.ancestors()
222 .find_map(|node| {
223 if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() {
224 if let Some(func) = function_from_child_node(db, file_id, node) {
225 let scopes = func.scopes(db);
226 let scope = scopes.scope_for(&node);
227 Some(expr::resolver_for_scope(func.body(db), db, scope))
228 } else {
229 // FIXME const/static/array length
230 None
231 }
232 } else {
233 try_get_resolver_for_node(db, file_id, node)
234 }
235 })
236 .unwrap_or_default()
237}
238
239fn try_get_resolver_for_node( 124fn try_get_resolver_for_node(
240 db: &impl HirDatabase, 125 db: &impl HirDatabase,
241 file_id: FileId, 126 file_id: FileId,
@@ -251,10 +136,281 @@ fn try_get_resolver_for_node(
251 } else if let Some(e) = ast::EnumDef::cast(node) { 136 } else if let Some(e) = ast::EnumDef::cast(node) {
252 let module = module_from_child_node(db, file_id, e.syntax())?; 137 let module = module_from_child_node(db, file_id, e.syntax())?;
253 Some(enum_from_module(db, module, e).resolver(db)) 138 Some(enum_from_module(db, module, e).resolver(db))
254 } else if let Some(f) = ast::FnDef::cast(node) { 139 } else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
255 function_from_source(db, file_id, f).map(|f| f.resolver(db)) 140 Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
256 } else { 141 } else {
257 // FIXME add missing cases 142 // FIXME add missing cases
258 None 143 None
259 } 144 }
260} 145}
146
147fn def_with_body_from_child_node(
148 db: &impl HirDatabase,
149 file_id: FileId,
150 node: &SyntaxNode,
151) -> Option<DefWithBody> {
152 let module = module_from_child_node(db, file_id, node)?;
153 let ctx = LocationCtx::new(db, module, file_id.into());
154 node.ancestors().find_map(|node| {
155 if let Some(def) = ast::FnDef::cast(node) {
156 return Some(Function { id: ctx.to_def(def) }.into());
157 }
158 if let Some(def) = ast::ConstDef::cast(node) {
159 return Some(Const { id: ctx.to_def(def) }.into());
160 }
161 if let Some(def) = ast::StaticDef::cast(node) {
162 return Some(Static { id: ctx.to_def(def) }.into());
163 }
164 None
165 })
166}
167
168/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
169/// original source files. It should not be used inside the HIR itself.
170#[derive(Debug)]
171pub struct SourceAnalyzer {
172 resolver: Resolver,
173 body_source_map: Option<Arc<BodySourceMap>>,
174 infer: Option<Arc<crate::ty::InferenceResult>>,
175 scopes: Option<Arc<crate::expr::ExprScopes>>,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub enum PathResolution {
180 /// An item
181 Def(crate::ModuleDef),
182 /// A local binding (only value namespace)
183 LocalBinding(Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>),
184 /// A generic parameter
185 GenericParam(u32),
186 SelfType(crate::ImplBlock),
187 AssocItem(crate::ImplItem),
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
191pub struct ScopeEntryWithSyntax {
192 pub(crate) name: Name,
193 pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
194}
195
196impl ScopeEntryWithSyntax {
197 pub fn name(&self) -> &Name {
198 &self.name
199 }
200
201 pub fn ptr(&self) -> Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>> {
202 self.ptr
203 }
204}
205
206#[derive(Debug)]
207pub struct ReferenceDescriptor {
208 pub range: TextRange,
209 pub name: String,
210}
211
212impl SourceAnalyzer {
213 pub fn new(
214 db: &impl HirDatabase,
215 file_id: FileId,
216 node: &SyntaxNode,
217 offset: Option<TextUnit>,
218 ) -> SourceAnalyzer {
219 let def_with_body = def_with_body_from_child_node(db, file_id, node);
220 if let Some(def) = def_with_body {
221 let source_map = def.body_source_map(db);
222 let scopes = db.expr_scopes(def);
223 let scope = match offset {
224 None => scope_for(&scopes, &source_map, &node),
225 Some(offset) => scope_for_offset(&scopes, &source_map, offset),
226 };
227 let resolver = expr::resolver_for_scope(def.body(db), db, scope);
228 SourceAnalyzer {
229 resolver,
230 body_source_map: Some(source_map),
231 infer: Some(def.infer(db)),
232 scopes: Some(scopes),
233 }
234 } else {
235 SourceAnalyzer {
236 resolver: node
237 .ancestors()
238 .find_map(|node| try_get_resolver_for_node(db, file_id, node))
239 .unwrap_or_default(),
240 body_source_map: None,
241 infer: None,
242 scopes: None,
243 }
244 }
245 }
246
247 pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option<crate::Ty> {
248 let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?;
249 Some(self.infer.as_ref()?[expr_id].clone())
250 }
251
252 pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option<crate::Ty> {
253 let pat_id = self.body_source_map.as_ref()?.node_pat(pat)?;
254 Some(self.infer.as_ref()?[pat_id].clone())
255 }
256
257 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
258 let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?;
259 self.infer.as_ref()?.method_resolution(expr_id)
260 }
261
262 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
263 let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?;
264 self.infer.as_ref()?.field_resolution(expr_id)
265 }
266
267 pub fn resolve_hir_path(
268 &self,
269 db: &impl HirDatabase,
270 path: &crate::Path,
271 ) -> PerNs<crate::Resolution> {
272 self.resolver.resolve_path(db, path)
273 }
274
275 pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
276 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
277 let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?;
278 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
279 return Some(PathResolution::AssocItem(assoc));
280 }
281 }
282 if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
283 let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?;
284 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
285 return Some(PathResolution::AssocItem(assoc));
286 }
287 }
288 let hir_path = crate::Path::from_ast(path)?;
289 let res = self.resolver.resolve_path(db, &hir_path);
290 let res = res.clone().take_types().or_else(|| res.take_values())?;
291 let res = match res {
292 crate::Resolution::Def(it) => PathResolution::Def(it),
293 crate::Resolution::LocalBinding(it) => {
294 PathResolution::LocalBinding(self.body_source_map.as_ref()?.pat_syntax(it)?)
295 }
296 crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it),
297 crate::Resolution::SelfType(it) => PathResolution::SelfType(it),
298 };
299 Some(res)
300 }
301
302 pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option<ScopeEntryWithSyntax> {
303 let mut shadowed = FxHashSet::default();
304 let name = name_ref.as_name();
305 let source_map = self.body_source_map.as_ref()?;
306 let scopes = self.scopes.as_ref()?;
307 let scope = scope_for(scopes, source_map, name_ref.syntax());
308 let ret = scopes
309 .scope_chain(scope)
310 .flat_map(|scope| scopes.entries(scope).iter())
311 .filter(|entry| shadowed.insert(entry.name()))
312 .filter(|entry| entry.name() == &name)
313 .nth(0);
314 ret.and_then(|entry| {
315 Some(ScopeEntryWithSyntax {
316 name: entry.name().clone(),
317 ptr: source_map.pat_syntax(entry.pat())?,
318 })
319 })
320 }
321
322 pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<crate::Resolution>> {
323 self.resolver.all_names(db)
324 }
325
326 pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec<ReferenceDescriptor> {
327 // FIXME: at least, this should work with any DefWithBody, but ideally
328 // this should be hir-based altogether
329 let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
330 let ptr = Either::A(AstPtr::new(pat.into()));
331 fn_def
332 .syntax()
333 .descendants()
334 .filter_map(ast::NameRef::cast)
335 .filter(|name_ref| match self.resolve_local_name(*name_ref) {
336 None => false,
337 Some(entry) => entry.ptr() == ptr,
338 })
339 .map(|name_ref| ReferenceDescriptor {
340 name: name_ref.text().to_string(),
341 range: name_ref.syntax().range(),
342 })
343 .collect()
344 }
345
346 #[cfg(test)]
347 pub(crate) fn body_source_map(&self) -> Arc<BodySourceMap> {
348 self.body_source_map.clone().unwrap()
349 }
350
351 #[cfg(test)]
352 pub(crate) fn inference_result(&self) -> Arc<crate::ty::InferenceResult> {
353 self.infer.clone().unwrap()
354 }
355
356 #[cfg(test)]
357 pub(crate) fn scopes(&self) -> Arc<ExprScopes> {
358 self.scopes.clone().unwrap()
359 }
360}
361
362fn scope_for(
363 scopes: &ExprScopes,
364 source_map: &BodySourceMap,
365 node: &SyntaxNode,
366) -> Option<ScopeId> {
367 node.ancestors()
368 .map(SyntaxNodePtr::new)
369 .filter_map(|ptr| source_map.syntax_expr(ptr))
370 .find_map(|it| scopes.scope_for(it))
371}
372
373fn scope_for_offset(
374 scopes: &ExprScopes,
375 source_map: &BodySourceMap,
376 offset: TextUnit,
377) -> Option<ScopeId> {
378 scopes
379 .scope_by_expr()
380 .iter()
381 .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope)))
382 // find containing scope
383 .min_by_key(|(ptr, _scope)| {
384 (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len())
385 })
386 .map(|(ptr, scope)| adjust(scopes, source_map, ptr, offset).unwrap_or(*scope))
387}
388
389// XXX: during completion, cursor might be outside of any particular
390// expression. Try to figure out the correct scope...
391fn adjust(
392 scopes: &ExprScopes,
393 source_map: &BodySourceMap,
394 ptr: SyntaxNodePtr,
395 offset: TextUnit,
396) -> Option<ScopeId> {
397 let r = ptr.range();
398 let child_scopes = scopes
399 .scope_by_expr()
400 .iter()
401 .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope)))
402 .map(|(ptr, scope)| (ptr.range(), scope))
403 .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
404
405 child_scopes
406 .max_by(|(r1, _), (r2, _)| {
407 if r2.is_subrange(&r1) {
408 std::cmp::Ordering::Greater
409 } else if r1.is_subrange(&r2) {
410 std::cmp::Ordering::Less
411 } else {
412 r1.start().cmp(&r2.start())
413 }
414 })
415 .map(|(_ptr, scope)| *scope)
416}
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 20e55d92d..ecf13fbc3 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -15,10 +15,11 @@ use std::sync::Arc;
15use std::{fmt, mem}; 15use std::{fmt, mem};
16 16
17use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; 17use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait};
18use display::{HirDisplay, HirFormatter};
18 19
19pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; 20pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig};
20pub(crate) use infer::{infer, InferenceResult, InferTy}; 21pub(crate) use infer::{infer, InferenceResult, InferTy};
21use display::{HirDisplay, HirFormatter}; 22pub use lower::CallableDef;
22 23
23/// A type constructor or type name: this might be something like the primitive 24/// A type constructor or type name: this might be something like the primitive
24/// type `bool`, a struct like `Vec`, or things like function pointers or 25/// type `bool`, a struct like `Vec`, or things like function pointers or
@@ -288,6 +289,15 @@ impl Ty {
288 } 289 }
289 } 290 }
290 291
292 pub fn as_callable(&self) -> Option<(CallableDef, &Substs)> {
293 match self {
294 Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(callable_def), parameters }) => {
295 Some((*callable_def, parameters))
296 }
297 _ => None,
298 }
299 }
300
291 fn builtin_deref(&self) -> Option<Ty> { 301 fn builtin_deref(&self) -> Option<Ty> {
292 match self { 302 match self {
293 Ty::Apply(a_ty) => match a_ty.ctor { 303 Ty::Apply(a_ty) => match a_ty.ctor {
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index 3ac8dc46b..bb23246a6 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -135,7 +135,7 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> {
135impl Ty { 135impl Ty {
136 /// Look up the method with the given name, returning the actual autoderefed 136 /// Look up the method with the given name, returning the actual autoderefed
137 /// receiver type (but without autoref applied yet). 137 /// receiver type (but without autoref applied yet).
138 pub fn lookup_method( 138 pub(crate) fn lookup_method(
139 self, 139 self,
140 db: &impl HirDatabase, 140 db: &impl HirDatabase,
141 name: &Name, 141 name: &Name,
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index ecc63f376..d7c2ca132 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -4,15 +4,15 @@ use std::fmt::Write;
4use insta::assert_snapshot_matches; 4use insta::assert_snapshot_matches;
5 5
6use ra_db::{SourceDatabase, salsa::Database, FilePosition}; 6use ra_db::{SourceDatabase, salsa::Database, FilePosition};
7use ra_syntax::{algo, ast::{self, AstNode}}; 7use ra_syntax::{algo, ast::{self, AstNode}, SyntaxKind::*};
8use test_utils::covers; 8use test_utils::covers;
9 9
10use crate::{ 10use crate::{
11 source_binder,
12 mock::MockDatabase, 11 mock::MockDatabase,
13 ty::display::HirDisplay, 12 ty::display::HirDisplay,
14 ty::InferenceResult, 13 ty::InferenceResult,
15 expr::BodySourceMap 14 expr::BodySourceMap,
15 SourceAnalyzer,
16}; 16};
17 17
18// These tests compare the inference results for all expressions in a file 18// These tests compare the inference results for all expressions in a file
@@ -1862,14 +1862,14 @@ fn test() {
1862 @r###" 1862 @r###"
1863[49; 50) '0': u32 1863[49; 50) '0': u32
1864[80; 83) '101': u32 1864[80; 83) '101': u32
1865[126; 128) '99': u32
1866[95; 213) '{ ...NST; }': () 1865[95; 213) '{ ...NST; }': ()
1867[138; 139) 'x': {unknown} 1866[138; 139) 'x': {unknown}
1868[142; 153) 'LOCAL_CONST': {unknown} 1867[142; 153) 'LOCAL_CONST': {unknown}
1869[163; 164) 'z': u32 1868[163; 164) 'z': u32
1870[167; 179) 'GLOBAL_CONST': u32 1869[167; 179) 'GLOBAL_CONST': u32
1871[189; 191) 'id': u32 1870[189; 191) 'id': u32
1872[194; 210) 'Foo::A..._CONST': u32"### 1871[194; 210) 'Foo::A..._CONST': u32
1872[126; 128) '99': u32"###
1873 ); 1873 );
1874} 1874}
1875 1875
@@ -1891,8 +1891,6 @@ fn test() {
1891 @r###" 1891 @r###"
1892[29; 32) '101': u32 1892[29; 32) '101': u32
1893[70; 73) '101': u32 1893[70; 73) '101': u32
1894[118; 120) '99': u32
1895[161; 163) '99': u32
1896[85; 280) '{ ...MUT; }': () 1894[85; 280) '{ ...MUT; }': ()
1897[173; 174) 'x': {unknown} 1895[173; 174) 'x': {unknown}
1898[177; 189) 'LOCAL_STATIC': {unknown} 1896[177; 189) 'LOCAL_STATIC': {unknown}
@@ -1901,7 +1899,9 @@ fn test() {
1901[229; 230) 'z': u32 1899[229; 230) 'z': u32
1902[233; 246) 'GLOBAL_STATIC': u32 1900[233; 246) 'GLOBAL_STATIC': u32
1903[256; 257) 'w': u32 1901[256; 257) 'w': u32
1904[260; 277) 'GLOBAL...IC_MUT': u32"### 1902[260; 277) 'GLOBAL...IC_MUT': u32
1903[118; 120) '99': u32
1904[161; 163) '99': u32"###
1905 ); 1905 );
1906} 1906}
1907 1907
@@ -2302,13 +2302,10 @@ fn test() -> u64 {
2302} 2302}
2303 2303
2304fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 2304fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
2305 let func = source_binder::function_from_position(db, pos).unwrap(); 2305 let file = db.parse(pos.file_id);
2306 let body_source_map = func.body_source_map(db); 2306 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
2307 let inference_result = func.infer(db); 2307 let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
2308 let (_, syntax) = func.source(db); 2308 let ty = analyzer.type_of(db, expr).unwrap();
2309 let node = algo::find_node_at_offset::<ast::Expr>(syntax.syntax(), pos.offset).unwrap();
2310 let expr = body_source_map.node_expr(node).unwrap();
2311 let ty = &inference_result[expr];
2312 ty.display(db).to_string() 2309 ty.display(db).to_string()
2313} 2310}
2314 2311
@@ -2350,25 +2347,11 @@ fn infer(content: &str) -> String {
2350 } 2347 }
2351 }; 2348 };
2352 2349
2353 for const_def in source_file.syntax().descendants().filter_map(ast::ConstDef::cast) { 2350 for node in source_file.syntax().descendants() {
2354 let konst = source_binder::const_from_source(&db, file_id, const_def).unwrap(); 2351 if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
2355 let inference_result = konst.infer(&db); 2352 let analyzer = SourceAnalyzer::new(&db, file_id, node, None);
2356 let body_source_map = konst.body_source_map(&db); 2353 infer_def(analyzer.inference_result(), analyzer.body_source_map());
2357 infer_def(inference_result, body_source_map) 2354 }
2358 }
2359
2360 for static_def in source_file.syntax().descendants().filter_map(ast::StaticDef::cast) {
2361 let static_ = source_binder::static_from_source(&db, file_id, static_def).unwrap();
2362 let inference_result = static_.infer(&db);
2363 let body_source_map = static_.body_source_map(&db);
2364 infer_def(inference_result, body_source_map)
2365 }
2366
2367 for fn_def in source_file.syntax().descendants().filter_map(ast::FnDef::cast) {
2368 let func = source_binder::function_from_source(&db, file_id, fn_def).unwrap();
2369 let inference_result = func.infer(&db);
2370 let body_source_map = func.body_source_map(&db);
2371 infer_def(inference_result, body_source_map)
2372 } 2355 }
2373 2356
2374 acc.truncate(acc.trim_end().len()); 2357 acc.truncate(acc.trim_end().len());
@@ -2403,10 +2386,12 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
2403 } 2386 }
2404 ", 2387 ",
2405 ); 2388 );
2406 let func = source_binder::function_from_position(&db, pos).unwrap();
2407 { 2389 {
2390 let file = db.parse(pos.file_id);
2391 let node =
2392 algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
2408 let events = db.log_executed(|| { 2393 let events = db.log_executed(|| {
2409 func.infer(&db); 2394 SourceAnalyzer::new(&db, pos.file_id, node, None);
2410 }); 2395 });
2411 assert!(format!("{:?}", events).contains("infer")) 2396 assert!(format!("{:?}", events).contains("infer"))
2412 } 2397 }
@@ -2423,8 +2408,11 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
2423 db.query_mut(ra_db::FileTextQuery).set(pos.file_id, Arc::new(new_text)); 2408 db.query_mut(ra_db::FileTextQuery).set(pos.file_id, Arc::new(new_text));
2424 2409
2425 { 2410 {
2411 let file = db.parse(pos.file_id);
2412 let node =
2413 algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
2426 let events = db.log_executed(|| { 2414 let events = db.log_executed(|| {
2427 func.infer(&db); 2415 SourceAnalyzer::new(&db, pos.file_id, node, None);
2428 }); 2416 });
2429 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) 2417 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
2430 } 2418 }