944: Add support for goto definition and hover on Self r=matklad a=vipentti

This fixes #943

Co-authored-by: Ville Penttinen <villem.penttinen@gmail.com>
This commit is contained in:
bors[bot] 2019-03-07 16:10:36 +00:00
commit 711b82a701
4 changed files with 190 additions and 26 deletions

View File

@ -205,19 +205,8 @@ pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> R
// TODO const/static/array length
None
}
} else if let Some(module) = ast::Module::cast(node) {
Some(module_from_declaration(db, file_id, module)?.resolver(db))
} else if let Some(_) = ast::SourceFile::cast(node) {
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
} else if let Some(s) = ast::StructDef::cast(node) {
let module = module_from_child_node(db, file_id, s.syntax())?;
Some(struct_from_module(db, module, s).resolver(db))
} else if let Some(e) = ast::EnumDef::cast(node) {
let module = module_from_child_node(db, file_id, e.syntax())?;
Some(enum_from_module(db, module, e).resolver(db))
} else {
// TODO add missing cases
None
try_get_resolver_for_node(db, file_id, node)
}
})
})
@ -236,20 +225,32 @@ pub fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNo
// TODO const/static/array length
None
}
} else if let Some(module) = ast::Module::cast(node) {
Some(module_from_declaration(db, file_id, module)?.resolver(db))
} else if let Some(_) = ast::SourceFile::cast(node) {
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
} else if let Some(s) = ast::StructDef::cast(node) {
let module = module_from_child_node(db, file_id, s.syntax())?;
Some(struct_from_module(db, module, s).resolver(db))
} else if let Some(e) = ast::EnumDef::cast(node) {
let module = module_from_child_node(db, file_id, e.syntax())?;
Some(enum_from_module(db, module, e).resolver(db))
} else {
// TODO add missing cases
None
try_get_resolver_for_node(db, file_id, node)
}
})
.unwrap_or_default()
}
fn try_get_resolver_for_node(
db: &impl HirDatabase,
file_id: FileId,
node: &SyntaxNode,
) -> Option<Resolver> {
if let Some(module) = ast::Module::cast(node) {
Some(module_from_declaration(db, file_id, module)?.resolver(db))
} else if let Some(_) = ast::SourceFile::cast(node) {
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
} else if let Some(s) = ast::StructDef::cast(node) {
let module = module_from_child_node(db, file_id, s.syntax())?;
Some(struct_from_module(db, module, s).resolver(db))
} else if let Some(e) = ast::EnumDef::cast(node) {
let module = module_from_child_node(db, file_id, e.syntax())?;
Some(enum_from_module(db, module, e).resolver(db))
} else if let Some(f) = ast::FnDef::cast(node) {
function_from_source(db, file_id, f).map(|f| f.resolver(db))
} else {
// TODO add missing cases
None
}
}

View File

@ -121,8 +121,12 @@ pub(crate) fn reference_definition(
Some(Resolution::GenericParam(..)) => {
// TODO: go to the generic param def
}
Some(Resolution::SelfType(_impl_block)) => {
// TODO: go to the implemented type
Some(Resolution::SelfType(impl_block)) => {
let ty = impl_block.target_ty(db);
if let hir::Ty::Adt { def_id, .. } = ty {
return Exact(NavigationTarget::from_adt_def(db, def_id));
}
}
None => {
// If we failed to resolve then check associated items
@ -337,6 +341,94 @@ mod tests {
"spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)",
);
}
#[test]
fn goto_definition_on_self() {
check_goto(
"
//- /lib.rs
struct Foo;
impl Foo {
pub fn new() -> Self {
Self<|> {}
}
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
check_goto(
"
//- /lib.rs
struct Foo;
impl Foo {
pub fn new() -> Self<|> {
Self {}
}
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
check_goto(
"
//- /lib.rs
enum Foo { A }
impl Foo {
pub fn new() -> Self<|> {
Foo::A
}
}
",
"Foo ENUM_DEF FileId(1) [0; 14) [5; 8)",
);
check_goto(
"
//- /lib.rs
enum Foo { A }
impl Foo {
pub fn thing(a: &Self<|>) {
}
}
",
"Foo ENUM_DEF FileId(1) [0; 14) [5; 8)",
);
}
#[test]
fn goto_definition_on_self_in_trait_impl() {
check_goto(
"
//- /lib.rs
struct Foo;
trait Make {
fn new() -> Self;
}
impl Make for Foo {
fn new() -> Self {
Self<|> {}
}
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
check_goto(
"
//- /lib.rs
struct Foo;
trait Make {
fn new() -> Self;
}
impl Make for Foo {
fn new() -> Self<|> {
Self{}
}
}
",
"Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
);
}
#[test]
fn goto_definition_works_when_used_on_definition_name_itself() {

View File

@ -557,4 +557,62 @@ mod tests {
assert_eq!(trim_markup_opt(hover.info.first()), Some("const C: u32"));
assert_eq!(hover.info.is_exact(), true);
}
#[test]
fn test_hover_self() {
let (analysis, position) = single_file_with_position(
"
struct Thing { x: u32 };
impl Thing {
fn new() -> Self {
Self<|> { x: 0 }
}
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
assert_eq!(trim_markup_opt(hover.info.first()), Some("struct Thing"));
assert_eq!(hover.info.is_exact(), true);
let (analysis, position) = single_file_with_position(
"
struct Thing { x: u32 };
impl Thing {
fn new() -> Self<|> {
Self { x: 0 }
}
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
assert_eq!(trim_markup_opt(hover.info.first()), Some("struct Thing"));
assert_eq!(hover.info.is_exact(), true);
let (analysis, position) = single_file_with_position(
"
enum Thing { A };
impl Thing {
pub fn new() -> Self<|> {
Thing::A
}
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
assert_eq!(hover.info.is_exact(), true);
let (analysis, position) = single_file_with_position(
"
enum Thing { A };
impl Thing {
pub fn thing(a: Self<|>) {
}
}
",
);
let hover = analysis.hover(position).unwrap().unwrap();
assert_eq!(trim_markup_opt(hover.info.first()), Some("enum Thing"));
assert_eq!(hover.info.is_exact(), true);
}
}

View File

@ -126,6 +126,19 @@ impl NavigationTarget {
}
}
pub(crate) fn from_adt_def(db: &RootDatabase, adt_def: hir::AdtDef) -> NavigationTarget {
match adt_def {
hir::AdtDef::Struct(s) => {
let (file_id, node) = s.source(db);
NavigationTarget::from_named(file_id.original_file(db), &*node)
}
hir::AdtDef::Enum(s) => {
let (file_id, node) = s.source(db);
NavigationTarget::from_named(file_id.original_file(db), &*node)
}
}
}
pub(crate) fn from_def(db: &RootDatabase, module_def: hir::ModuleDef) -> NavigationTarget {
match module_def {
hir::ModuleDef::Module(module) => NavigationTarget::from_module(db, module),