aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-27 15:59:56 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-27 15:59:56 +0000
commit84e47113e0ab581c1b2c207cbcb212aa86f83c4a (patch)
tree92ff5036db5a7bb4812b63f69d17402b88e3873f
parente1dd31e7e284ef49c2de317a1a81da7a854ffc63 (diff)
parentc879f43186a30bd5ab6886600bd6fac8aff035b2 (diff)
Merge #906
906: Add support for goto definition and hover for struct fields r=matklad a=vipentti This works partially towards fixing #512 Co-authored-by: Ville Penttinen <[email protected]>
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs44
-rw-r--r--crates/ra_ide_api/src/hover.rs82
-rw-r--r--crates/ra_ide_api/src/marks.rs1
3 files changed, 124 insertions, 3 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index da33739be..6fa430754 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -74,6 +74,30 @@ pub(crate) fn reference_definition(
74 return Exact(NavigationTarget::from_field(db, field)); 74 return Exact(NavigationTarget::from_field(db, field));
75 }; 75 };
76 } 76 }
77
78 // It could also be a named field
79 if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) {
80 tested_by!(goto_definition_works_for_named_fields);
81
82 let infer_result = function.infer(db);
83 let syntax_mapping = function.body_syntax_mapping(db);
84
85 let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
86
87 if let Some(expr) = struct_lit.and_then(|lit| syntax_mapping.node_expr(lit.into())) {
88 let ty = infer_result[expr].clone();
89 if let hir::Ty::Adt { def_id, .. } = ty {
90 if let hir::AdtDef::Struct(s) = def_id {
91 let hir_path = hir::Path::from_name_ref(name_ref);
92 let hir_name = hir_path.as_ident().unwrap();
93
94 if let Some(field) = s.field(db, hir_name) {
95 return Exact(NavigationTarget::from_field(db, field));
96 }
97 }
98 }
99 }
100 }
77 } 101 }
78 // Try name resolution 102 // Try name resolution
79 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); 103 let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax());
@@ -256,6 +280,26 @@ mod tests {
256 } 280 }
257 281
258 #[test] 282 #[test]
283 fn goto_definition_works_for_named_fields() {
284 covers!(goto_definition_works_for_named_fields);
285 check_goto(
286 "
287 //- /lib.rs
288 struct Foo {
289 spam: u32,
290 }
291
292 fn bar() -> Foo {
293 Foo {
294 spam<|>: 0,
295 }
296 }
297 ",
298 "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)",
299 );
300 }
301
302 #[test]
259 fn goto_definition_works_when_used_on_definition_name_itself() { 303 fn goto_definition_works_when_used_on_definition_name_itself() {
260 check_goto( 304 check_goto(
261 " 305 "
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index ef3b5df29..47913d753 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,6 +1,6 @@
1use ra_db::SourceDatabase; 1use ra_db::SourceDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner}, 3 AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner},
4 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, 4 algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}},
5}; 5};
6 6
@@ -179,6 +179,7 @@ impl NavigationTarget {
179 .visit(doc_comments::<ast::TypeAliasDef>) 179 .visit(doc_comments::<ast::TypeAliasDef>)
180 .visit(doc_comments::<ast::ConstDef>) 180 .visit(doc_comments::<ast::ConstDef>)
181 .visit(doc_comments::<ast::StaticDef>) 181 .visit(doc_comments::<ast::StaticDef>)
182 .visit(doc_comments::<ast::NamedFieldDef>)
182 .accept(&node)? 183 .accept(&node)?
183 } 184 }
184 185
@@ -189,6 +190,20 @@ impl NavigationTarget {
189 // TODO: After type inference is done, add type information to improve the output 190 // TODO: After type inference is done, add type information to improve the output
190 let node = self.node(db)?; 191 let node = self.node(db)?;
191 192
193 fn visit_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
194 where
195 T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
196 {
197 let mut string = visit_node(node, prefix)?;
198
199 if let Some(type_ref) = node.ascribed_type() {
200 string.push_str(": ");
201 type_ref.syntax().text().push_to(&mut string);
202 }
203
204 Some(string)
205 }
206
192 fn visit_node<T>(node: &T, label: &str) -> Option<String> 207 fn visit_node<T>(node: &T, label: &str) -> Option<String>
193 where 208 where
194 T: NameOwner + VisibilityOwner, 209 T: NameOwner + VisibilityOwner,
@@ -207,8 +222,9 @@ impl NavigationTarget {
207 .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) 222 .visit(|node: &ast::TraitDef| visit_node(node, "trait "))
208 .visit(|node: &ast::Module| visit_node(node, "mod ")) 223 .visit(|node: &ast::Module| visit_node(node, "mod "))
209 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) 224 .visit(|node: &ast::TypeAliasDef| visit_node(node, "type "))
210 .visit(|node: &ast::ConstDef| visit_node(node, "const ")) 225 .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const "))
211 .visit(|node: &ast::StaticDef| visit_node(node, "static ")) 226 .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static "))
227 .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, ""))
212 .accept(&node)? 228 .accept(&node)?
213 } 229 }
214} 230}
@@ -321,6 +337,66 @@ mod tests {
321 } 337 }
322 338
323 #[test] 339 #[test]
340 fn hover_shows_struct_field_info() {
341 // Hovering over the field when instantiating
342 check_hover_result(
343 r#"
344 //- /main.rs
345 struct Foo {
346 field_a: u32,
347 }
348
349 fn main() {
350 let foo = Foo {
351 field_a<|>: 0,
352 };
353 }
354 "#,
355 &["field_a: u32"],
356 );
357
358 // Hovering over the field in the definition
359 check_hover_result(
360 r#"
361 //- /main.rs
362 struct Foo {
363 field_a<|>: u32,
364 }
365
366 fn main() {
367 let foo = Foo {
368 field_a: 0,
369 };
370 }
371 "#,
372 &["field_a: u32"],
373 );
374 }
375
376 #[test]
377 fn hover_const_static() {
378 check_hover_result(
379 r#"
380 //- /main.rs
381 fn main() {
382 const foo<|>: u32 = 0;
383 }
384 "#,
385 &["const foo: u32"],
386 );
387
388 check_hover_result(
389 r#"
390 //- /main.rs
391 fn main() {
392 static foo<|>: u32 = 0;
393 }
394 "#,
395 &["static foo: u32"],
396 );
397 }
398
399 #[test]
324 fn hover_some() { 400 fn hover_some() {
325 let (analysis, position) = single_file_with_position( 401 let (analysis, position) = single_file_with_position(
326 " 402 "
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs
index 44d8fdf82..bcbe0d21b 100644
--- a/crates/ra_ide_api/src/marks.rs
+++ b/crates/ra_ide_api/src/marks.rs
@@ -2,6 +2,7 @@ test_utils::marks!(
2 inserts_parens_for_function_calls 2 inserts_parens_for_function_calls
3 goto_definition_works_for_methods 3 goto_definition_works_for_methods
4 goto_definition_works_for_fields 4 goto_definition_works_for_fields
5 goto_definition_works_for_named_fields
5 call_info_bad_offset 6 call_info_bad_offset
6 dont_complete_current_use 7 dont_complete_current_use
7); 8);