aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/goto_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/goto_definition.rs')
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs150
1 files changed, 46 insertions, 104 deletions
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)