aboutsummaryrefslogtreecommitdiff
path: root/crates
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
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')
-rw-r--r--crates/ra_assists/src/add_explicit_type.rs8
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs15
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs10
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs12
-rw-r--r--crates/ra_assists/src/inline_local_variable.rs11
-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
-rw-r--r--crates/ra_ide_api/src/call_info.rs43
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs35
-rw-r--r--crates/ra_ide_api/src/completion/complete_path.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_ide_api/src/completion/complete_struct_literal.rs11
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs15
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs25
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs150
-rw-r--r--crates/ra_ide_api/src/hover.rs18
-rw-r--r--crates/ra_ide_api/src/references.rs20
-rw-r--r--crates/ra_ide_api/src/runnables.rs1
-rw-r--r--crates/ra_syntax/tests/test.rs2
28 files changed, 568 insertions, 636 deletions
diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs
index 1dc59bb87..cb0ac9885 100644
--- a/crates/ra_assists/src/add_explicit_type.rs
+++ b/crates/ra_assists/src/add_explicit_type.rs
@@ -1,7 +1,6 @@
1use hir::{ 1use hir::{
2 HirDisplay, Ty, 2 HirDisplay, Ty,
3 db::HirDatabase, 3 db::HirDatabase,
4 source_binder::function_from_child_node,
5}; 4};
6use ra_syntax::{ 5use ra_syntax::{
7 SyntaxKind, 6 SyntaxKind,
@@ -30,11 +29,8 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
30 } 29 }
31 // Infer type 30 // Infer type
32 let db = ctx.db; 31 let db = ctx.db;
33 let func = function_from_child_node(db, ctx.frange.file_id, pat.syntax())?; 32 let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
34 let inference_res = func.infer(db); 33 let ty = analyzer.type_of(db, expr)?;
35 let source_map = func.body_source_map(db);
36 let expr_id = source_map.node_expr(expr.into())?;
37 let ty = inference_res[expr_id].clone();
38 // Assist not applicable if the type is unknown 34 // Assist not applicable if the type is unknown
39 if is_unknown(&ty) { 35 if is_unknown(&ty) {
40 return None; 36 return None;
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 19a2d05bc..100ebb7b6 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -2,7 +2,6 @@ use std::fmt::Write;
2 2
3use crate::{Assist, AssistId, AssistCtx}; 3use crate::{Assist, AssistId, AssistCtx};
4 4
5use hir::Resolver;
6use hir::db::HirDatabase; 5use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; 6use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
8use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; 7use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner};
@@ -46,9 +45,9 @@ fn add_missing_impl_members_inner(
46 let trait_def = { 45 let trait_def = {
47 let file_id = ctx.frange.file_id; 46 let file_id = ctx.frange.file_id;
48 let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; 47 let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
49 let resolver = hir::source_binder::resolver_for_position(ctx.db, position); 48 let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
50 49
51 resolve_target_trait_def(ctx.db, &resolver, impl_node)? 50 resolve_target_trait_def(ctx.db, &analyzer, impl_node)?
52 }; 51 };
53 52
54 let missing_fns: Vec<_> = { 53 let missing_fns: Vec<_> = {
@@ -122,14 +121,14 @@ fn add_missing_impl_members_inner(
122/// implemented) to a `ast::TraitDef`. 121/// implemented) to a `ast::TraitDef`.
123fn resolve_target_trait_def( 122fn resolve_target_trait_def(
124 db: &impl HirDatabase, 123 db: &impl HirDatabase,
125 resolver: &Resolver, 124 analyzer: &hir::SourceAnalyzer,
126 impl_block: &ast::ImplBlock, 125 impl_block: &ast::ImplBlock,
127) -> Option<TreeArc<ast::TraitDef>> { 126) -> Option<TreeArc<ast::TraitDef>> {
128 let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?; 127 let ast_path =
129 let hir_path = ast_path.path().and_then(hir::Path::from_ast)?; 128 impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?;
130 129
131 match resolver.resolve_path(db, &hir_path).take_types() { 130 match analyzer.resolve_path(db, &ast_path) {
132 Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), 131 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
133 _ => None, 132 _ => None,
134 } 133 }
135} 134}
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index da67ab667..45e327cd4 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -1,7 +1,7 @@
1use std::fmt::Write; 1use std::fmt::Write;
2 2
3use hir::{ 3use hir::{
4 AdtDef, FieldSource, source_binder, 4 AdtDef, FieldSource,
5 db::HirDatabase, 5 db::HirDatabase,
6}; 6};
7use ra_syntax::ast::{self, AstNode}; 7use ra_syntax::ast::{self, AstNode};
@@ -20,12 +20,8 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
20 } 20 }
21 21
22 let expr = match_expr.expr()?; 22 let expr = match_expr.expr()?;
23 let function = 23 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
24 source_binder::function_from_child_node(ctx.db, ctx.frange.file_id, expr.syntax())?; 24 let match_expr_ty = analyzer.type_of(ctx.db, expr)?;
25 let infer_result = function.infer(ctx.db);
26 let source_map = function.body_source_map(ctx.db);
27 let node_expr = source_map.node_expr(expr)?;
28 let match_expr_ty = infer_result[node_expr].clone();
29 let enum_def = match_expr_ty.autoderef(ctx.db).find_map(|ty| match ty.as_adt() { 25 let enum_def = match_expr_ty.autoderef(ctx.db).find_map(|ty| match ty.as_adt() {
30 Some((AdtDef::Enum(e), _)) => Some(e), 26 Some((AdtDef::Enum(e), _)) => Some(e),
31 _ => None, 27 _ => None,
diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs
index c7790dc72..663b4f669 100644
--- a/crates/ra_assists/src/fill_struct_fields.rs
+++ b/crates/ra_assists/src/fill_struct_fields.rs
@@ -1,6 +1,6 @@
1use std::fmt::Write; 1use std::fmt::Write;
2 2
3use hir::{AdtDef, db::HirDatabase, source_binder::function_from_child_node}; 3use hir::{AdtDef, db::HirDatabase};
4 4
5use ra_syntax::ast::{self, AstNode}; 5use ra_syntax::ast::{self, AstNode};
6 6
@@ -51,15 +51,13 @@ where
51 } 51 }
52 52
53 fn evaluate_struct_def_fields(&mut self) -> Option<()> { 53 fn evaluate_struct_def_fields(&mut self) -> Option<()> {
54 let function = function_from_child_node( 54 let analyzer = hir::SourceAnalyzer::new(
55 self.ctx.db, 55 self.ctx.db,
56 self.ctx.frange.file_id, 56 self.ctx.frange.file_id,
57 self.struct_lit.syntax(), 57 self.struct_lit.syntax(),
58 )?; 58 None,
59 let infer_result = function.infer(self.ctx.db); 59 );
60 let source_map = function.body_source_map(self.ctx.db); 60 let struct_lit_ty = analyzer.type_of(self.ctx.db, self.struct_lit.into())?;
61 let node_expr = source_map.node_expr(self.struct_lit.into())?;
62 let struct_lit_ty = infer_result[node_expr].clone();
63 let struct_def = match struct_lit_ty.as_adt() { 61 let struct_def = match struct_lit_ty.as_adt() {
64 Some((AdtDef::Struct(s), _)) => s, 62 Some((AdtDef::Struct(s), _)) => s,
65 _ => return None, 63 _ => return None,
diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs
index 950c2910b..e0479ef13 100644
--- a/crates/ra_assists/src/inline_local_variable.rs
+++ b/crates/ra_assists/src/inline_local_variable.rs
@@ -1,7 +1,4 @@
1use hir::{ 1use hir::db::HirDatabase;
2 db::HirDatabase,
3 source_binder::function_from_child_node,
4};
5use ra_syntax::{ 2use ra_syntax::{
6 ast::{self, AstNode, AstToken, PatKind, ExprKind}, 3 ast::{self, AstNode, AstToken, PatKind, ExprKind},
7 TextRange, 4 TextRange,
@@ -29,10 +26,8 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
29 } else { 26 } else {
30 let_stmt.syntax().range() 27 let_stmt.syntax().range()
31 }; 28 };
32 29 let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
33 let function = function_from_child_node(ctx.db, ctx.frange.file_id, bind_pat.syntax())?; 30 let refs = analyzer.find_all_refs(bind_pat);
34 let scope = function.scopes(ctx.db);
35 let refs = scope.find_all_refs(bind_pat);
36 31
37 let mut wrap_in_parens = vec![true; refs.len()]; 32 let mut wrap_in_parens = vec![true; refs.len()];
38 33
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 }
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs
index dbb3853d0..4413aec73 100644
--- a/crates/ra_ide_api/src/call_info.rs
+++ b/crates/ra_ide_api/src/call_info.rs
@@ -2,7 +2,6 @@ use test_utils::tested_by;
2use ra_db::SourceDatabase; 2use ra_db::SourceDatabase;
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SyntaxNode, TextUnit, 4 AstNode, SyntaxNode, TextUnit,
5 SyntaxKind::FN_DEF,
6 ast::{self, ArgListOwner}, 5 ast::{self, ArgListOwner},
7 algo::find_node_at_offset, 6 algo::find_node_at_offset,
8}; 7};
@@ -18,19 +17,26 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
18 let calling_node = FnCallNode::with_node(syntax, position.offset)?; 17 let calling_node = FnCallNode::with_node(syntax, position.offset)?;
19 let name_ref = calling_node.name_ref()?; 18 let name_ref = calling_node.name_ref()?;
20 19
21 // Resolve the function's NameRef (NOTE: this isn't entirely accurate). 20 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
22 let file_symbols = crate::symbol_index::index_resolve(db, name_ref); 21 let function = match calling_node {
23 let symbol = file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF)?; 22 FnCallNode::CallExpr(expr) => {
24 let fn_file = db.parse(symbol.file_id); 23 //FIXME: apply subst
25 let fn_def = symbol.ptr.to_node(&fn_file); 24 let (callable_def, _subst) =
26 let fn_def = ast::FnDef::cast(fn_def).unwrap(); 25 analyzer.type_of(db, expr.expr()?.into())?.as_callable()?;
27 let function = hir::source_binder::function_from_source(db, symbol.file_id, fn_def)?; 26 match callable_def {
27 hir::CallableDef::Function(it) => it,
28 //FIXME: handle other callables
29 _ => return None,
30 }
31 }
32 FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(expr)?,
33 };
28 34
29 let mut call_info = CallInfo::new(db, function); 35 let mut call_info = CallInfo::new(db, function);
30 36
31 // If we have a calling expression let's find which argument we are on 37 // If we have a calling expression let's find which argument we are on
32 let num_params = call_info.parameters().len(); 38 let num_params = call_info.parameters().len();
33 let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); 39 let has_self = function.signature(db).has_self_param();
34 40
35 if num_params == 1 { 41 if num_params == 1 {
36 if !has_self { 42 if !has_self {
@@ -74,7 +80,7 @@ enum FnCallNode<'a> {
74} 80}
75 81
76impl<'a> FnCallNode<'a> { 82impl<'a> FnCallNode<'a> {
77 pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> { 83 fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
78 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { 84 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
79 return Some(FnCallNode::CallExpr(expr)); 85 return Some(FnCallNode::CallExpr(expr));
80 } 86 }
@@ -84,7 +90,7 @@ impl<'a> FnCallNode<'a> {
84 None 90 None
85 } 91 }
86 92
87 pub fn name_ref(&self) -> Option<&'a ast::NameRef> { 93 fn name_ref(&self) -> Option<&'a ast::NameRef> {
88 match *self { 94 match *self {
89 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { 95 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
90 ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 96 ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
@@ -97,7 +103,7 @@ impl<'a> FnCallNode<'a> {
97 } 103 }
98 } 104 }
99 105
100 pub fn arg_list(&self) -> Option<&'a ast::ArgList> { 106 fn arg_list(&self) -> Option<&'a ast::ArgList> {
101 match *self { 107 match *self {
102 FnCallNode::CallExpr(expr) => expr.arg_list(), 108 FnCallNode::CallExpr(expr) => expr.arg_list(),
103 FnCallNode::MethodCallExpr(expr) => expr.arg_list(), 109 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
@@ -142,7 +148,7 @@ mod tests {
142 } 148 }
143 149
144 #[test] 150 #[test]
145 fn test_fn_signature_two_args_first() { 151 fn test_fn_signature_two_args_firstx() {
146 let info = call_info( 152 let info = call_info(
147 r#"fn foo(x: u32, y: u32) -> u32 {x + y} 153 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
148fn bar() { foo(<|>3, ); }"#, 154fn bar() { foo(<|>3, ); }"#,
@@ -382,11 +388,9 @@ assert_eq!(6, my_crate::add_one(5));
382 fn test_fn_signature_with_docs_from_actix() { 388 fn test_fn_signature_with_docs_from_actix() {
383 let info = call_info( 389 let info = call_info(
384 r#" 390 r#"
385pub trait WriteHandler<E> 391struct WriteHandler<E>;
386where 392
387 Self: Actor, 393impl<E> WriteHandler<E> {
388 Self::Context: ActorContext,
389{
390 /// Method is called when writer emits error. 394 /// Method is called when writer emits error.
391 /// 395 ///
392 /// If this method returns `ErrorAction::Continue` writer processing 396 /// If this method returns `ErrorAction::Continue` writer processing
@@ -403,8 +407,7 @@ where
403 } 407 }
404} 408}
405 409
406pub fn foo() { 410pub fn foo(mut r: WriteHandler<()>) {
407 WriteHandler r;
408 r.finished(<|>); 411 r.finished(<|>);
409} 412}
410 413
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs
index c093d5cfb..4a111aba5 100644
--- a/crates/ra_ide_api/src/completion/complete_dot.rs
+++ b/crates/ra_ide_api/src/completion/complete_dot.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete dot accesses, i.e. fields or methods (currently only fields). 5/// Complete dot accesses, i.e. fields or methods (currently only fields).
6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { 7 let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
8 (Some(function), Some(receiver)) => (function, receiver), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(receiver) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let receiver_ty = infer_result[expr].clone();
18 if !ctx.is_call { 11 if !ctx.is_call {
19 complete_fields(acc, ctx, receiver_ty.clone()); 12 complete_fields(acc, ctx, receiver_ty.clone());
20 } 13 }
@@ -315,4 +308,28 @@ mod tests {
315]"### 308]"###
316 ); 309 );
317 } 310 }
311
312 #[test]
313 fn test_completion_works_in_consts() {
314 assert_debug_snapshot_matches!(
315 do_ref_completion(
316 r"
317 struct A { the_field: u32 }
318 const X: u32 = {
319 A { the_field: 92 }.<|>
320 };
321 ",
322 ),
323 @r###"[
324 CompletionItem {
325 label: "the_field",
326 source_range: [106; 106),
327 delete: [106; 106),
328 insert: "the_field",
329 kind: Field,
330 detail: "u32"
331 }
332]"###
333 );
334 }
318} 335}
diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs
index 7e47fa6bd..bc03a7095 100644
--- a/crates/ra_ide_api/src/completion/complete_path.rs
+++ b/crates/ra_ide_api/src/completion/complete_path.rs
@@ -9,7 +9,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
9 Some(path) => path.clone(), 9 Some(path) => path.clone(),
10 _ => return, 10 _ => return,
11 }; 11 };
12 let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { 12 let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path).take_types() {
13 Some(Resolution::Def(def)) => def, 13 Some(Resolution::Def(def)) => def,
14 _ => return, 14 _ => return,
15 }; 15 };
diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs
index 7abcd019b..0ef248687 100644
--- a/crates/ra_ide_api/src/completion/complete_pattern.rs
+++ b/crates/ra_ide_api/src/completion/complete_pattern.rs
@@ -7,7 +7,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 } 7 }
8 // FIXME: ideally, we should look at the type we are matching against and 8 // FIXME: ideally, we should look at the type we are matching against and
9 // suggest variants + auto-imports 9 // suggest variants + auto-imports
10 let names = ctx.resolver.all_names(ctx.db); 10 let names = ctx.analyzer.all_names(ctx.db);
11 for (name, res) in names.into_iter() { 11 for (name, res) in names.into_iter() {
12 let r = res.as_ref(); 12 let r = res.as_ref();
13 let def = match r.take_types().or(r.take_values()) { 13 let def = match r.take_types().or(r.take_values()) {
diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs
index 9d82f2270..fd256fc3b 100644
--- a/crates/ra_ide_api/src/completion/complete_scope.rs
+++ b/crates/ra_ide_api/src/completion/complete_scope.rs
@@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
4 if !ctx.is_trivial_path { 4 if !ctx.is_trivial_path {
5 return; 5 return;
6 } 6 }
7 let names = ctx.resolver.all_names(ctx.db); 7 let names = ctx.analyzer.all_names(ctx.db);
8 8
9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); 9 names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
10} 10}
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
index f58bcd03e..48fbf67f7 100644
--- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs
+++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete fields in fields literals. 5/// Complete fields in fields literals.
6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, struct_lit) = match (&ctx.function, ctx.struct_lit_syntax) { 7 let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) {
8 (Some(function), Some(struct_lit)) => (function, struct_lit), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(struct_lit.into()) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let ty = infer_result[expr].clone();
18 let (adt, substs) = match ty.as_adt() { 11 let (adt, substs) = match ty.as_adt() {
19 Some(res) => res, 12 Some(res) => res,
20 _ => return, 13 _ => return,
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 65dffa470..359f2cffa 100644
--- a/crates/ra_ide_api/src/completion/completion_context.rs
+++ b/crates/ra_ide_api/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, 5 algo::{find_token_at_offset, find_covering_element, find_node_at_offset},
6 SyntaxKind::*, 6 SyntaxKind::*,
7}; 7};
8use hir::{source_binder, Resolver}; 8use hir::source_binder;
9 9
10use crate::{db, FilePosition}; 10use crate::{db, FilePosition};
11 11
@@ -14,11 +14,10 @@ use crate::{db, FilePosition};
14#[derive(Debug)] 14#[derive(Debug)]
15pub(crate) struct CompletionContext<'a> { 15pub(crate) struct CompletionContext<'a> {
16 pub(super) db: &'a db::RootDatabase, 16 pub(super) db: &'a db::RootDatabase,
17 pub(super) analyzer: hir::SourceAnalyzer,
17 pub(super) offset: TextUnit, 18 pub(super) offset: TextUnit,
18 pub(super) token: SyntaxToken<'a>, 19 pub(super) token: SyntaxToken<'a>,
19 pub(super) resolver: Resolver,
20 pub(super) module: Option<hir::Module>, 20 pub(super) module: Option<hir::Module>,
21 pub(super) function: Option<hir::Function>,
22 pub(super) function_syntax: Option<&'a ast::FnDef>, 21 pub(super) function_syntax: Option<&'a ast::FnDef>,
23 pub(super) use_item_syntax: Option<&'a ast::UseItem>, 22 pub(super) use_item_syntax: Option<&'a ast::UseItem>,
24 pub(super) struct_lit_syntax: Option<&'a ast::StructLit>, 23 pub(super) struct_lit_syntax: Option<&'a ast::StructLit>,
@@ -47,16 +46,16 @@ impl<'a> CompletionContext<'a> {
47 original_file: &'a SourceFile, 46 original_file: &'a SourceFile,
48 position: FilePosition, 47 position: FilePosition,
49 ) -> Option<CompletionContext<'a>> { 48 ) -> Option<CompletionContext<'a>> {
50 let resolver = source_binder::resolver_for_position(db, position);
51 let module = source_binder::module_from_position(db, position); 49 let module = source_binder::module_from_position(db, position);
52 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; 50 let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?;
51 let analyzer =
52 hir::SourceAnalyzer::new(db, position.file_id, token.parent(), Some(position.offset));
53 let mut ctx = CompletionContext { 53 let mut ctx = CompletionContext {
54 db, 54 db,
55 analyzer,
55 token, 56 token,
56 offset: position.offset, 57 offset: position.offset,
57 resolver,
58 module, 58 module,
59 function: None,
60 function_syntax: None, 59 function_syntax: None,
61 use_item_syntax: None, 60 use_item_syntax: None,
62 struct_lit_syntax: None, 61 struct_lit_syntax: None,
@@ -147,10 +146,6 @@ impl<'a> CompletionContext<'a> {
147 .ancestors() 146 .ancestors()
148 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 147 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
149 .find_map(ast::FnDef::cast); 148 .find_map(ast::FnDef::cast);
150 if let (Some(module), Some(fn_def)) = (self.module, self.function_syntax) {
151 let function = source_binder::function_from_module(self.db, module, fn_def);
152 self.function = Some(function);
153 }
154 149
155 let parent = match name_ref.syntax().parent() { 150 let parent = match name_ref.syntax().parent() {
156 Some(it) => it, 151 Some(it) => it,
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 3c518faf5..84645287d 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -1,11 +1,11 @@
1use ra_db::{FileId, SourceDatabase}; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, 3 SyntaxNode, AstNode, SmolStr, TextRange, TreeArc, AstPtr,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor}, 6 algo::visit::{visitor, Visitor},
7}; 7};
8use hir::{ModuleSource, FieldSource, Name, ImplItem}; 8use hir::{ModuleSource, FieldSource, ImplItem, Either};
9 9
10use crate::{FileSymbol, db::RootDatabase}; 10use crate::{FileSymbol, db::RootDatabase};
11 11
@@ -74,15 +74,25 @@ impl NavigationTarget {
74 } 74 }
75 } 75 }
76 76
77 pub(crate) fn from_scope_entry( 77 pub(crate) fn from_pat(
78 db: &RootDatabase,
78 file_id: FileId, 79 file_id: FileId,
79 name: Name, 80 pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
80 ptr: SyntaxNodePtr,
81 ) -> NavigationTarget { 81 ) -> NavigationTarget {
82 let file = db.parse(file_id);
83 let (name, full_range) = match pat {
84 Either::A(pat) => match pat.to_node(&file).kind() {
85 ast::PatKind::BindPat(pat) => {
86 return NavigationTarget::from_bind_pat(file_id, &pat)
87 }
88 _ => ("_".into(), pat.syntax_node_ptr().range()),
89 },
90 Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()),
91 };
82 NavigationTarget { 92 NavigationTarget {
83 file_id, 93 file_id,
84 name: name.to_string().into(), 94 name,
85 full_range: ptr.range(), 95 full_range,
86 focus_range: None, 96 focus_range: None,
87 kind: NAME, 97 kind: NAME,
88 container_name: None, 98 container_name: None,
@@ -229,6 +239,7 @@ impl NavigationTarget {
229 239
230 /// Allows `NavigationTarget` to be created from a `NameOwner` 240 /// Allows `NavigationTarget` to be created from a `NameOwner`
231 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { 241 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget {
242 //FIXME: use `_` instead of empty string
232 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 243 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
233 let focus_range = node.name().map(|it| it.syntax().range()); 244 let focus_range = node.name().map(|it| it.syntax().range());
234 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) 245 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax())
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 60c1f5085..517dffbca 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -5,7 +5,6 @@ use ra_syntax::{
5 SyntaxNode, 5 SyntaxNode,
6}; 6};
7use test_utils::tested_by; 7use test_utils::tested_by;
8use hir::Resolution;
9 8
10use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; 9use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
11 10
@@ -48,127 +47,70 @@ pub(crate) fn reference_definition(
48) -> ReferenceResult { 47) -> ReferenceResult {
49 use self::ReferenceResult::*; 48 use self::ReferenceResult::*;
50 49
51 let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()); 50 let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
52
53 if let Some(function) = function {
54 // Check if it is a method
55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 let infer_result = function.infer(db);
58 let source_map = function.body_source_map(db);
59 let expr = ast::Expr::cast(method_call.syntax()).unwrap();
60 if let Some(func) =
61 source_map.node_expr(expr).and_then(|it| infer_result.method_resolution(it))
62 {
63 return Exact(NavigationTarget::from_function(db, func));
64 };
65 }
66 // It could also be a field access
67 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
68 tested_by!(goto_definition_works_for_fields);
69 let infer_result = function.infer(db);
70 let source_map = function.body_source_map(db);
71 let expr = ast::Expr::cast(field_expr.syntax()).unwrap();
72 if let Some(field) =
73 source_map.node_expr(expr).and_then(|it| infer_result.field_resolution(it))
74 {
75 return Exact(NavigationTarget::from_field(db, field));
76 };
77 }
78 51
79 // It could also be a named field 52 // Special cases:
80 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
81 tested_by!(goto_definition_works_for_named_fields);
82 53
83 let infer_result = function.infer(db); 54 // Check if it is a method
84 let source_map = function.body_source_map(db); 55 if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
56 tested_by!(goto_definition_works_for_methods);
57 if let Some(func) = analyzer.resolve_method_call(method_call) {
58 return Exact(NavigationTarget::from_function(db, func));
59 }
60 }
61 // It could also be a field access
62 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
63 tested_by!(goto_definition_works_for_fields);
64 if let Some(field) = analyzer.resolve_field(field_expr) {
65 return Exact(NavigationTarget::from_field(db, field));
66 };
67 }
85 68
86 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); 69 // It could also be a named field
70 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
71 tested_by!(goto_definition_works_for_named_fields);
87 72
88 if let Some(expr) = struct_lit.and_then(|lit| source_map.node_expr(lit.into())) { 73 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
89 let ty = infer_result[expr].clone();
90 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
91 let hir_path = hir::Path::from_name_ref(name_ref);
92 let hir_name = hir_path.as_ident().unwrap();
93 74
94 if let Some(field) = s.field(db, hir_name) { 75 if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
95 return Exact(NavigationTarget::from_field(db, field)); 76 if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
96 } 77 let hir_path = hir::Path::from_name_ref(name_ref);
78 let hir_name = hir_path.as_ident().unwrap();
79
80 if let Some(field) = s.field(db, hir_name) {
81 return Exact(NavigationTarget::from_field(db, field));
97 } 82 }
98 } 83 }
99 } 84 }
100 } 85 }
101 86
102 // Try name resolution 87 // General case, a path or a local:
103 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); 88 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
104 if let Some(path) = 89 if let Some(resolved) = analyzer.resolve_path(db, path) {
105 name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) 90 match resolved {
106 { 91 hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)),
107 let resolved = resolver.resolve_path(db, &path); 92 hir::PathResolution::LocalBinding(pat) => {
108 match resolved.clone().take_types().or_else(|| resolved.take_values()) { 93 let nav = NavigationTarget::from_pat(db, file_id, pat);
109 Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), 94 return Exact(nav);
110 Some(Resolution::LocalBinding(pat)) => {
111 let body = resolver.body().expect("no body for local binding");
112 let source_map = body.owner().body_source_map(db);
113 let ptr = source_map.pat_syntax(pat).expect("pattern not found in syntax mapping");
114 let name =
115 path.as_ident().cloned().expect("local binding from a multi-segment path");
116 let ptr = ptr.either(|it| it.into(), |it| it.into());
117 let nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
118 return Exact(nav);
119 }
120 Some(Resolution::GenericParam(..)) => {
121 // FIXME: go to the generic param def
122 }
123 Some(Resolution::SelfType(impl_block)) => {
124 let ty = impl_block.target_ty(db);
125
126 if let Some((def_id, _)) = ty.as_adt() {
127 return Exact(NavigationTarget::from_adt_def(db, def_id));
128 } 95 }
129 } 96 hir::PathResolution::GenericParam(..) => {
130 None => { 97 // FIXME: go to the generic param def
131 // If we failed to resolve then check associated items 98 }
132 if let Some(function) = function { 99 hir::PathResolution::SelfType(impl_block) => {
133 // Resolve associated item for path expressions 100 let ty = impl_block.target_ty(db);
134 if let Some(path_expr) =
135 name_ref.syntax().ancestors().find_map(ast::PathExpr::cast)
136 {
137 let infer_result = function.infer(db);
138 let source_map = function.body_source_map(db);
139
140 if let Some(expr) = ast::Expr::cast(path_expr.syntax()) {
141 if let Some(res) = source_map
142 .node_expr(expr)
143 .and_then(|it| infer_result.assoc_resolutions_for_expr(it.into()))
144 {
145 return Exact(NavigationTarget::from_impl_item(db, res));
146 }
147 }
148 }
149 101
150 // Resolve associated item for path patterns 102 if let Some((def_id, _)) = ty.as_adt() {
151 if let Some(path_pat) = 103 return Exact(NavigationTarget::from_adt_def(db, def_id));
152 name_ref.syntax().ancestors().find_map(ast::PathPat::cast)
153 {
154 let infer_result = function.infer(db);
155 let source_map = function.body_source_map(db);
156
157 let pat: &ast::Pat = path_pat.into();
158
159 if let Some(res) = source_map
160 .node_pat(pat)
161 .and_then(|it| infer_result.assoc_resolutions_for_pat(it.into()))
162 {
163 return Exact(NavigationTarget::from_impl_item(db, res));
164 }
165 } 104 }
166 } 105 }
106 hir::PathResolution::AssocItem(assoc) => {
107 return Exact(NavigationTarget::from_impl_item(db, assoc))
108 }
167 } 109 }
168 } 110 }
169 } 111 }
170 112
171 // If that fails try the index based approach. 113 // Fallback index based approach:
172 let navs = crate::symbol_index::index_resolve(db, name_ref) 114 let navs = crate::symbol_index::index_resolve(db, name_ref)
173 .into_iter() 115 .into_iter()
174 .map(NavigationTarget::from_symbol) 116 .map(NavigationTarget::from_symbol)
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index 3a8c93b99..397b56786 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -132,17 +132,15 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
132 .ancestors() 132 .ancestors()
133 .take_while(|it| it.range() == leaf_node.range()) 133 .take_while(|it| it.range() == leaf_node.range())
134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; 134 .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?;
135 let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; 135 let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node, None);
136 let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; 136 let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) {
137 let infer = function.infer(db); 137 ty
138 let source_map = function.body_source_map(db); 138 } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
139 if let Some(expr) = ast::Expr::cast(node).and_then(|e| source_map.node_expr(e)) { 139 ty
140 Some(infer[expr].display(db).to_string())
141 } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| source_map.node_pat(p)) {
142 Some(infer[pat].display(db).to_string())
143 } else { 140 } else {
144 None 141 return None;
145 } 142 };
143 Some(ty.display(db).to_string())
146} 144}
147 145
148#[cfg(test)] 146#[cfg(test)]
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
index 3e30e047c..9f655d83c 100644
--- a/crates/ra_ide_api/src/references.rs
+++ b/crates/ra_ide_api/src/references.rs
@@ -61,11 +61,10 @@ pub(crate) fn find_all_refs(
61 position: FilePosition, 61 position: FilePosition,
62) -> Option<ReferenceSearchResult> { 62) -> Option<ReferenceSearchResult> {
63 let file = db.parse(position.file_id); 63 let file = db.parse(position.file_id);
64 let (binding, descr) = find_binding(db, &file, position)?; 64 let (binding, analyzer) = find_binding(db, &file, position)?;
65 let declaration = NavigationTarget::from_bind_pat(position.file_id, binding); 65 let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);
66 66
67 let references = descr 67 let references = analyzer
68 .scopes(db)
69 .find_all_refs(binding) 68 .find_all_refs(binding)
70 .into_iter() 69 .into_iter()
71 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) 70 .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
@@ -77,21 +76,18 @@ pub(crate) fn find_all_refs(
77 db: &RootDatabase, 76 db: &RootDatabase,
78 source_file: &'a SourceFile, 77 source_file: &'a SourceFile,
79 position: FilePosition, 78 position: FilePosition,
80 ) -> Option<(&'a ast::BindPat, hir::Function)> { 79 ) -> Option<(&'a ast::BindPat, hir::SourceAnalyzer)> {
81 let syntax = source_file.syntax(); 80 let syntax = source_file.syntax();
82 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { 81 if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
83 let descr = 82 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
84 source_binder::function_from_child_node(db, position.file_id, binding.syntax())?; 83 return Some((binding, analyzer));
85 return Some((binding, descr));
86 }; 84 };
87 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; 85 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
88 let descr = 86 let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
89 source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?; 87 let resolved = analyzer.resolve_local_name(name_ref)?;
90 let scope = descr.scopes(db);
91 let resolved = scope.resolve_local_name(name_ref)?;
92 if let Either::A(ptr) = resolved.ptr() { 88 if let Either::A(ptr) = resolved.ptr() {
93 if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file).kind() { 89 if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file).kind() {
94 return Some((binding, descr)); 90 return Some((binding, analyzer));
95 } 91 }
96 } 92 }
97 None 93 None
diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs
index 2395930f0..3969076a8 100644
--- a/crates/ra_ide_api/src/runnables.rs
+++ b/crates/ra_ide_api/src/runnables.rs
@@ -65,7 +65,6 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: &ast::Module) -> Opt
65 let range = module.syntax().range(); 65 let range = module.syntax().range();
66 let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?; 66 let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?;
67 67
68 // FIXME: thread cancellation instead of `.ok`ing
69 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); 68 let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::");
70 Some(Runnable { range, kind: RunnableKind::TestMod { path } }) 69 Some(Runnable { range, kind: RunnableKind::TestMod { path } })
71} 70}
diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs
index 276756c85..91799f8b5 100644
--- a/crates/ra_syntax/tests/test.rs
+++ b/crates/ra_syntax/tests/test.rs
@@ -60,7 +60,7 @@ fn reparse_fuzz_tests() {
60 } 60 }
61} 61}
62 62
63/// Test that Rust-analyzer can parse and validate the rust-analyser 63/// Test that Rust-analyzer can parse and validate the rust-analyzer
64/// FIXME: Use this as a benchmark 64/// FIXME: Use this as a benchmark
65#[test] 65#[test]
66fn self_hosting_parsing() { 66fn self_hosting_parsing() {