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