diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index da33739be3..6fa4307547 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( return Exact(NavigationTarget::from_field(db, field)); }; } + + // It could also be a named field + if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { + tested_by!(goto_definition_works_for_named_fields); + + let infer_result = function.infer(db); + let syntax_mapping = function.body_syntax_mapping(db); + + let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); + + if let Some(expr) = struct_lit.and_then(|lit| syntax_mapping.node_expr(lit.into())) { + let ty = infer_result[expr].clone(); + if let hir::Ty::Adt { def_id, .. } = ty { + if let hir::AdtDef::Struct(s) = def_id { + let hir_path = hir::Path::from_name_ref(name_ref); + let hir_name = hir_path.as_ident().unwrap(); + + if let Some(field) = s.field(db, hir_name) { + return Exact(NavigationTarget::from_field(db, field)); + } + } + } + } + } } // Try name resolution let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); @@ -255,6 +279,26 @@ mod tests { ); } + #[test] + fn goto_definition_works_for_named_fields() { + covers!(goto_definition_works_for_named_fields); + check_goto( + " + //- /lib.rs + struct Foo { + spam: u32, + } + + fn bar() -> Foo { + Foo { + spam<|>: 0, + } + } + ", + "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", + ); + } + #[test] fn goto_definition_works_when_used_on_definition_name_itself() { check_goto( diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index ef3b5df292..47913d7532 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -1,6 +1,6 @@ use ra_db::SourceDatabase; use ra_syntax::{ - AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner}, + AstNode, SyntaxNode, TreeArc, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, }; @@ -179,6 +179,7 @@ impl NavigationTarget { .visit(doc_comments::) .visit(doc_comments::) .visit(doc_comments::) + .visit(doc_comments::) .accept(&node)? } @@ -189,6 +190,20 @@ impl NavigationTarget { // TODO: After type inference is done, add type information to improve the output let node = self.node(db)?; + fn visit_ascribed_node(node: &T, prefix: &str) -> Option + where + T: NameOwner + VisibilityOwner + TypeAscriptionOwner, + { + let mut string = visit_node(node, prefix)?; + + if let Some(type_ref) = node.ascribed_type() { + string.push_str(": "); + type_ref.syntax().text().push_to(&mut string); + } + + Some(string) + } + fn visit_node(node: &T, label: &str) -> Option where T: NameOwner + VisibilityOwner, @@ -207,8 +222,9 @@ impl NavigationTarget { .visit(|node: &ast::TraitDef| visit_node(node, "trait ")) .visit(|node: &ast::Module| visit_node(node, "mod ")) .visit(|node: &ast::TypeAliasDef| visit_node(node, "type ")) - .visit(|node: &ast::ConstDef| visit_node(node, "const ")) - .visit(|node: &ast::StaticDef| visit_node(node, "static ")) + .visit(|node: &ast::ConstDef| visit_ascribed_node(node, "const ")) + .visit(|node: &ast::StaticDef| visit_ascribed_node(node, "static ")) + .visit(|node: &ast::NamedFieldDef| visit_ascribed_node(node, "")) .accept(&node)? } } @@ -320,6 +336,66 @@ mod tests { ); } + #[test] + fn hover_shows_struct_field_info() { + // Hovering over the field when instantiating + check_hover_result( + r#" + //- /main.rs + struct Foo { + field_a: u32, + } + + fn main() { + let foo = Foo { + field_a<|>: 0, + }; + } + "#, + &["field_a: u32"], + ); + + // Hovering over the field in the definition + check_hover_result( + r#" + //- /main.rs + struct Foo { + field_a<|>: u32, + } + + fn main() { + let foo = Foo { + field_a: 0, + }; + } + "#, + &["field_a: u32"], + ); + } + + #[test] + fn hover_const_static() { + check_hover_result( + r#" + //- /main.rs + fn main() { + const foo<|>: u32 = 0; + } + "#, + &["const foo: u32"], + ); + + check_hover_result( + r#" + //- /main.rs + fn main() { + static foo<|>: u32 = 0; + } + "#, + &["static foo: u32"], + ); + } + #[test] fn hover_some() { let (analysis, position) = single_file_with_position( diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index 44d8fdf829..bcbe0d21b5 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs @@ -2,6 +2,7 @@ test_utils::marks!( inserts_parens_for_function_calls goto_definition_works_for_methods goto_definition_works_for_fields + goto_definition_works_for_named_fields call_info_bad_offset dont_complete_current_use );