diff options
Diffstat (limited to 'crates/ra_ide_api/src/goto_definition.rs')
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 104 |
1 files changed, 25 insertions, 79 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index adae29e9c..9c56f17f2 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -1,12 +1,19 @@ | |||
1 | use ra_db::{FileId, SourceDatabase}; | 1 | use ra_db::{FileId, SourceDatabase}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, ast, | 3 | AstNode, ast, |
4 | algo::{find_node_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{ |
5 | find_node_at_offset, | ||
6 | visit::{visitor, Visitor}, | ||
7 | }, | ||
5 | SyntaxNode, | 8 | SyntaxNode, |
6 | }; | 9 | }; |
7 | use test_utils::tested_by; | ||
8 | 10 | ||
9 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 11 | use crate::{ |
12 | FilePosition, NavigationTarget, | ||
13 | db::RootDatabase, | ||
14 | RangeInfo, | ||
15 | name_ref_kind::{NameRefKind::*, classify_name_ref}, | ||
16 | }; | ||
10 | 17 | ||
11 | pub(crate) fn goto_definition( | 18 | pub(crate) fn goto_definition( |
12 | db: &RootDatabase, | 19 | db: &RootDatabase, |
@@ -50,85 +57,24 @@ pub(crate) fn reference_definition( | |||
50 | 57 | ||
51 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | 58 | let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); |
52 | 59 | ||
53 | // Special cases: | 60 | match classify_name_ref(db, &analyzer, name_ref) { |
54 | 61 | Some(Method(func)) => return Exact(NavigationTarget::from_function(db, func)), | |
55 | // Check if it is a method | 62 | Some(Macro(mac)) => return Exact(NavigationTarget::from_macro_def(db, mac)), |
56 | if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { | 63 | Some(FieldAccess(field)) => return Exact(NavigationTarget::from_field(db, field)), |
57 | tested_by!(goto_definition_works_for_methods); | 64 | Some(AssocItem(assoc)) => return Exact(NavigationTarget::from_impl_item(db, assoc)), |
58 | if let Some(func) = analyzer.resolve_method_call(method_call) { | 65 | Some(Def(def)) => return Exact(NavigationTarget::from_def(db, def)), |
59 | return Exact(NavigationTarget::from_function(db, func)); | 66 | Some(SelfType(ty)) => { |
60 | } | 67 | if let Some((def_id, _)) = ty.as_adt() { |
61 | } | 68 | return Exact(NavigationTarget::from_adt_def(db, def_id)); |
62 | |||
63 | //it could be a macro call | ||
64 | if let Some(macro_call) = name_ref | ||
65 | .syntax() | ||
66 | .parent() | ||
67 | .and_then(|node| node.parent()) | ||
68 | .and_then(|node| node.parent()) | ||
69 | .and_then(ast::MacroCall::cast) | ||
70 | { | ||
71 | tested_by!(goto_definition_works_for_macros); | ||
72 | if let Some(macro_call) = analyzer.resolve_macro_call(macro_call) { | ||
73 | return Exact(NavigationTarget::from_macro_def(db, macro_call)); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | // It could also be a field access | ||
78 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
79 | tested_by!(goto_definition_works_for_fields); | ||
80 | if let Some(field) = analyzer.resolve_field(field_expr) { | ||
81 | return Exact(NavigationTarget::from_field(db, field)); | ||
82 | }; | ||
83 | } | ||
84 | |||
85 | // It could also be a named field | ||
86 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { | ||
87 | tested_by!(goto_definition_works_for_named_fields); | ||
88 | |||
89 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | ||
90 | |||
91 | if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) { | ||
92 | if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { | ||
93 | let hir_path = hir::Path::from_name_ref(name_ref); | ||
94 | let hir_name = hir_path.as_ident().unwrap(); | ||
95 | |||
96 | if let Some(field) = s.field(db, hir_name) { | ||
97 | return Exact(NavigationTarget::from_field(db, field)); | ||
98 | } | ||
99 | } | 69 | } |
100 | } | 70 | } |
101 | } | 71 | Some(Pat(pat)) => return Exact(NavigationTarget::from_pat(db, file_id, pat)), |
102 | 72 | Some(SelfParam(par)) => return Exact(NavigationTarget::from_self_param(file_id, par)), | |
103 | // General case, a path or a local: | 73 | Some(GenericParam(_)) => { |
104 | if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { | 74 | // FIXME: go to the generic param def |
105 | if let Some(resolved) = analyzer.resolve_path(db, path) { | ||
106 | match resolved { | ||
107 | hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)), | ||
108 | hir::PathResolution::LocalBinding(pat) => { | ||
109 | let nav = NavigationTarget::from_pat(db, file_id, pat); | ||
110 | return Exact(nav); | ||
111 | } | ||
112 | hir::PathResolution::GenericParam(..) => { | ||
113 | // FIXME: go to the generic param def | ||
114 | } | ||
115 | hir::PathResolution::Macro(def) => { | ||
116 | let nav = NavigationTarget::from_macro_def(db, def); | ||
117 | return Exact(nav); | ||
118 | } | ||
119 | hir::PathResolution::SelfType(impl_block) => { | ||
120 | let ty = impl_block.target_ty(db); | ||
121 | |||
122 | if let Some((def_id, _)) = ty.as_adt() { | ||
123 | return Exact(NavigationTarget::from_adt_def(db, def_id)); | ||
124 | } | ||
125 | } | ||
126 | hir::PathResolution::AssocItem(assoc) => { | ||
127 | return Exact(NavigationTarget::from_impl_item(db, assoc)); | ||
128 | } | ||
129 | } | ||
130 | } | 75 | } |
131 | } | 76 | None => {} |
77 | }; | ||
132 | 78 | ||
133 | // Fallback index based approach: | 79 | // Fallback index based approach: |
134 | let navs = crate::symbol_index::index_resolve(db, name_ref) | 80 | let navs = crate::symbol_index::index_resolve(db, name_ref) |