mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
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 <villem.penttinen@gmail.com>
This commit is contained in:
commit
84e47113e0
3 changed files with 124 additions and 3 deletions
|
@ -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(
|
||||
|
|
|
@ -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::<ast::TypeAliasDef>)
|
||||
.visit(doc_comments::<ast::ConstDef>)
|
||||
.visit(doc_comments::<ast::StaticDef>)
|
||||
.visit(doc_comments::<ast::NamedFieldDef>)
|
||||
.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<T>(node: &T, prefix: &str) -> Option<String>
|
||||
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<T>(node: &T, label: &str) -> Option<String>
|
||||
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(
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue