diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 34 | ||||
-rw-r--r-- | crates/ra_ide_api/src/marks.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 13 |
3 files changed, 47 insertions, 6 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 46bdde00d..45b4c56ef 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -3,6 +3,7 @@ use ra_syntax::{ | |||
3 | AstNode, ast, | 3 | AstNode, ast, |
4 | algo::find_node_at_offset, | 4 | algo::find_node_at_offset, |
5 | }; | 5 | }; |
6 | use test_utils::tested_by; | ||
6 | 7 | ||
7 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; | 8 | use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; |
8 | 9 | ||
@@ -60,6 +61,7 @@ pub(crate) fn reference_definition( | |||
60 | .parent() | 61 | .parent() |
61 | .and_then(ast::MethodCallExpr::cast) | 62 | .and_then(ast::MethodCallExpr::cast) |
62 | { | 63 | { |
64 | tested_by!(goto_definition_works_for_methods); | ||
63 | let infer_result = function.infer(db); | 65 | let infer_result = function.infer(db); |
64 | let syntax_mapping = function.body_syntax_mapping(db); | 66 | let syntax_mapping = function.body_syntax_mapping(db); |
65 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); | 67 | let expr = ast::Expr::cast(method_call.syntax()).unwrap(); |
@@ -70,6 +72,19 @@ pub(crate) fn reference_definition( | |||
70 | return Exact(NavigationTarget::from_function(db, func)); | 72 | return Exact(NavigationTarget::from_function(db, func)); |
71 | }; | 73 | }; |
72 | } | 74 | } |
75 | // It could also be a field access | ||
76 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { | ||
77 | tested_by!(goto_definition_works_for_fields); | ||
78 | let infer_result = function.infer(db); | ||
79 | let syntax_mapping = function.body_syntax_mapping(db); | ||
80 | let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); | ||
81 | if let Some(field) = syntax_mapping | ||
82 | .node_expr(expr) | ||
83 | .and_then(|it| infer_result.field_resolution(it)) | ||
84 | { | ||
85 | return Exact(NavigationTarget::from_field(db, field)); | ||
86 | }; | ||
87 | } | ||
73 | } | 88 | } |
74 | // Then try module name resolution | 89 | // Then try module name resolution |
75 | if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) | 90 | if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax()) |
@@ -117,6 +132,8 @@ fn name_definition( | |||
117 | 132 | ||
118 | #[cfg(test)] | 133 | #[cfg(test)] |
119 | mod tests { | 134 | mod tests { |
135 | use test_utils::covers; | ||
136 | |||
120 | use crate::mock_analysis::analysis_and_position; | 137 | use crate::mock_analysis::analysis_and_position; |
121 | 138 | ||
122 | fn check_goto(fixuture: &str, expected: &str) { | 139 | fn check_goto(fixuture: &str, expected: &str) { |
@@ -183,6 +200,7 @@ mod tests { | |||
183 | 200 | ||
184 | #[test] | 201 | #[test] |
185 | fn goto_definition_works_for_methods() { | 202 | fn goto_definition_works_for_methods() { |
203 | covers!(goto_definition_works_for_methods); | ||
186 | check_goto( | 204 | check_goto( |
187 | " | 205 | " |
188 | //- /lib.rs | 206 | //- /lib.rs |
@@ -197,15 +215,23 @@ mod tests { | |||
197 | ", | 215 | ", |
198 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", | 216 | "frobnicate FN_DEF FileId(1) [27; 52) [30; 40)", |
199 | ); | 217 | ); |
218 | } | ||
200 | 219 | ||
220 | #[test] | ||
221 | fn goto_definition_works_for_fields() { | ||
222 | covers!(goto_definition_works_for_fields); | ||
201 | check_goto( | 223 | check_goto( |
202 | " | 224 | " |
203 | //- /lib.rs | 225 | //- /lib.rs |
204 | mod <|>foo; | 226 | struct Foo { |
205 | //- /foo/mod.rs | 227 | spam: u32, |
206 | // empty | 228 | } |
229 | |||
230 | fn bar(foo: &Foo) { | ||
231 | foo.spam<|>; | ||
232 | } | ||
207 | ", | 233 | ", |
208 | "foo SOURCE_FILE FileId(2) [0; 10)", | 234 | "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
209 | ); | 235 | ); |
210 | } | 236 | } |
211 | } | 237 | } |
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index dc5b2702a..e33bf6c91 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs | |||
@@ -1 +1,5 @@ | |||
1 | test_utils::marks!(inserts_parens_for_function_calls); | 1 | test_utils::marks!( |
2 | inserts_parens_for_function_calls | ||
3 | goto_definition_works_for_methods | ||
4 | goto_definition_works_for_fields | ||
5 | ); | ||
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index c5be8e01b..ae2175dbc 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | SyntaxNode, AstNode, SmolStr, TextRange, ast, | 3 | SyntaxNode, AstNode, SmolStr, TextRange, ast, |
4 | SyntaxKind::{self, NAME}, | 4 | SyntaxKind::{self, NAME}, |
5 | }; | 5 | }; |
6 | use hir::{ModuleSource}; | 6 | use hir::{ModuleSource, FieldSource}; |
7 | 7 | ||
8 | use crate::{FileSymbol, db::RootDatabase}; | 8 | use crate::{FileSymbol, db::RootDatabase}; |
9 | 9 | ||
@@ -101,6 +101,17 @@ impl NavigationTarget { | |||
101 | NavigationTarget::from_named(file_id.original_file(db), &*fn_def) | 101 | NavigationTarget::from_named(file_id.original_file(db), &*fn_def) |
102 | } | 102 | } |
103 | 103 | ||
104 | pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget { | ||
105 | let (file_id, field) = field.source(db); | ||
106 | let file_id = file_id.original_file(db); | ||
107 | match field { | ||
108 | FieldSource::Named(it) => NavigationTarget::from_named(file_id, &*it), | ||
109 | FieldSource::Pos(it) => { | ||
110 | NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax()) | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
104 | // TODO once Def::Item is gone, this should be able to always return a NavigationTarget | 115 | // TODO once Def::Item is gone, this should be able to always return a NavigationTarget |
105 | pub(crate) fn from_def( | 116 | pub(crate) fn from_def( |
106 | db: &RootDatabase, | 117 | db: &RootDatabase, |