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