From 4a0bb3d7c53c2a914649087bf206d52ed5768576 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Thu, 7 Mar 2019 10:32:39 +0200 Subject: Add support for goto definition and hover on Self This fixes #943 --- crates/ra_hir/src/source_binder.rs | 49 +++++++-------- crates/ra_ide_api/src/goto_definition.rs | 96 +++++++++++++++++++++++++++++- crates/ra_ide_api/src/hover.rs | 58 ++++++++++++++++++ crates/ra_ide_api/src/navigation_target.rs | 13 ++++ 4 files changed, 190 insertions(+), 26 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index ea20cd15a..4a9921a85 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -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 { + 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 + } +} diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index dd5f9f31c..d4e10b69c 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -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() { diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 638c24e31..f14001e84 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -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); + } } diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index d806cb368..f6d7f3192 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs @@ -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), -- cgit v1.2.3