diff options
author | Aleksey Kladov <[email protected]> | 2019-04-10 09:15:55 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-04-11 14:29:33 +0100 |
commit | 10d66d63d716a10ba7a5a8d1b69c9066249caf69 (patch) | |
tree | 3897deea123f2bec49c28cdeb453ae0cfa0f49f1 | |
parent | e6e2571bdf780d304c792d4317bbaf1d6f5d7a0a (diff) |
introduce SourceAnalyzer
-rw-r--r-- | crates/ra_assists/src/add_explicit_type.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 15 | ||||
-rw-r--r-- | crates/ra_assists/src/fill_match_arms.rs | 10 | ||||
-rw-r--r-- | crates/ra_assists/src/fill_struct_fields.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 16 | ||||
-rw-r--r-- | crates/ra_hir/src/expr/scope.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 100 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_struct_literal.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/completion_context.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/navigation_target.rs | 25 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 152 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 18 |
15 files changed, 206 insertions, 187 deletions
diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs index 1dc59bb87..c50db4e21 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::SourceAnalyser::new(db, ctx.frange.file_id, stmt.syntax()); |
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..0b2127e11 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 analyser = hir::SourceAnalyser::new(ctx.db, position.file_id, impl_node.syntax()); |
50 | 49 | ||
51 | resolve_target_trait_def(ctx.db, &resolver, impl_node)? | 50 | resolve_target_trait_def(ctx.db, &analyser, 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 | binder: &hir::SourceAnalyser, |
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 binder.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..050b1c73f 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::SourceAnalyser::new(ctx.db, ctx.frange.file_id, expr.syntax()); |
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..e23cccbb8 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,12 @@ 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::SourceAnalyser::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 | ); |
59 | let infer_result = function.infer(self.ctx.db); | 59 | let struct_lit_ty = analyzer.type_of(self.ctx.db, self.struct_lit.into())?; |
60 | let source_map = function.body_source_map(self.ctx.db); | ||
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() { | 60 | let struct_def = match struct_lit_ty.as_adt() { |
64 | Some((AdtDef::Struct(s), _)) => s, | 61 | Some((AdtDef::Struct(s), _)) => s, |
65 | _ => return None, | 62 | _ => return None, |
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index f13a6b37a..660edf006 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -450,10 +450,6 @@ 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 | } |
@@ -523,7 +519,7 @@ impl Function { | |||
523 | self.signature(db).name.clone() | 519 | self.signature(db).name.clone() |
524 | } | 520 | } |
525 | 521 | ||
526 | pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { | 522 | pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { |
527 | db.body_with_source_map((*self).into()).1 | 523 | db.body_with_source_map((*self).into()).1 |
528 | } | 524 | } |
529 | 525 | ||
@@ -606,7 +602,7 @@ impl Const { | |||
606 | db.infer((*self).into()) | 602 | db.infer((*self).into()) |
607 | } | 603 | } |
608 | 604 | ||
609 | pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { | 605 | pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { |
610 | db.body_with_source_map((*self).into()).1 | 606 | db.body_with_source_map((*self).into()).1 |
611 | } | 607 | } |
612 | 608 | ||
@@ -679,7 +675,7 @@ impl Static { | |||
679 | db.infer((*self).into()) | 675 | db.infer((*self).into()) |
680 | } | 676 | } |
681 | 677 | ||
682 | pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { | 678 | pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc<BodySourceMap> { |
683 | db.body_with_source_map((*self).into()).1 | 679 | db.body_with_source_map((*self).into()).1 |
684 | } | 680 | } |
685 | } | 681 | } |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index a8e115e47..3806a3605 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -117,31 +117,27 @@ impl Index<PatId> for Body { | |||
117 | } | 117 | } |
118 | 118 | ||
119 | impl BodySourceMap { | 119 | impl BodySourceMap { |
120 | pub fn expr_syntax(&self, expr: ExprId) -> Option<SyntaxNodePtr> { | 120 | pub(crate) fn expr_syntax(&self, expr: ExprId) -> Option<SyntaxNodePtr> { |
121 | self.expr_map_back.get(expr).cloned() | 121 | self.expr_map_back.get(expr).cloned() |
122 | } | 122 | } |
123 | 123 | ||
124 | pub fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option<ExprId> { | 124 | pub(crate) fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option<ExprId> { |
125 | self.expr_map.get(&ptr).cloned() | 125 | self.expr_map.get(&ptr).cloned() |
126 | } | 126 | } |
127 | 127 | ||
128 | pub fn node_expr(&self, node: &ast::Expr) -> Option<ExprId> { | 128 | pub(crate) fn node_expr(&self, node: &ast::Expr) -> Option<ExprId> { |
129 | self.expr_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() | 129 | self.expr_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() |
130 | } | 130 | } |
131 | 131 | ||
132 | pub fn pat_syntax(&self, pat: PatId) -> Option<PatPtr> { | 132 | pub(crate) fn pat_syntax(&self, pat: PatId) -> Option<PatPtr> { |
133 | self.pat_map_back.get(pat).cloned() | 133 | self.pat_map_back.get(pat).cloned() |
134 | } | 134 | } |
135 | 135 | ||
136 | pub fn syntax_pat(&self, ptr: PatPtr) -> Option<PatId> { | 136 | 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() | 137 | self.pat_map.get(&Either::A(AstPtr::new(node))).cloned() |
142 | } | 138 | } |
143 | 139 | ||
144 | pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> { | 140 | pub(crate) fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::NamedField> { |
145 | self.field_map[&(expr, field)].clone() | 141 | self.field_map[&(expr, field)].clone() |
146 | } | 142 | } |
147 | } | 143 | } |
diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 725b6c00e..404c979eb 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs | |||
@@ -109,7 +109,7 @@ impl ExprScopes { | |||
109 | 109 | ||
110 | #[derive(Debug, Clone, PartialEq, Eq)] | 110 | #[derive(Debug, Clone, PartialEq, Eq)] |
111 | pub struct ScopesWithSourceMap { | 111 | pub struct ScopesWithSourceMap { |
112 | pub source_map: Arc<BodySourceMap>, | 112 | pub(crate) source_map: Arc<BodySourceMap>, |
113 | pub scopes: Arc<ExprScopes>, | 113 | pub scopes: Arc<ExprScopes>, |
114 | } | 114 | } |
115 | 115 | ||
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 0881939a2..59b402c57 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -66,6 +66,7 @@ pub use self::{ | |||
66 | adt::AdtDef, | 66 | adt::AdtDef, |
67 | expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, | 67 | expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, |
68 | resolve::{Resolver, Resolution}, | 68 | resolve::{Resolver, Resolution}, |
69 | source_binder::{SourceAnalyser, PathResolution}, | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | pub use self::code_model_api::{ | 72 | pub use self::code_model_api::{ |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 182ed4c91..79e304383 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -5,15 +5,17 @@ | |||
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 | |||
8 | use ra_db::{FileId, FilePosition}; | 10 | use ra_db::{FileId, FilePosition}; |
9 | use ra_syntax::{ | 11 | use ra_syntax::{ |
10 | SyntaxNode, | 12 | SyntaxNode, AstPtr, |
11 | ast::{self, AstNode, NameOwner}, | 13 | ast::{self, AstNode, NameOwner}, |
12 | algo::{find_node_at_offset, find_token_at_offset}, | 14 | algo::{find_node_at_offset, find_token_at_offset}, |
13 | }; | 15 | }; |
14 | 16 | ||
15 | use crate::{ | 17 | use crate::{ |
16 | HirDatabase, Function, Struct, Enum,Const,Static, | 18 | HirDatabase, Function, Struct, Enum, Const, Static, Either, |
17 | AsName, Module, HirFileId, Crate, Trait, Resolver, | 19 | AsName, Module, HirFileId, Crate, Trait, Resolver, |
18 | ids::LocationCtx, | 20 | ids::LocationCtx, |
19 | expr, AstId | 21 | expr, AstId |
@@ -258,3 +260,97 @@ fn try_get_resolver_for_node( | |||
258 | None | 260 | None |
259 | } | 261 | } |
260 | } | 262 | } |
263 | |||
264 | // Name is bad, don't use inside HIR | ||
265 | #[derive(Debug)] | ||
266 | pub struct SourceAnalyser { | ||
267 | resolver: Resolver, | ||
268 | body_source_map: Option<Arc<crate::expr::BodySourceMap>>, | ||
269 | infer: Option<Arc<crate::ty::InferenceResult>>, | ||
270 | } | ||
271 | |||
272 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
273 | pub enum PathResolution { | ||
274 | /// An item | ||
275 | Def(crate::ModuleDef), | ||
276 | /// A local binding (only value namespace) | ||
277 | LocalBinding(crate::expr::PatId), | ||
278 | /// A generic parameter | ||
279 | GenericParam(u32), | ||
280 | SelfType(crate::ImplBlock), | ||
281 | AssocItem(crate::ImplItem), | ||
282 | } | ||
283 | |||
284 | impl SourceAnalyser { | ||
285 | pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyser { | ||
286 | let resolver = resolver_for_node(db, file_id, node); | ||
287 | let function = function_from_child_node(db, file_id, node); | ||
288 | if let Some(function) = function { | ||
289 | SourceAnalyser { | ||
290 | resolver, | ||
291 | body_source_map: Some(function.body_source_map(db)), | ||
292 | infer: Some(function.infer(db)), | ||
293 | } | ||
294 | } else { | ||
295 | SourceAnalyser { resolver, body_source_map: None, infer: None } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option<crate::Ty> { | ||
300 | let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?; | ||
301 | Some(self.infer.as_ref()?[expr_id].clone()) | ||
302 | } | ||
303 | |||
304 | pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option<crate::Ty> { | ||
305 | let pat_id = self.body_source_map.as_ref()?.node_pat(pat)?; | ||
306 | Some(self.infer.as_ref()?[pat_id].clone()) | ||
307 | } | ||
308 | |||
309 | pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { | ||
310 | let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?; | ||
311 | self.infer.as_ref()?.method_resolution(expr_id) | ||
312 | } | ||
313 | |||
314 | pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { | ||
315 | let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?; | ||
316 | self.infer.as_ref()?.field_resolution(expr_id) | ||
317 | } | ||
318 | |||
319 | pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { | ||
320 | if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { | ||
321 | let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?; | ||
322 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { | ||
323 | return Some(PathResolution::AssocItem(assoc)); | ||
324 | } | ||
325 | } | ||
326 | if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { | ||
327 | let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?; | ||
328 | if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { | ||
329 | return Some(PathResolution::AssocItem(assoc)); | ||
330 | } | ||
331 | } | ||
332 | let hir_path = crate::Path::from_ast(path)?; | ||
333 | let res = self.resolver.resolve_path(db, &hir_path); | ||
334 | let res = res.clone().take_types().or_else(|| res.take_values())?; | ||
335 | Some(res.into()) | ||
336 | } | ||
337 | |||
338 | pub fn pat_syntax( | ||
339 | &self, | ||
340 | _db: &impl HirDatabase, | ||
341 | pat: crate::expr::PatId, | ||
342 | ) -> Option<Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>> { | ||
343 | self.body_source_map.as_ref()?.pat_syntax(pat) | ||
344 | } | ||
345 | } | ||
346 | |||
347 | impl From<crate::Resolution> for PathResolution { | ||
348 | fn from(res: crate::Resolution) -> PathResolution { | ||
349 | match res { | ||
350 | crate::Resolution::Def(it) => PathResolution::Def(it), | ||
351 | crate::Resolution::LocalBinding(it) => PathResolution::LocalBinding(it), | ||
352 | crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it), | ||
353 | crate::Resolution::SelfType(it) => PathResolution::SelfType(it), | ||
354 | } | ||
355 | } | ||
356 | } | ||
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index c093d5cfb..358057364 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 | } |
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..ce21fca9b 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs | |||
@@ -14,6 +14,7 @@ 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::SourceAnalyser, | ||
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) resolver: Resolver, |
@@ -50,8 +51,10 @@ impl<'a> CompletionContext<'a> { | |||
50 | let resolver = source_binder::resolver_for_position(db, position); | 51 | let resolver = source_binder::resolver_for_position(db, position); |
51 | let module = source_binder::module_from_position(db, position); | 52 | let module = source_binder::module_from_position(db, position); |
52 | let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; | 53 | let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; |
54 | let analyzer = hir::SourceAnalyser::new(db, position.file_id, token.parent()); | ||
53 | let mut ctx = CompletionContext { | 55 | let mut ctx = CompletionContext { |
54 | db, | 56 | db, |
57 | analyzer, | ||
55 | token, | 58 | token, |
56 | offset: position.offset, | 59 | offset: position.offset, |
57 | resolver, | 60 | resolver, |
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..7f93f50c4 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,72 @@ 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::SourceAnalyser::new(db, file_id, name_ref.syntax()); |
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 | if let Some(pat) = analyzer.pat_syntax(db, pat) { |
109 | Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), | 94 | let nav = NavigationTarget::from_pat(db, file_id, pat); |
110 | Some(Resolution::LocalBinding(pat)) => { | 95 | return Exact(nav); |
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 | } | ||
129 | } | ||
130 | None => { | ||
131 | // If we failed to resolve then check associated items | ||
132 | if let Some(function) = function { | ||
133 | // Resolve associated item for path expressions | ||
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 | } | 96 | } |
97 | } | ||
98 | hir::PathResolution::GenericParam(..) => { | ||
99 | // FIXME: go to the generic param def | ||
100 | } | ||
101 | hir::PathResolution::SelfType(impl_block) => { | ||
102 | let ty = impl_block.target_ty(db); | ||
149 | 103 | ||
150 | // Resolve associated item for path patterns | 104 | if let Some((def_id, _)) = ty.as_adt() { |
151 | if let Some(path_pat) = | 105 | 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 | } | 106 | } |
166 | } | 107 | } |
108 | hir::PathResolution::AssocItem(assoc) => { | ||
109 | return Exact(NavigationTarget::from_impl_item(db, assoc)) | ||
110 | } | ||
167 | } | 111 | } |
168 | } | 112 | } |
169 | } | 113 | } |
170 | 114 | ||
171 | // If that fails try the index based approach. | 115 | // Fallback index based approach: |
172 | let navs = crate::symbol_index::index_resolve(db, name_ref) | 116 | let navs = crate::symbol_index::index_resolve(db, name_ref) |
173 | .into_iter() | 117 | .into_iter() |
174 | .map(NavigationTarget::from_symbol) | 118 | .map(NavigationTarget::from_symbol) |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 3a8c93b99..ec167a196 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::SourceAnalyser::new(db, frange.file_id, node); |
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)] |