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:
bors[bot] 2019-02-27 15:59:56 +00:00
commit 84e47113e0
3 changed files with 124 additions and 3 deletions

View file

@ -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(

View file

@ -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(

View file

@ -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
);