aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-04-10 09:15:55 +0100
committerAleksey Kladov <[email protected]>2019-04-11 14:29:33 +0100
commit10d66d63d716a10ba7a5a8d1b69c9066249caf69 (patch)
tree3897deea123f2bec49c28cdeb453ae0cfa0f49f1 /crates
parente6e2571bdf780d304c792d4317bbaf1d6f5d7a0a (diff)
introduce SourceAnalyzer
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/add_explicit_type.rs8
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs15
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs10
-rw-r--r--crates/ra_assists/src/fill_struct_fields.rs11
-rw-r--r--crates/ra_hir/src/code_model_api.rs10
-rw-r--r--crates/ra_hir/src/expr.rs16
-rw-r--r--crates/ra_hir/src/expr/scope.rs2
-rw-r--r--crates/ra_hir/src/lib.rs1
-rw-r--r--crates/ra_hir/src/source_binder.rs100
-rw-r--r--crates/ra_ide_api/src/completion/complete_dot.rs11
-rw-r--r--crates/ra_ide_api/src/completion/complete_struct_literal.rs11
-rw-r--r--crates/ra_ide_api/src/completion/completion_context.rs3
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs25
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs152
-rw-r--r--crates/ra_ide_api/src/hover.rs18
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 @@
1use hir::{ 1use hir::{
2 HirDisplay, Ty, 2 HirDisplay, Ty,
3 db::HirDatabase, 3 db::HirDatabase,
4 source_binder::function_from_child_node,
5}; 4};
6use ra_syntax::{ 5use ra_syntax::{
7 SyntaxKind, 6 SyntaxKind,
@@ -30,11 +29,8 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
30 } 29 }
31 // Infer type 30 // Infer type
32 let db = ctx.db; 31 let db = ctx.db;
33 let func = function_from_child_node(db, ctx.frange.file_id, pat.syntax())?; 32 let analyzer = hir::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
3use crate::{Assist, AssistId, AssistCtx}; 3use crate::{Assist, AssistId, AssistCtx};
4 4
5use hir::Resolver;
6use hir::db::HirDatabase; 5use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; 6use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
8use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; 7use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner};
@@ -46,9 +45,9 @@ fn add_missing_impl_members_inner(
46 let trait_def = { 45 let trait_def = {
47 let file_id = ctx.frange.file_id; 46 let file_id = ctx.frange.file_id;
48 let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; 47 let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
49 let resolver = hir::source_binder::resolver_for_position(ctx.db, position); 48 let 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`.
123fn resolve_target_trait_def( 122fn 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 @@
1use std::fmt::Write; 1use std::fmt::Write;
2 2
3use hir::{ 3use hir::{
4 AdtDef, FieldSource, source_binder, 4 AdtDef, FieldSource,
5 db::HirDatabase, 5 db::HirDatabase,
6}; 6};
7use ra_syntax::ast::{self, AstNode}; 7use ra_syntax::ast::{self, AstNode};
@@ -20,12 +20,8 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
20 } 20 }
21 21
22 let expr = match_expr.expr()?; 22 let expr = match_expr.expr()?;
23 let function = 23 let analyzer = hir::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 @@
1use std::fmt::Write; 1use std::fmt::Write;
2 2
3use hir::{AdtDef, db::HirDatabase, source_binder::function_from_child_node}; 3use hir::{AdtDef, db::HirDatabase};
4 4
5use ra_syntax::ast::{self, AstNode}; 5use ra_syntax::ast::{self, AstNode};
6 6
@@ -51,15 +51,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
119impl BodySourceMap { 119impl 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)]
111pub struct ScopesWithSourceMap { 111pub 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
71pub use self::code_model_api::{ 72pub 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".
8use std::sync::Arc;
9
8use ra_db::{FileId, FilePosition}; 10use ra_db::{FileId, FilePosition};
9use ra_syntax::{ 11use 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
15use crate::{ 17use 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)]
266pub 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)]
273pub 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
284impl 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
347impl 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).
6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { 7 let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
8 (Some(function), Some(receiver)) => (function, receiver), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(receiver) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let receiver_ty = infer_result[expr].clone();
18 if !ctx.is_call { 11 if !ctx.is_call {
19 complete_fields(acc, ctx, receiver_ty.clone()); 12 complete_fields(acc, ctx, receiver_ty.clone());
20 } 13 }
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
index f58bcd03e..48fbf67f7 100644
--- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs
+++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs
@@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions};
4 4
5/// Complete fields in fields literals. 5/// Complete fields in fields literals.
6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { 6pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
7 let (function, struct_lit) = match (&ctx.function, ctx.struct_lit_syntax) { 7 let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) {
8 (Some(function), Some(struct_lit)) => (function, struct_lit), 8 Some(it) => it,
9 _ => return,
10 };
11 let infer_result = function.infer(ctx.db);
12 let source_map = function.body_source_map(ctx.db);
13 let expr = match source_map.node_expr(struct_lit.into()) {
14 Some(expr) => expr,
15 None => return, 9 None => return,
16 }; 10 };
17 let ty = infer_result[expr].clone();
18 let (adt, substs) = match ty.as_adt() { 11 let (adt, substs) = match ty.as_adt() {
19 Some(res) => res, 12 Some(res) => res,
20 _ => return, 13 _ => return,
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs
index 65dffa470..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)]
15pub(crate) struct CompletionContext<'a> { 15pub(crate) struct CompletionContext<'a> {
16 pub(super) db: &'a db::RootDatabase, 16 pub(super) db: &'a db::RootDatabase,
17 pub(super) analyzer: hir::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 @@
1use ra_db::{FileId, SourceDatabase}; 1use ra_db::{FileId, SourceDatabase};
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, 3 SyntaxNode, AstNode, SmolStr, TextRange, TreeArc, AstPtr,
4 SyntaxKind::{self, NAME}, 4 SyntaxKind::{self, NAME},
5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, 5 ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
6 algo::visit::{visitor, Visitor}, 6 algo::visit::{visitor, Visitor},
7}; 7};
8use hir::{ModuleSource, FieldSource, Name, ImplItem}; 8use hir::{ModuleSource, FieldSource, ImplItem, Either};
9 9
10use crate::{FileSymbol, db::RootDatabase}; 10use crate::{FileSymbol, db::RootDatabase};
11 11
@@ -74,15 +74,25 @@ impl NavigationTarget {
74 } 74 }
75 } 75 }
76 76
77 pub(crate) fn from_scope_entry( 77 pub(crate) fn from_pat(
78 db: &RootDatabase,
78 file_id: FileId, 79 file_id: FileId,
79 name: Name, 80 pat: Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>,
80 ptr: SyntaxNodePtr,
81 ) -> NavigationTarget { 81 ) -> NavigationTarget {
82 let file = db.parse(file_id);
83 let (name, full_range) = match pat {
84 Either::A(pat) => match pat.to_node(&file).kind() {
85 ast::PatKind::BindPat(pat) => {
86 return NavigationTarget::from_bind_pat(file_id, &pat)
87 }
88 _ => ("_".into(), pat.syntax_node_ptr().range()),
89 },
90 Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()),
91 };
82 NavigationTarget { 92 NavigationTarget {
83 file_id, 93 file_id,
84 name: name.to_string().into(), 94 name,
85 full_range: ptr.range(), 95 full_range,
86 focus_range: None, 96 focus_range: None,
87 kind: NAME, 97 kind: NAME,
88 container_name: None, 98 container_name: None,
@@ -229,6 +239,7 @@ impl NavigationTarget {
229 239
230 /// Allows `NavigationTarget` to be created from a `NameOwner` 240 /// Allows `NavigationTarget` to be created from a `NameOwner`
231 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { 241 pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget {
242 //FIXME: use `_` instead of empty string
232 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 243 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
233 let focus_range = node.name().map(|it| it.syntax().range()); 244 let focus_range = node.name().map(|it| it.syntax().range());
234 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) 245 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax())
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 60c1f5085..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};
7use test_utils::tested_by; 7use test_utils::tested_by;
8use hir::Resolution;
9 8
10use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; 9use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
11 10
@@ -48,127 +47,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)]