7154: Show goto type actions for Const and TypeParams r=matklad a=Veykril

Shows goto type actions for type parameters:
![Code_6hn3rowu9M](https://user-images.githubusercontent.com/3757771/103547890-42aaeb00-4ea5-11eb-8ac7-f166869af5f8.png)

Shows goto type actions for const parameters:
![Code_8UFCcbZL3z](https://user-images.githubusercontent.com/3757771/103547891-43438180-4ea5-11eb-91e8-50681e4d831e.png)

Also shows implementations for `Self`:
![Code_eQj1pWfser](https://user-images.githubusercontent.com/3757771/103547892-43438180-4ea5-11eb-9122-461f2e0fdd01.png)


Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-01-04 15:23:53 +00:00 committed by GitHub
commit 6f8af890ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 150 additions and 45 deletions

View File

@ -1276,6 +1276,18 @@ pub fn ty(self, db: &dyn HirDatabase) -> Type {
} }
} }
pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
db.generic_predicates_for_param(self.id)
.into_iter()
.filter_map(|pred| match &pred.value {
hir_ty::GenericPredicate::Implemented(trait_ref) => {
Some(Trait::from(trait_ref.trait_))
}
_ => None,
})
.collect()
}
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
let params = db.generic_defaults(self.id.parent); let params = db.generic_defaults(self.id.parent);
let local_idx = hir_ty::param_idx(db, self.id)?; let local_idx = hir_ty::param_idx(db, self.id)?;
@ -1343,6 +1355,12 @@ pub fn module(self, db: &dyn HirDatabase) -> Module {
pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
self.id.parent.into() self.id.parent.into()
} }
pub fn ty(self, db: &dyn HirDatabase) -> Type {
let def = self.id.parent;
let krate = def.module(db.upcast()).krate;
Type::new(db, krate, def, db.const_param_ty(self.id))
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

View File

@ -182,16 +182,18 @@ fn to_action(nav_target: NavigationTarget) -> HoverAction {
}) })
} }
match def { let adt = match def {
Definition::ModuleDef(it) => match it { Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action),
ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)), Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it),
ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)), Definition::SelfType(it) => it.target_ty(db).as_adt(),
ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
_ => None,
},
_ => None, _ => None,
}?;
match adt {
Adt::Struct(it) => it.try_to_nav(db),
Adt::Union(it) => it.try_to_nav(db),
Adt::Enum(it) => it.try_to_nav(db),
} }
.map(to_action)
} }
fn runnable_action( fn runnable_action(
@ -226,8 +228,6 @@ fn runnable_action(
} }
fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
match def {
Definition::Local(it) => {
let mut targets: Vec<ModuleDef> = Vec::new(); let mut targets: Vec<ModuleDef> = Vec::new();
let mut push_new_def = |item: ModuleDef| { let mut push_new_def = |item: ModuleDef| {
if !targets.contains(&item) { if !targets.contains(&item) {
@ -235,7 +235,16 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
} }
}; };
it.ty(db).walk(db, |t| { if let Definition::TypeParam(it) = def {
it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
} else {
let ty = match def {
Definition::Local(it) => it.ty(db),
Definition::ConstParam(it) => it.ty(db),
_ => return None,
};
ty.walk(db, |t| {
if let Some(adt) = t.as_adt() { if let Some(adt) = t.as_adt() {
push_new_def(adt.into()); push_new_def(adt.into());
} else if let Some(trait_) = t.as_dyn_trait() { } else if let Some(trait_) = t.as_dyn_trait() {
@ -246,25 +255,19 @@ fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
push_new_def(trait_.into()); push_new_def(trait_.into());
} }
}); });
}
let targets = targets let targets = targets
.into_iter() .into_iter()
.filter_map(|it| { .filter_map(|it| {
Some(HoverGotoTypeData { Some(HoverGotoTypeData {
mod_path: render_path( mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())),
db,
it.module(db)?,
it.name(db).map(|name| name.to_string()),
),
nav: it.try_to_nav(db)?, nav: it.try_to_nav(db)?,
}) })
}) })
.collect(); .collect();
Some(HoverAction::GoToType(targets)) Some(HoverAction::GoToType(targets))
}
_ => None,
}
} }
fn hover_markup( fn hover_markup(
@ -2174,6 +2177,25 @@ fn test_hover_enum_has_impl_action() {
); );
} }
#[test]
fn test_hover_self_has_impl_action() {
check_actions(
r#"struct foo where Self<|>:;"#,
expect![[r#"
[
Implementation(
FilePosition {
file_id: FileId(
0,
),
offset: 7,
},
),
]
"#]],
);
}
#[test] #[test]
fn test_hover_test_has_action() { fn test_hover_test_has_action() {
check_actions( check_actions(
@ -3062,6 +3084,71 @@ fn test() -> impl Foo { S {} }
); );
} }
#[test]
fn test_hover_const_param_has_goto_type_action() {
check_actions(
r#"
struct Bar;
struct Foo<const BAR: Bar>;
impl<const BAR: Bar> Foo<BAR<|>> {}
"#,
expect![[r#"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "test::Bar",
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 0..11,
focus_range: 7..10,
name: "Bar",
kind: Struct,
description: "struct Bar",
},
},
],
),
]
"#]],
);
}
#[test]
fn test_hover_type_param_has_goto_type_action() {
check_actions(
r#"
trait Foo {}
fn foo<T: Foo>(t: T<|>){}
"#,
expect![[r#"
[
GoToType(
[
HoverGotoTypeData {
mod_path: "test::Foo",
nav: NavigationTarget {
file_id: FileId(
0,
),
full_range: 0..12,
focus_range: 6..9,
name: "Foo",
kind: Trait,
description: "trait Foo",
},
},
],
),
]
"#]],
);
}
#[test] #[test]
fn hover_displays_normalized_crate_names() { fn hover_displays_normalized_crate_names() {
check( check(