diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-13 12:17:48 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-13 12:17:48 +0100 |
commit | 8887782c4ab97d22f3d5c10e142407e4371c5c61 (patch) | |
tree | 80b60b2c0c2f6104b98e16648b95d99d9b1d3463 | |
parent | 34a05b7fea4add78446b2d93a64538982abacb9f (diff) | |
parent | 2facb5e061971afbf6bd2fabe3966d5de9d46489 (diff) |
Merge #1129
1129: introduce SourceAnalyzer API for ides r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
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 @@ | |||
1 | use hir::{ | 1 | use hir::{ |
2 | HirDisplay, Ty, | 2 | HirDisplay, Ty, |
3 | db::HirDatabase, | 3 | db::HirDatabase, |
4 | source_binder::function_from_child_node, | ||
5 | }; | 4 | }; |
6 | use ra_syntax::{ | 5 | use 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 | ||
3 | use crate::{Assist, AssistId, AssistCtx}; | 3 | use crate::{Assist, AssistId, AssistCtx}; |
4 | 4 | ||
5 | use hir::Resolver; | ||
6 | use hir::db::HirDatabase; | 5 | use hir::db::HirDatabase; |
7 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; | 6 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; |
8 | use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; | 7 | use 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`. |
123 | fn resolve_target_trait_def( | 122 | fn 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 @@ | |||
1 | use std::fmt::Write; | 1 | use std::fmt::Write; |
2 | 2 | ||
3 | use hir::{ | 3 | use hir::{ |
4 | AdtDef, FieldSource, source_binder, | 4 | AdtDef, FieldSource, |
5 | db::HirDatabase, | 5 | db::HirDatabase, |
6 | }; | 6 | }; |
7 | use ra_syntax::ast::{self, AstNode}; | 7 | use 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 @@ | |||
1 | use std::fmt::Write; | 1 | use std::fmt::Write; |
2 | 2 | ||
3 | use hir::{AdtDef, db::HirDatabase, source_binder::function_from_child_node}; | 3 | use hir::{AdtDef, db::HirDatabase}; |
4 | 4 | ||
5 | use ra_syntax::ast::{self, AstNode}; | 5 | use 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 @@ | |||
1 | use hir::{ | 1 | use hir::db::HirDatabase; |
2 | db::HirDatabase, | ||
3 | source_binder::function_from_child_node, | ||
4 | }; | ||
5 | use ra_syntax::{ | 2 | use 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}; | |||
4 | use ra_syntax::{ast::self, TreeArc}; | 4 | use ra_syntax::{ast::self, TreeArc}; |
5 | 5 | ||
6 | use crate::{ | 6 | use 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 | ||
687 | impl Docs for Static { | 667 | impl 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 | }; |
17 | use crate::{ path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; | 17 | use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; |
18 | 18 | ||
19 | pub use self::scope::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}; | 19 | pub use self::scope::ExprScopes; |
20 | 20 | ||
21 | pub(crate) mod scope; | 21 | pub(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? |
84 | pub fn resolver_for_expr(body: Arc<Body>, db: &impl HirDatabase, expr_id: ExprId) -> Resolver { | 84 | pub(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 | ||
89 | pub fn resolver_for_scope( | 93 | pub(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 | ||
119 | impl BodySourceMap { | 123 | impl 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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::FxHashMap; |
4 | |||
5 | use ra_syntax::{ | ||
6 | AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr, | ||
7 | algo::generate, | ||
8 | ast, | ||
9 | }; | ||
10 | use ra_arena::{Arena, RawId, impl_arena_id}; | 4 | use ra_arena::{Arena, RawId, impl_arena_id}; |
11 | 5 | ||
12 | use crate::{ | 6 | use 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); | |||
23 | pub struct ExprScopes { | 17 | pub 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)] |
30 | pub struct ScopeEntry { | 24 | pub(crate) struct ScopeEntry { |
31 | name: Name, | 25 | name: Name, |
32 | pat: PatId, | 26 | pat: PatId, |
33 | } | 27 | } |
34 | 28 | ||
29 | impl 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)] |
36 | pub struct ScopeData { | 40 | pub(crate) struct ScopeData { |
37 | parent: Option<ScopeId>, | 41 | parent: Option<ScopeId>, |
38 | entries: Vec<ScopeEntry>, | 42 | entries: Vec<ScopeEntry>, |
39 | } | 43 | } |
40 | 44 | ||
41 | impl ExprScopes { | 45 | impl 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)] | ||
111 | pub struct ScopesWithSourceMap { | ||
112 | pub source_map: Arc<BodySourceMap>, | ||
113 | pub scopes: Arc<ExprScopes>, | ||
114 | } | ||
115 | |||
116 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
117 | pub struct ScopeEntryWithSyntax { | ||
118 | name: Name, | ||
119 | ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
120 | } | ||
121 | |||
122 | impl 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 | |||
132 | impl 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 | |||
219 | impl 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)] | ||
290 | pub struct ReferenceDescriptor { | ||
291 | pub range: TextRange, | ||
292 | pub name: String, | ||
293 | } | ||
294 | |||
295 | #[cfg(test)] | 173 | #[cfg(test)] |
296 | mod tests { | 174 | mod 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 | ||
56 | pub use self::{ | 57 | pub 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 | ||
71 | pub use self::code_model_api::{ | 73 | pub 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)] |
18 | pub struct Resolver { | 18 | pub(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 | ||
119 | impl Resolver { | 119 | impl 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 | ||
210 | impl Resolver { | 202 | impl 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". |
8 | use std::sync::Arc; | ||
9 | |||
10 | use rustc_hash::{FxHashSet, FxHashMap}; | ||
8 | use ra_db::{FileId, FilePosition}; | 11 | use ra_db::{FileId, FilePosition}; |
9 | use ra_syntax::{ | 12 | use 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 | ||
15 | use crate::{ | 19 | use 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 | ||
90 | pub 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 | |||
100 | pub 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 | |||
111 | pub 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 | |||
117 | pub 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 | |||
127 | pub 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 | |||
138 | pub 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 | |||
147 | pub fn struct_from_module( | 95 | pub 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 | ||
158 | pub 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 | |||
168 | pub 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 | |||
179 | pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum { | 106 | pub 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 | ||
197 | pub 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 | |||
220 | pub 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 | |||
239 | fn try_get_resolver_for_node( | 124 | fn 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 | |||
147 | fn 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)] | ||
171 | pub 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)] | ||
179 | pub 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)] | ||
191 | pub struct ScopeEntryWithSyntax { | ||
192 | pub(crate) name: Name, | ||
193 | pub(crate) ptr: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>, | ||
194 | } | ||
195 | |||
196 | impl 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)] | ||
207 | pub struct ReferenceDescriptor { | ||
208 | pub range: TextRange, | ||
209 | pub name: String, | ||
210 | } | ||
211 | |||
212 | impl 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 | |||
362 | fn 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 | |||
373 | fn 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... | ||
391 | fn 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; | |||
15 | use std::{fmt, mem}; | 15 | use std::{fmt, mem}; |
16 | 16 | ||
17 | use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; | 17 | use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; |
18 | use display::{HirDisplay, HirFormatter}; | ||
18 | 19 | ||
19 | pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; | 20 | pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig}; |
20 | pub(crate) use infer::{infer, InferenceResult, InferTy}; | 21 | pub(crate) use infer::{infer, InferenceResult, InferTy}; |
21 | use display::{HirDisplay, HirFormatter}; | 22 | pub 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> { | |||
135 | impl Ty { | 135 | impl 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; | |||
4 | use insta::assert_snapshot_matches; | 4 | use insta::assert_snapshot_matches; |
5 | 5 | ||
6 | use ra_db::{SourceDatabase, salsa::Database, FilePosition}; | 6 | use ra_db::{SourceDatabase, salsa::Database, FilePosition}; |
7 | use ra_syntax::{algo, ast::{self, AstNode}}; | 7 | use ra_syntax::{algo, ast::{self, AstNode}, SyntaxKind::*}; |
8 | use test_utils::covers; | 8 | use test_utils::covers; |
9 | 9 | ||
10 | use crate::{ | 10 | use 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 | ||
2304 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 2304 | fn 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; | |||
2 | use ra_db::SourceDatabase; | 2 | use ra_db::SourceDatabase; |
3 | use ra_syntax::{ | 3 | use 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 | ||
76 | impl<'a> FnCallNode<'a> { | 82 | impl<'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} |
148 | fn bar() { foo(<|>3, ); }"#, | 154 | fn 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#" |
385 | pub trait WriteHandler<E> | 391 | struct WriteHandler<E>; |
386 | where | 392 | |
387 | Self: Actor, | 393 | impl<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 | ||
406 | pub fn foo() { | 410 | pub 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). |
6 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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. |
6 | pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(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 | }; |
8 | use hir::{source_binder, Resolver}; | 8 | use hir::source_binder; |
9 | 9 | ||
10 | use crate::{db, FilePosition}; | 10 | use crate::{db, FilePosition}; |
11 | 11 | ||
@@ -14,11 +14,10 @@ use crate::{db, FilePosition}; | |||
14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
15 | pub(crate) struct CompletionContext<'a> { | 15 | pub(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 @@ | |||
1 | use ra_db::{FileId, SourceDatabase}; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
8 | use hir::{ModuleSource, FieldSource, Name, ImplItem}; | 8 | use hir::{ModuleSource, FieldSource, ImplItem, Either}; |
9 | 9 | ||
10 | use crate::{FileSymbol, db::RootDatabase}; | 10 | use 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 | }; |
7 | use test_utils::tested_by; | 7 | use test_utils::tested_by; |
8 | use hir::Resolution; | ||
9 | 8 | ||
10 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 9 | use 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] |
66 | fn self_hosting_parsing() { | 66 | fn self_hosting_parsing() { |