From 102b7f0f56258f115b3405f48c6ac20102e71606 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 14:23:06 +0300 Subject: [PATCH 1/9] Fix empty hover action group for a runnable. --- crates/rust-analyzer/src/main_loop/handlers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index b34b529b5ca..f3868bf1777 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -1186,7 +1186,7 @@ fn to_runnable_action( runnable: Runnable, ) -> Option { let cargo_spec = CargoTargetSpec::for_file(&snap, file_id).ok()?; - if should_skip_target(&runnable, cargo_spec.as_ref()) { + if !snap.config.hover.runnable() || should_skip_target(&runnable, cargo_spec.as_ref()) { return None; } From 4d6c6a6b1e00f61af96e16386c7f03f83f96a173 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 16:15:28 +0300 Subject: [PATCH 2/9] Fix rust-analyzer.debug.openDebugPane option --- editors/code/src/config.ts | 2 +- editors/code/src/debug.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d8f0037d4c9..22ebdf6366d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -117,7 +117,7 @@ export class Config { return { engine: this.get("debug.engine"), engineSettings: this.get("debug.engineSettings"), - openUpDebugPane: this.get("debug.openUpDebugPane"), + openDebugPane: this.get("debug.openDebugPane"), sourceFileMap: sourceFileMap }; } diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index a0c9b3ab2e6..61c12dbe074 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts @@ -82,7 +82,7 @@ async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise Date: Wed, 10 Jun 2020 21:24:36 +0300 Subject: [PATCH 3/9] Add `Go to Type Definition` hover action. --- crates/ra_hir/src/code_model.rs | 108 ++- crates/ra_hir/src/lib.rs | 8 +- crates/ra_hir_ty/src/lib.rs | 6 +- .../ra_ide/src/display/navigation_target.rs | 9 + crates/ra_ide/src/hover.rs | 811 +++++++++++++++++- crates/ra_ide/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 1 + .../rust-analyzer/src/main_loop/handlers.rs | 46 +- 8 files changed, 956 insertions(+), 35 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 1a9f6cc768b..c22eb451b96 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -26,8 +26,8 @@ use hir_ty::{ autoderef, display::{HirDisplayError, HirFormatter}, expr::ExprValidator, - method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, - TyDefId, TypeCtor, + method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, OpaqueTyId, + Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, }; use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; @@ -1380,6 +1380,87 @@ impl Type { ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, } } + + /// Returns a flattened list of all the ADTs and Traits mentioned in the type + pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec { + fn push_new_item(item: AdtOrTrait, acc: &mut Vec) { + if !acc.contains(&item) { + acc.push(item); + } + } + + fn push_bounds( + db: &dyn HirDatabase, + predicates: &[GenericPredicate], + acc: &mut Vec, + ) { + for p in predicates.iter() { + match p { + GenericPredicate::Implemented(trait_ref) => { + push_new_item(Trait::from(trait_ref.trait_).into(), acc); + walk_types(db, &trait_ref.substs, acc); + } + GenericPredicate::Projection(_) => {} + GenericPredicate::Error => (), + } + } + } + + fn walk_types(db: &dyn HirDatabase, tw: &T, acc: &mut Vec) { + tw.walk(&mut |ty| walk_type(db, ty, acc)); + } + + fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { + match ty.strip_references() { + Ty::Apply(ApplicationTy { ctor, parameters, .. }) => { + match ctor { + TypeCtor::Adt(adt_id) => push_new_item(Adt::from(*adt_id).into(), acc), + _ => (), + } + // adt params, tuples, etc... + walk_types(db, parameters, acc); + } + Ty::Dyn(predicates) => { + push_bounds(db, predicates, acc); + } + Ty::Placeholder(id) => { + let generic_params = db.generic_params(id.parent); + let param_data = &generic_params.types[id.local_id]; + match param_data.provenance { + hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { + let predicates: Vec<_> = db + .generic_predicates_for_param(*id) + .into_iter() + .map(|pred| pred.value.clone()) + .collect(); + push_bounds(db, &predicates, acc); + } + _ => (), + } + } + Ty::Opaque(opaque_ty) => { + let bounds = match opaque_ty.opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + let datas = db + .return_type_impl_traits(func) + .expect("impl trait id without data"); + let data = (*datas) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&opaque_ty.parameters) + } + }; + push_bounds(db, &bounds.value, acc); + walk_types(db, &opaque_ty.parameters, acc); + } + _ => (), + } + } + + let mut res: Vec = Vec::new(); // not a Set to preserve the order + walk_type(db, &self.ty.value, &mut res); + res + } } impl HirDisplay for Type { @@ -1488,3 +1569,26 @@ pub trait HasVisibility { vis.is_visible_from(db.upcast(), module.id) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AdtOrTrait { + Adt(Adt), + Trait(Trait), +} +impl_froms!(AdtOrTrait: Adt, Trait); + +impl AdtOrTrait { + pub fn module(self, db: &dyn HirDatabase) -> Module { + match self { + AdtOrTrait::Adt(adt) => adt.module(db), + AdtOrTrait::Trait(trait_) => trait_.module(db), + } + } + + pub fn name(self, db: &dyn HirDatabase) -> Name { + match self { + AdtOrTrait::Adt(adt) => adt.name(db), + AdtOrTrait::Trait(trait_) => trait_.name(db), + } + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3364a822f43..eded039e4cf 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -51,10 +51,10 @@ mod has_source; pub use crate::{ code_model::{ - Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, - DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs, - HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, - Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, + Adt, AdtOrTrait, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, + CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, + GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, + Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, }, has_source::HasSource, semantics::{original_range, PathResolution, Semantics, SemanticsScope}, diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2b9372b4b1b..9d4d6aaa4e8 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -1052,10 +1052,10 @@ pub enum OpaqueTyId { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Vec, + pub impl_traits: Vec, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub(crate) struct ReturnTypeImplTrait { - pub(crate) bounds: Binders>, +pub struct ReturnTypeImplTrait { + pub bounds: Binders>, } diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index c7bb1e69f8a..325b247bb72 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -321,6 +321,15 @@ impl ToNav for hir::Adt { } } +impl ToNav for hir::AdtOrTrait { + fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + match self { + hir::AdtOrTrait::Adt(adt) => adt.to_nav(db), + hir::AdtOrTrait::Trait(trait_) => trait_.to_nav(db), + } + } +} + impl ToNav for hir::AssocItem { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { match self { diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index ad78b767120..c434e5c8b69 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,8 +1,8 @@ use std::iter::once; use hir::{ - Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, - ModuleDef, ModuleSource, Semantics, + Adt, AdtOrTrait, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, + HirDisplay, Module, ModuleDef, ModuleSource, Semantics, }; use itertools::Itertools; use ra_db::SourceDatabase; @@ -24,19 +24,21 @@ pub struct HoverConfig { pub implementations: bool, pub run: bool, pub debug: bool, + pub goto_type_def: bool, } impl Default for HoverConfig { fn default() -> Self { - Self { implementations: true, run: true, debug: true } + Self { implementations: true, run: true, debug: true, goto_type_def: true } } } impl HoverConfig { - pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false }; + pub const NO_ACTIONS: Self = + Self { implementations: false, run: false, debug: false, goto_type_def: false }; pub fn any(&self) -> bool { - self.implementations || self.runnable() + self.implementations || self.runnable() || self.goto_type_def } pub fn none(&self) -> bool { @@ -52,6 +54,13 @@ impl HoverConfig { pub enum HoverAction { Runnable(Runnable), Implementaion(FilePosition), + GoToType(Vec), +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct HoverGotoTypeData { + pub mod_path: String, + pub nav: NavigationTarget, } /// Contains the results when hovering over an item @@ -138,6 +147,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option Option { + match def { + Definition::Local(it) => { + let ty = it.ty(db); + let v = ty.flattened_type_items(db); + let targets = v.into_iter() + .map(|it| HoverGotoTypeData { + mod_path: adt_or_trait_mod_path(db, &it), + nav: it.to_nav(db), + }) + .collect_vec(); + + Some(HoverAction::GoToType(targets)) + } + _ => None, + } +} + fn hover_text( docs: Option, desc: Option, @@ -248,25 +279,30 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option .map(|name| name.to_string()) } -fn determine_mod_path(db: &RootDatabase, def: &Definition) -> Option { - let mod_path = def.module(db).map(|module| { - once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) - .chain( - module - .path_to_root(db) - .into_iter() - .rev() - .map(|it| it.name(db).map(|name| name.to_string())), - ) - .chain(once(definition_owner_name(db, def))) - .flatten() - .join("::") - }); - mod_path +fn determine_mod_path(db: &RootDatabase, module: Module, name: Option) -> String { + once(db.crate_graph()[module.krate().into()].display_name.as_ref().map(ToString::to_string)) + .chain( + module + .path_to_root(db) + .into_iter() + .rev() + .map(|it| it.name(db).map(|name| name.to_string())), + ) + .chain(once(name)) + .flatten() + .join("::") +} + +fn adt_or_trait_mod_path(db: &RootDatabase, item: &AdtOrTrait) -> String { + determine_mod_path(db, item.module(db), Some(item.name(db).to_string())) +} + +fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { + def.module(db).map(|module| determine_mod_path(db, module, definition_owner_name(db, def))) } fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option { - let mod_path = determine_mod_path(db, &def); + let mod_path = definition_mod_path(db, &def); return match def { Definition::Macro(it) => { let src = it.source(db); @@ -1310,4 +1346,737 @@ fn func(foo: i32) { if true { <|>foo; }; } ] "###); } + + #[test] + fn test_hover_struct_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + struct S{ f1: u32 } + + fn main() { + let s<|>t = S{ f1:0 }; + } + ", + &["S"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..19, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 7..8, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_generic_struct_has_goto_type_actions() { + let (_, actions) = check_hover_result( + " + //- /main.rs + struct Arg(u32); + struct S{ f1: T } + + fn main() { + let s<|>t = S{ f1:Arg(0) }; + } + ", + &["S"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 17..37, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 24..25, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Arg", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..16, + name: "Arg", + kind: STRUCT_DEF, + focus_range: Some( + 7..10, + ), + container_name: None, + description: Some( + "struct Arg", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_generic_struct_has_flattened_goto_type_actions() { + let (_, actions) = check_hover_result( + " + //- /main.rs + struct Arg(u32); + struct S{ f1: T } + + fn main() { + let s<|>t = S{ f1: S{ f1: Arg(0) } }; + } + ", + &["S>"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 17..37, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 24..25, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Arg", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..16, + name: "Arg", + kind: STRUCT_DEF, + focus_range: Some( + 7..10, + ), + container_name: None, + description: Some( + "struct Arg", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_tuple_has_goto_type_actions() { + let (_, actions) = check_hover_result( + " + //- /main.rs + struct A(u32); + struct B(u32); + mod M { + pub struct C(u32); + } + + fn main() { + let s<|>t = (A(1), B(2), M::C(3) ); + } + ", + &["(A, B, C)"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "A", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..14, + name: "A", + kind: STRUCT_DEF, + focus_range: Some( + 7..8, + ), + container_name: None, + description: Some( + "struct A", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "B", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 15..29, + name: "B", + kind: STRUCT_DEF, + focus_range: Some( + 22..23, + ), + container_name: None, + description: Some( + "struct B", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "M::C", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 42..60, + name: "C", + kind: STRUCT_DEF, + focus_range: Some( + 53..54, + ), + container_name: None, + description: Some( + "pub struct C", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_return_impl_trait_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo {} + + fn foo() -> impl Foo {} + + fn main() { + let s<|>t = foo(); + } + ", + &["impl Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_generic_return_impl_trait_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo {} + struct S; + + fn foo() -> impl Foo {} + + fn main() { + let s<|>t = foo(); + } + ", + &["impl Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..15, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 16..25, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 23..24, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_arg_impl_trait_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait Foo {} + fn foo(ar<|>g: &impl Foo) {} + ", + &["&impl Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_arg_generic_impl_trait_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait Foo {} + struct S {} + fn foo(ar<|>g: &impl Foo) {} + ", + &["&impl Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..15, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 16..27, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 23..24, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_dyn_return_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo {} + struct S; + impl Foo for S {} + + struct B{} + + fn foo() -> B {} + + fn main() { + let s<|>t = foo(); + } + ", + &["B"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "B", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 41..54, + name: "B", + kind: STRUCT_DEF, + focus_range: Some( + 48..49, + ), + container_name: None, + description: Some( + "struct B", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_dyn_arg_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait Foo {} + fn foo(ar<|>g: &dyn Foo) {} + ", + &["&dyn Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_generic_dyn_arg_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait Foo {} + struct S {} + fn foo(ar<|>g: &dyn Foo) {} + ", + &["&dyn Foo"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..15, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 16..27, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 23..24, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_arg_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait ImplTrait {} + trait DynTrait {} + struct B {} + struct S {} + + fn foo(a<|>rg: &impl ImplTrait>>) {} + ", + &["&impl ImplTrait>>"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "ImplTrait", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..21, + name: "ImplTrait", + kind: TRAIT_DEF, + focus_range: Some( + 6..15, + ), + container_name: None, + description: Some( + "trait ImplTrait", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 58..69, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 65..66, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "DynTrait", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 22..42, + name: "DynTrait", + kind: TRAIT_DEF, + focus_range: Some( + 28..36, + ), + container_name: None, + description: Some( + "trait DynTrait", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "B", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 43..57, + name: "B", + kind: STRUCT_DEF, + focus_range: Some( + 50..51, + ), + container_name: None, + description: Some( + "struct B", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 51dc1f041ca..be9ab62c0bb 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -66,7 +66,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, - hover::{HoverAction, HoverConfig, HoverResult}, + hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0df7427cb97..aa2c4ae15dc 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -296,6 +296,7 @@ impl Config { set(value, "/hoverActions/implementations", &mut self.hover.implementations); set(value, "/hoverActions/run", &mut self.hover.run); set(value, "/hoverActions/debug", &mut self.hover.debug); + set(value, "/hoverActions/gotoTypeDef", &mut self.hover.goto_type_def); } else { self.hover = HoverConfig::NO_ACTIONS; } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index f3868bf1777..2d7e649d2dc 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -18,8 +18,8 @@ use lsp_types::{ TextDocumentIdentifier, Url, WorkspaceEdit, }; use ra_ide::{ - FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, - SearchScope, TextEdit, + FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, + RangeInfo, Runnable, RunnableKind, SearchScope, TextEdit, }; use ra_prof::profile; use ra_project_model::TargetKind; @@ -1150,6 +1150,23 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command { } } +fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option { + let value = if snap.config.client_caps.location_link { + let link = to_proto::location_link(snap, None, nav.clone()).ok()?; + to_value(link).ok()? + } else { + let range = FileRange { file_id: nav.file_id(), range: nav.range() }; + let location = to_proto::location(snap, range).ok()?; + to_value(location).ok()? + }; + + Some(Command { + title: nav.name().to_string(), + command: "rust-analyzer.gotoLocation".into(), + arguments: Some(vec![value]), + }) +} + fn to_command_link(command: Command, tooltip: String) -> lsp_ext::CommandLink { lsp_ext::CommandLink { tooltip: Some(tooltip), command } } @@ -1180,7 +1197,7 @@ fn show_impl_command_link( None } -fn to_runnable_action( +fn runnable_action_links( snap: &GlobalStateSnapshot, file_id: FileId, runnable: Runnable, @@ -1208,6 +1225,26 @@ fn to_runnable_action( }) } +fn goto_type_action_links( + snap: &GlobalStateSnapshot, + nav_targets: &[HoverGotoTypeData], +) -> Option { + if !snap.config.hover.goto_type_def || nav_targets.is_empty() { + return None; + } + + Some(lsp_ext::CommandLinkGroup { + title: Some("Go to ".into()), + commands: nav_targets + .iter() + .filter_map(|it| { + goto_location_command(snap, &it.nav) + .map(|cmd| to_command_link(cmd, it.mod_path.clone())) + }) + .collect(), + }) +} + fn prepare_hover_actions( snap: &GlobalStateSnapshot, file_id: FileId, @@ -1221,7 +1258,8 @@ fn prepare_hover_actions( .iter() .filter_map(|it| match it { HoverAction::Implementaion(position) => show_impl_command_link(snap, position), - HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r.clone()), + HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), + HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), }) .collect() } From 283ec13fc06dda7ec4d22955e2d2eb96aaff95fd Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 22:56:49 +0300 Subject: [PATCH 4/9] Fix type "items" order. --- crates/ra_hir/src/code_model.rs | 25 ++++++++++++++++------- crates/ra_ide/src/hover.rs | 35 +++++++++++++++++---------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index c22eb451b96..64b9a4cc3a6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -27,7 +27,7 @@ use hir_ty::{ display::{HirDisplayError, HirFormatter}, expr::ExprValidator, method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, OpaqueTyId, - Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, + Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, }; use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; @@ -1381,7 +1381,7 @@ impl Type { } } - /// Returns a flattened list of all the ADTs and Traits mentioned in the type + /// Returns a flattened list of all ADTs and Traits mentioned in the type pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec { fn push_new_item(item: AdtOrTrait, acc: &mut Vec) { if !acc.contains(&item) { @@ -1398,7 +1398,7 @@ impl Type { match p { GenericPredicate::Implemented(trait_ref) => { push_new_item(Trait::from(trait_ref.trait_).into(), acc); - walk_types(db, &trait_ref.substs, acc); + walk_substs(db, &trait_ref.substs, acc); } GenericPredicate::Projection(_) => {} GenericPredicate::Error => (), @@ -1406,8 +1406,11 @@ impl Type { } } - fn walk_types(db: &dyn HirDatabase, tw: &T, acc: &mut Vec) { - tw.walk(&mut |ty| walk_type(db, ty, acc)); + // TypeWalk::walk does not preserve items order! + fn walk_substs(db: &dyn HirDatabase, substs: &Substs, acc: &mut Vec) { + for ty in substs.iter() { + walk_type(db, ty, acc); + } } fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { @@ -1415,10 +1418,18 @@ impl Type { Ty::Apply(ApplicationTy { ctor, parameters, .. }) => { match ctor { TypeCtor::Adt(adt_id) => push_new_item(Adt::from(*adt_id).into(), acc), + TypeCtor::AssociatedType(type_alias_id) => { + let trait_id = match type_alias_id.lookup(db.upcast()).container { + AssocContainerId::TraitId(it) => it, + _ => panic!("not an associated type"), + }; + + push_new_item(Trait::from(trait_id).into(), acc); + } _ => (), } // adt params, tuples, etc... - walk_types(db, parameters, acc); + walk_substs(db, parameters, acc); } Ty::Dyn(predicates) => { push_bounds(db, predicates, acc); @@ -1451,7 +1462,7 @@ impl Type { } }; push_bounds(db, &bounds.value, acc); - walk_types(db, &opaque_ty.parameters, acc); + walk_substs(db, &opaque_ty.parameters, acc); } _ => (), } diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index c434e5c8b69..c4ee2ff796b 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -234,9 +234,10 @@ fn runnable_action( fn goto_type_action(db: &RootDatabase, def: Definition) -> Option { match def { Definition::Local(it) => { - let ty = it.ty(db); - let v = ty.flattened_type_items(db); - let targets = v.into_iter() + let targets = it + .ty(db) + .flattened_type_items(db) + .into_iter() .map(|it| HoverGotoTypeData { mod_path: adt_or_trait_mod_path(db, &it), nav: it.to_nav(db), @@ -1980,7 +1981,7 @@ fn func(foo: i32) { if true { <|>foo; }; } } #[test] - fn test_hover_arg_goto_type_action() { + fn test_hover_goto_type_action_links_order() { let (_, actions) = check_hover_result( " //- /lib.rs @@ -1988,10 +1989,10 @@ fn func(foo: i32) { if true { <|>foo; }; } trait DynTrait {} struct B {} struct S {} - - fn foo(a<|>rg: &impl ImplTrait>>) {} + + fn foo(a<|>rg: &impl ImplTrait>>>) {} ", - &["&impl ImplTrait>>"], + &["&impl ImplTrait>>>"], ); assert_debug_snapshot!(actions, @r###" @@ -2018,20 +2019,20 @@ fn func(foo: i32) { if true { <|>foo; }; } }, }, HoverGotoTypeData { - mod_path: "S", + mod_path: "B", nav: NavigationTarget { file_id: FileId( 1, ), - full_range: 58..69, - name: "S", + full_range: 43..57, + name: "B", kind: STRUCT_DEF, focus_range: Some( - 65..66, + 50..51, ), container_name: None, description: Some( - "struct S", + "struct B", ), docs: None, }, @@ -2056,20 +2057,20 @@ fn func(foo: i32) { if true { <|>foo; }; } }, }, HoverGotoTypeData { - mod_path: "B", + mod_path: "S", nav: NavigationTarget { file_id: FileId( 1, ), - full_range: 43..57, - name: "B", + full_range: 58..69, + name: "S", kind: STRUCT_DEF, focus_range: Some( - 50..51, + 65..66, ), container_name: None, description: Some( - "struct B", + "struct S", ), docs: None, }, From d4e75312ba64c2541e0d97b9ad8d6e2a735882a4 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 22:58:25 +0300 Subject: [PATCH 5/9] Add associated type test. --- .vscode/launch.json | 8 +++++- Cargo.toml | 2 +- crates/ra_ide/src/hover.rs | 57 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ca27d87836..7a2c033cbf4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -120,6 +120,12 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ], "preLaunchTask": "Pretest" - } + }, + { + "name": "(Windows) Attach", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}" + } ] } diff --git a/Cargo.toml b/Cargo.toml index 5278b5a1676..a5f68f7fe2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "crates/*", "xtask/" ] [profile.dev] # disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 0 +debug = 2 [profile.release] incremental = true diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index c4ee2ff796b..2a06006e152 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -2080,4 +2080,61 @@ fn func(foo: i32) { if true { <|>foo; }; } ] "###); } + + #[test] + fn test_hover_associated_type_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo { + type Item; + fn get(self) -> Self::Item {} + } + + struct Bar{} + struct S{} + + impl Foo for S{ + type Item = Bar; + } + + fn test() -> impl Foo { + S{} + } + + fn main() { + let s<|>t = test().get(); + } + ", + &["Foo::Item"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..62, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } } From 7e986d1504e6fd6dc1fc9b64f5fb9eac2bef952a Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 10 Jun 2020 23:01:19 +0300 Subject: [PATCH 6/9] Add `rust-analyzer.gotoLocation` command --- .vscode/launch.json | 8 +------- Cargo.toml | 2 +- editors/code/package.json | 5 +++++ editors/code/src/commands.ts | 14 ++++++++++++++ editors/code/src/config.ts | 3 +++ editors/code/src/main.ts | 1 + 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a2c033cbf4..8ca27d87836 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -120,12 +120,6 @@ "sourceMaps": true, "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ], "preLaunchTask": "Pretest" - }, - { - "name": "(Windows) Attach", - "type": "cppvsdbg", - "request": "attach", - "processId": "${command:pickProcess}" - } + } ] } diff --git a/Cargo.toml b/Cargo.toml index a5f68f7fe2c..5278b5a1676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "crates/*", "xtask/" ] [profile.dev] # disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 2 +debug = 0 [profile.release] incremental = true diff --git a/editors/code/package.json b/editors/code/package.json index 3acc375f672..e6ceb235fcf 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -510,6 +510,11 @@ "type": "boolean", "default": true }, + "rust-analyzer.hoverActions.gotoTypeDef": { + "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", + "type": "boolean", + "default": true + }, "rust-analyzer.linkedProjects": { "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects. \nElements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format", "type": "array", diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 3e9c3aa0e59..48a25495fb9 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -353,6 +353,20 @@ export function applyActionGroup(_ctx: Ctx): Cmd { }; } +export function gotoLocation(ctx: Ctx): Cmd { + return async (locationLink: lc.LocationLink) => { + const client = ctx.client; + if (client) { + const uri = client.protocol2CodeConverter.asUri(locationLink.targetUri); + let range = client.protocol2CodeConverter.asRange(locationLink.targetSelectionRange); + // collapse the range to a cursor position + range = range.with({ end: range.start }); + + await vscode.window.showTextDocument(uri, { selection: range }); + } + }; +} + export function resolveCodeAction(ctx: Ctx): Cmd { const client = ctx.client; return async (params: ra.ResolveCodeActionParams) => { diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 22ebdf6366d..9591d4fe32c 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -135,6 +135,9 @@ export class Config { return { enable: this.get("hoverActions.enable"), implementations: this.get("hoverActions.implementations"), + run: this.get("hoverActions.run"), + debug: this.get("hoverActions.debug"), + gotoTypeDef: this.get("hoverActions.gotoTypeDef"), }; } } diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index a92c676fa2d..270fbcb6448 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -100,6 +100,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); ctx.registerCommand('applyActionGroup', commands.applyActionGroup); + ctx.registerCommand('gotoLocation', commands.gotoLocation); ctx.pushCleanup(activateTaskProvider(workspaceFolder)); From 7ec0064409f90334f6b0dd61e572a65702702985 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 11 Jun 2020 14:41:42 +0300 Subject: [PATCH 7/9] Remove AdtOrTrait --- crates/ra_hir/src/code_model.rs | 51 ++++++++----------- crates/ra_hir/src/lib.rs | 8 +-- .../ra_ide/src/display/navigation_target.rs | 9 ---- crates/ra_ide/src/hover.rs | 21 +++++--- 4 files changed, 39 insertions(+), 50 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 64b9a4cc3a6..5137a16e64c 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -186,6 +186,22 @@ impl ModuleDef { module.visibility_of(db, self) } + + pub fn name(self, db: &dyn HirDatabase) -> Option { + match self { + ModuleDef::Adt(it) => Some(it.name(db)), + ModuleDef::Trait(it) => Some(it.name(db)), + ModuleDef::Function(it) => Some(it.name(db)), + ModuleDef::EnumVariant(it) => Some(it.name(db)), + ModuleDef::TypeAlias(it) => Some(it.name(db)), + + ModuleDef::Module(it) => it.name(db), + ModuleDef::Const(it) => it.name(db), + ModuleDef::Static(it) => it.name(db), + + ModuleDef::BuiltinType(it) => Some(it.as_name()), + } + } } pub use hir_def::{ @@ -1382,8 +1398,8 @@ impl Type { } /// Returns a flattened list of all ADTs and Traits mentioned in the type - pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec { - fn push_new_item(item: AdtOrTrait, acc: &mut Vec) { + pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec { + fn push_new_item(item: ModuleDef, acc: &mut Vec) { if !acc.contains(&item) { acc.push(item); } @@ -1392,7 +1408,7 @@ impl Type { fn push_bounds( db: &dyn HirDatabase, predicates: &[GenericPredicate], - acc: &mut Vec, + acc: &mut Vec, ) { for p in predicates.iter() { match p { @@ -1407,13 +1423,13 @@ impl Type { } // TypeWalk::walk does not preserve items order! - fn walk_substs(db: &dyn HirDatabase, substs: &Substs, acc: &mut Vec) { + fn walk_substs(db: &dyn HirDatabase, substs: &Substs, acc: &mut Vec) { for ty in substs.iter() { walk_type(db, ty, acc); } } - fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { + fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { match ty.strip_references() { Ty::Apply(ApplicationTy { ctor, parameters, .. }) => { match ctor { @@ -1468,7 +1484,7 @@ impl Type { } } - let mut res: Vec = Vec::new(); // not a Set to preserve the order + let mut res: Vec = Vec::new(); // not a Set to preserve the order walk_type(db, &self.ty.value, &mut res); res } @@ -1580,26 +1596,3 @@ pub trait HasVisibility { vis.is_visible_from(db.upcast(), module.id) } } - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum AdtOrTrait { - Adt(Adt), - Trait(Trait), -} -impl_froms!(AdtOrTrait: Adt, Trait); - -impl AdtOrTrait { - pub fn module(self, db: &dyn HirDatabase) -> Module { - match self { - AdtOrTrait::Adt(adt) => adt.module(db), - AdtOrTrait::Trait(trait_) => trait_.module(db), - } - } - - pub fn name(self, db: &dyn HirDatabase) -> Name { - match self { - AdtOrTrait::Adt(adt) => adt.name(db), - AdtOrTrait::Trait(trait_) => trait_.name(db), - } - } -} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index eded039e4cf..3364a822f43 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -51,10 +51,10 @@ mod has_source; pub use crate::{ code_model::{ - Adt, AdtOrTrait, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, - CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, - GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, - Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, + Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, + DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs, + HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, + Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, }, has_source::HasSource, semantics::{original_range, PathResolution, Semantics, SemanticsScope}, diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs index 325b247bb72..c7bb1e69f8a 100644 --- a/crates/ra_ide/src/display/navigation_target.rs +++ b/crates/ra_ide/src/display/navigation_target.rs @@ -321,15 +321,6 @@ impl ToNav for hir::Adt { } } -impl ToNav for hir::AdtOrTrait { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { - match self { - hir::AdtOrTrait::Adt(adt) => adt.to_nav(db), - hir::AdtOrTrait::Trait(trait_) => trait_.to_nav(db), - } - } -} - impl ToNav for hir::AssocItem { fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { match self { diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 2a06006e152..045713519e5 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -1,8 +1,8 @@ use std::iter::once; use hir::{ - Adt, AdtOrTrait, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, - HirDisplay, Module, ModuleDef, ModuleSource, Semantics, + Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay, + Module, ModuleDef, ModuleSource, Semantics, }; use itertools::Itertools; use ra_db::SourceDatabase; @@ -13,7 +13,9 @@ use ra_ide_db::{ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; use crate::{ - display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, + display::{ + macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav, TryToNav, + }, runnables::runnable, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, }; @@ -238,9 +240,11 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option { .ty(db) .flattened_type_items(db) .into_iter() - .map(|it| HoverGotoTypeData { - mod_path: adt_or_trait_mod_path(db, &it), - nav: it.to_nav(db), + .filter_map(|it| { + Some(HoverGotoTypeData { + mod_path: mod_path(db, &it)?, + nav: it.try_to_nav(db)?, + }) }) .collect_vec(); @@ -294,8 +298,9 @@ fn determine_mod_path(db: &RootDatabase, module: Module, name: Option) - .join("::") } -fn adt_or_trait_mod_path(db: &RootDatabase, item: &AdtOrTrait) -> String { - determine_mod_path(db, item.module(db), Some(item.name(db).to_string())) +// returns None only for ModuleDef::BuiltinType +fn mod_path(db: &RootDatabase, item: &ModuleDef) -> Option { + Some(determine_mod_path(db, item.module(db)?, item.name(db).map(|name| name.to_string()))) } fn definition_mod_path(db: &RootDatabase, def: &Definition) -> Option { From 4b07c1e77515ae9198aae6275700aacd43181b50 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 11 Jun 2020 20:17:32 +0300 Subject: [PATCH 8/9] Add Type::walk method --- crates/ra_hir/src/code_model.rs | 138 +++++++++++++++----------------- crates/ra_hir_ty/src/lib.rs | 52 ++++++++++++ crates/ra_ide/src/hover.rs | 25 +++++- 3 files changed, 136 insertions(+), 79 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5137a16e64c..d0a8199a643 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -26,8 +26,8 @@ use hir_ty::{ autoderef, display::{HirDisplayError, HirFormatter}, expr::ExprValidator, - method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, OpaqueTyId, - Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, + method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, TraitRef, + Ty, TyDefId, TypeCtor, }; use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; @@ -1375,6 +1375,18 @@ impl Type { Some(adt.into()) } + pub fn as_dyn_trait(&self) -> Option { + self.ty.value.dyn_trait().map(Into::into) + } + + pub fn as_impl_trait(&self, db: &dyn HirDatabase) -> Option { + self.ty.value.impl_trait_ref(db).map(|it| it.trait_.into()) + } + + pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option { + self.ty.value.associated_type_parent_trait(db).map(Into::into) + } + // FIXME: provide required accessors such that it becomes implementable from outside. pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { match (&self.ty.value, &other.ty.value) { @@ -1397,96 +1409,72 @@ impl Type { } } - /// Returns a flattened list of all ADTs and Traits mentioned in the type - pub fn flattened_type_items(&self, db: &dyn HirDatabase) -> Vec { - fn push_new_item(item: ModuleDef, acc: &mut Vec) { - if !acc.contains(&item) { - acc.push(item); - } - } - - fn push_bounds( - db: &dyn HirDatabase, - predicates: &[GenericPredicate], - acc: &mut Vec, - ) { - for p in predicates.iter() { - match p { - GenericPredicate::Implemented(trait_ref) => { - push_new_item(Trait::from(trait_ref.trait_).into(), acc); - walk_substs(db, &trait_ref.substs, acc); - } - GenericPredicate::Projection(_) => {} - GenericPredicate::Error => (), - } - } - } - + pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { // TypeWalk::walk does not preserve items order! - fn walk_substs(db: &dyn HirDatabase, substs: &Substs, acc: &mut Vec) { + fn walk_substs(db: &dyn HirDatabase, substs: &Substs, cb: &mut impl FnMut(Type)) { for ty in substs.iter() { - walk_type(db, ty, acc); + walk_ty(db, ty, cb); } } - fn walk_type(db: &dyn HirDatabase, ty: &Ty, acc: &mut Vec) { - match ty.strip_references() { - Ty::Apply(ApplicationTy { ctor, parameters, .. }) => { - match ctor { - TypeCtor::Adt(adt_id) => push_new_item(Adt::from(*adt_id).into(), acc), - TypeCtor::AssociatedType(type_alias_id) => { - let trait_id = match type_alias_id.lookup(db.upcast()).container { - AssocContainerId::TraitId(it) => it, - _ => panic!("not an associated type"), - }; + fn walk_trait( + db: &dyn HirDatabase, + ty: Ty, + trait_ref: &TraitRef, + cb: &mut impl FnMut(Type), + ) { + let def_db: &dyn DefDatabase = db.upcast(); + let resolver = trait_ref.trait_.resolver(def_db); + let krate = trait_ref.trait_.lookup(def_db).container.module(def_db).krate; + cb(Type::new_with_resolver_inner(db, krate, &resolver, ty)); + walk_substs(db, &trait_ref.substs, cb); + } - push_new_item(Trait::from(trait_id).into(), acc); + fn walk_ty(db: &dyn HirDatabase, ty: &Ty, cb: &mut impl FnMut(Type)) { + let def_db: &dyn DefDatabase = db.upcast(); + let ty = ty.strip_references(); + match ty { + Ty::Apply(ApplicationTy { ctor, parameters }) => { + match ctor { + TypeCtor::Adt(adt) => { + cb(Type::from_def(db, adt.module(def_db).krate, *adt)); + } + TypeCtor::AssociatedType(_) => { + if let Some(trait_id) = ty.associated_type_parent_trait(db) { + let resolver = trait_id.resolver(def_db); + let krate = trait_id.lookup(def_db).container.module(def_db).krate; + cb(Type::new_with_resolver_inner(db, krate, &resolver, ty.clone())); + } } _ => (), } + // adt params, tuples, etc... - walk_substs(db, parameters, acc); - } - Ty::Dyn(predicates) => { - push_bounds(db, predicates, acc); - } - Ty::Placeholder(id) => { - let generic_params = db.generic_params(id.parent); - let param_data = &generic_params.types[id.local_id]; - match param_data.provenance { - hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { - let predicates: Vec<_> = db - .generic_predicates_for_param(*id) - .into_iter() - .map(|pred| pred.value.clone()) - .collect(); - push_bounds(db, &predicates, acc); - } - _ => (), - } + walk_substs(db, parameters, cb); } Ty::Opaque(opaque_ty) => { - let bounds = match opaque_ty.opaque_ty_id { - OpaqueTyId::ReturnTypeImplTrait(func, idx) => { - let datas = db - .return_type_impl_traits(func) - .expect("impl trait id without data"); - let data = (*datas) - .as_ref() - .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); - data.clone().subst(&opaque_ty.parameters) - } - }; - push_bounds(db, &bounds.value, acc); - walk_substs(db, &opaque_ty.parameters, acc); + if let Some(trait_ref) = ty.impl_trait_ref(db) { + walk_trait(db, ty.clone(), &trait_ref, cb); + } + + walk_substs(db, &opaque_ty.parameters, cb); } + Ty::Placeholder(_) => { + if let Some(trait_ref) = ty.impl_trait_ref(db) { + walk_trait(db, ty.clone(), &trait_ref, cb); + } + } + Ty::Dyn(_) => { + if let Some(trait_ref) = ty.dyn_trait_ref() { + walk_trait(db, ty.clone(), trait_ref, cb); + } + } + _ => (), } } - let mut res: Vec = Vec::new(); // not a Set to preserve the order - walk_type(db, &self.ty.value, &mut res); - res + walk_ty(db, &self.ty.value, &mut cb); } } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 9d4d6aaa4e8..25ab9d87214 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -877,6 +877,58 @@ impl Ty { _ => None, } } + + pub fn impl_trait_ref(&self, db: &dyn HirDatabase) -> Option { + match self { + Ty::Opaque(opaque_ty) => { + let predicates = match opaque_ty.opaque_ty_id { + OpaqueTyId::ReturnTypeImplTrait(func, idx) => { + db.return_type_impl_traits(func).map(|it| { + let data = (*it) + .as_ref() + .map(|rpit| rpit.impl_traits[idx as usize].bounds.clone()); + data.clone().subst(&opaque_ty.parameters) + }) + } + }; + + predicates.and_then(|it| { + it.value.iter().find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.clone()), + _ => None, + }) + }) + } + Ty::Placeholder(id) => { + let generic_params = db.generic_params(id.parent); + let param_data = &generic_params.types[id.local_id]; + match param_data.provenance { + hir_def::generics::TypeParamProvenance::ArgumentImplTrait => db + .generic_predicates_for_param(*id) + .into_iter() + .map(|pred| pred.value.clone()) + .find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.clone()), + _ => None, + }), + _ => None, + } + } + _ => None, + } + } + + pub fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option { + match self { + Ty::Apply(ApplicationTy { ctor: TypeCtor::AssociatedType(type_alias_id), .. }) => { + match type_alias_id.lookup(db.upcast()).container { + AssocContainerId::TraitId(trait_id) => Some(trait_id), + _ => None, + } + } + _ => None, + } + } } /// This allows walking structures that contain types to do something with those diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 045713519e5..c2909e20017 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -236,9 +236,26 @@ fn runnable_action( fn goto_type_action(db: &RootDatabase, def: Definition) -> Option { match def { Definition::Local(it) => { - let targets = it - .ty(db) - .flattened_type_items(db) + let mut targets: Vec = Vec::new(); + let mut push_new_def = |item: ModuleDef| { + if !targets.contains(&item) { + targets.push(item); + } + }; + + it.ty(db).walk(db, |t| { + if let Some(adt) = t.as_adt() { + push_new_def(adt.into()); + } else if let Some(trait_) = t.as_dyn_trait() { + push_new_def(trait_.into()); + } else if let Some(trait_) = t.as_impl_trait(db) { + push_new_def(trait_.into()); + } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { + push_new_def(trait_.into()); + } + }); + + let targets = targets .into_iter() .filter_map(|it| { Some(HoverGotoTypeData { @@ -246,7 +263,7 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option { nav: it.try_to_nav(db)?, }) }) - .collect_vec(); + .collect(); Some(HoverAction::GoToType(targets)) } From 022fbefffad0d7c402ac5607457f2828decb2188 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 11 Jun 2020 23:06:58 +0300 Subject: [PATCH 9/9] Apply suggestions from code review --- crates/ra_hir/src/code_model.rs | 85 ++++++----- crates/ra_hir_ty/src/lib.rs | 53 ++++--- crates/ra_ide/src/hover.rs | 255 +++++++++++++++++++++++++++++++- 3 files changed, 328 insertions(+), 65 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index d0a8199a643..ffd5278ecb6 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -26,8 +26,8 @@ use hir_ty::{ autoderef, display::{HirDisplayError, HirFormatter}, expr::ExprValidator, - method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, TraitRef, - Ty, TyDefId, TypeCtor, + method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, + TraitEnvironment, Ty, TyDefId, TypeCtor, }; use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; @@ -1379,8 +1379,17 @@ impl Type { self.ty.value.dyn_trait().map(Into::into) } - pub fn as_impl_trait(&self, db: &dyn HirDatabase) -> Option { - self.ty.value.impl_trait_ref(db).map(|it| it.trait_.into()) + pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option> { + self.ty.value.impl_trait_bounds(db).map(|it| { + it.into_iter() + .filter_map(|pred| match pred { + hir_ty::GenericPredicate::Implemented(trait_ref) => { + Some(Trait::from(trait_ref.trait_)) + } + _ => None, + }) + .collect() + }) } pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option { @@ -1410,71 +1419,77 @@ impl Type { } pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { - // TypeWalk::walk does not preserve items order! - fn walk_substs(db: &dyn HirDatabase, substs: &Substs, cb: &mut impl FnMut(Type)) { + // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. + // We need a different order here. + + fn walk_substs( + db: &dyn HirDatabase, + type_: &Type, + substs: &Substs, + cb: &mut impl FnMut(Type), + ) { for ty in substs.iter() { - walk_ty(db, ty, cb); + walk_type(db, &type_.derived(ty.clone()), cb); } } - fn walk_trait( + fn walk_bounds( db: &dyn HirDatabase, - ty: Ty, - trait_ref: &TraitRef, + type_: &Type, + bounds: &[GenericPredicate], cb: &mut impl FnMut(Type), ) { - let def_db: &dyn DefDatabase = db.upcast(); - let resolver = trait_ref.trait_.resolver(def_db); - let krate = trait_ref.trait_.lookup(def_db).container.module(def_db).krate; - cb(Type::new_with_resolver_inner(db, krate, &resolver, ty)); - walk_substs(db, &trait_ref.substs, cb); + for pred in bounds { + match pred { + GenericPredicate::Implemented(trait_ref) => { + cb(type_.clone()); + walk_substs(db, type_, &trait_ref.substs, cb); + } + _ => (), + } + } } - fn walk_ty(db: &dyn HirDatabase, ty: &Ty, cb: &mut impl FnMut(Type)) { - let def_db: &dyn DefDatabase = db.upcast(); - let ty = ty.strip_references(); + fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { + let ty = type_.ty.value.strip_references(); match ty { Ty::Apply(ApplicationTy { ctor, parameters }) => { match ctor { - TypeCtor::Adt(adt) => { - cb(Type::from_def(db, adt.module(def_db).krate, *adt)); + TypeCtor::Adt(_) => { + cb(type_.derived(ty.clone())); } TypeCtor::AssociatedType(_) => { - if let Some(trait_id) = ty.associated_type_parent_trait(db) { - let resolver = trait_id.resolver(def_db); - let krate = trait_id.lookup(def_db).container.module(def_db).krate; - cb(Type::new_with_resolver_inner(db, krate, &resolver, ty.clone())); + if let Some(_) = ty.associated_type_parent_trait(db) { + cb(type_.derived(ty.clone())); } } _ => (), } // adt params, tuples, etc... - walk_substs(db, parameters, cb); + walk_substs(db, type_, parameters, cb); } Ty::Opaque(opaque_ty) => { - if let Some(trait_ref) = ty.impl_trait_ref(db) { - walk_trait(db, ty.clone(), &trait_ref, cb); + if let Some(bounds) = ty.impl_trait_bounds(db) { + walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); } - walk_substs(db, &opaque_ty.parameters, cb); + walk_substs(db, type_, &opaque_ty.parameters, cb); } Ty::Placeholder(_) => { - if let Some(trait_ref) = ty.impl_trait_ref(db) { - walk_trait(db, ty.clone(), &trait_ref, cb); + if let Some(bounds) = ty.impl_trait_bounds(db) { + walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); } } - Ty::Dyn(_) => { - if let Some(trait_ref) = ty.dyn_trait_ref() { - walk_trait(db, ty.clone(), trait_ref, cb); - } + Ty::Dyn(bounds) => { + walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); } _ => (), } } - walk_ty(db, &self.ty.value, &mut cb); + walk_type(db, self, &mut cb); } } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 25ab9d87214..f2223232492 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -73,6 +73,7 @@ pub use lower::{ pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; pub use chalk_ir::{BoundVar, DebruijnIndex}; +use itertools::Itertools; /// A type constructor or type name: this might be something like the primitive /// type `bool`, a struct like `Vec`, or things like function pointers or @@ -815,6 +816,11 @@ impl Ty { } } + /// If this is a `dyn Trait`, returns that trait. + pub fn dyn_trait(&self) -> Option { + self.dyn_trait_ref().map(|it| it.trait_) + } + fn builtin_deref(&self) -> Option { match self { Ty::Apply(a_ty) => match a_ty.ctor { @@ -867,18 +873,7 @@ impl Ty { } } - /// If this is a `dyn Trait`, returns that trait. - pub fn dyn_trait(&self) -> Option { - match self { - Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.trait_), - _ => None, - }), - _ => None, - } - } - - pub fn impl_trait_ref(&self, db: &dyn HirDatabase) -> Option { + pub fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { match self { Ty::Opaque(opaque_ty) => { let predicates = match opaque_ty.opaque_ty_id { @@ -892,25 +887,21 @@ impl Ty { } }; - predicates.and_then(|it| { - it.value.iter().find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.clone()), - _ => None, - }) - }) + predicates.map(|it| it.value) } Ty::Placeholder(id) => { let generic_params = db.generic_params(id.parent); let param_data = &generic_params.types[id.local_id]; match param_data.provenance { - hir_def::generics::TypeParamProvenance::ArgumentImplTrait => db - .generic_predicates_for_param(*id) - .into_iter() - .map(|pred| pred.value.clone()) - .find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.clone()), - _ => None, - }), + hir_def::generics::TypeParamProvenance::ArgumentImplTrait => { + let predicates = db + .generic_predicates_for_param(*id) + .into_iter() + .map(|pred| pred.value.clone()) + .collect_vec(); + + Some(predicates) + } _ => None, } } @@ -926,6 +917,12 @@ impl Ty { _ => None, } } + Ty::Projection(projection_ty) => { + match projection_ty.associated_ty.lookup(db.upcast()).container { + AssocContainerId::TraitId(trait_id) => Some(trait_id), + _ => None, + } + } _ => None, } } @@ -1104,10 +1101,10 @@ pub enum OpaqueTyId { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct ReturnTypeImplTraits { - pub impl_traits: Vec, + pub(crate) impl_traits: Vec, } #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub(crate) struct ReturnTypeImplTrait { pub bounds: Binders>, } diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index c2909e20017..d870e4cbce6 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -248,8 +248,8 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option { push_new_def(adt.into()); } else if let Some(trait_) = t.as_dyn_trait() { push_new_def(trait_.into()); - } else if let Some(trait_) = t.as_impl_trait(db) { - push_new_def(trait_.into()); + } else if let Some(traits) = t.as_impl_traits(db) { + traits.into_iter().for_each(|it| push_new_def(it.into())); } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { push_new_def(trait_.into()); } @@ -1734,6 +1734,176 @@ fn func(foo: i32) { if true { <|>foo; }; } "###); } + #[test] + fn test_hover_return_impl_traits_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo {} + trait Bar {} + + fn foo() -> impl Foo + Bar {} + + fn main() { + let s<|>t = foo(); + } + ", + &["impl Foo + Bar"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Bar", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 13..25, + name: "Bar", + kind: TRAIT_DEF, + focus_range: Some( + 19..22, + ), + container_name: None, + description: Some( + "trait Bar", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + + #[test] + fn test_hover_generic_return_impl_traits_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /main.rs + trait Foo {} + trait Bar {} + struct S1 {} + struct S2 {} + + fn foo() -> impl Foo + Bar {} + + fn main() { + let s<|>t = foo(); + } + ", + &["impl Foo + Bar"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..15, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Bar", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 16..31, + name: "Bar", + kind: TRAIT_DEF, + focus_range: Some( + 22..25, + ), + container_name: None, + description: Some( + "trait Bar", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S1", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 32..44, + name: "S1", + kind: STRUCT_DEF, + focus_range: Some( + 39..41, + ), + container_name: None, + description: Some( + "struct S1", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S2", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 45..57, + name: "S2", + kind: STRUCT_DEF, + focus_range: Some( + 52..54, + ), + container_name: None, + description: Some( + "struct S2", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + #[test] fn test_hover_arg_impl_trait_has_goto_type_action() { let (_, actions) = check_hover_result( @@ -1774,6 +1944,87 @@ fn func(foo: i32) { if true { <|>foo; }; } "###); } + #[test] + fn test_hover_arg_impl_traits_has_goto_type_action() { + let (_, actions) = check_hover_result( + " + //- /lib.rs + trait Foo {} + trait Bar {} + struct S{} + + fn foo(ar<|>g: &impl Foo + Bar) {} + ", + &["&impl Foo + Bar"], + ); + assert_debug_snapshot!(actions, + @r###" + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "Foo", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 0..12, + name: "Foo", + kind: TRAIT_DEF, + focus_range: Some( + 6..9, + ), + container_name: None, + description: Some( + "trait Foo", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "Bar", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 13..28, + name: "Bar", + kind: TRAIT_DEF, + focus_range: Some( + 19..22, + ), + container_name: None, + description: Some( + "trait Bar", + ), + docs: None, + }, + }, + HoverGotoTypeData { + mod_path: "S", + nav: NavigationTarget { + file_id: FileId( + 1, + ), + full_range: 29..39, + name: "S", + kind: STRUCT_DEF, + focus_range: Some( + 36..37, + ), + container_name: None, + description: Some( + "struct S", + ), + docs: None, + }, + }, + ], + ), + ] + "###); + } + #[test] fn test_hover_arg_generic_impl_trait_has_goto_type_action() { let (_, actions) = check_hover_result(