diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 62eccf4752..a2255508e3 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs @@ -5,9 +5,7 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, Edition, FileId}; use either::Either; use hir_def::{ - adt::ReprKind, - adt::StructKind, - adt::VariantData, + adt::{ReprKind, StructKind, VariantData}, builtin_type::BuiltinType, expr::{BindingAnnotation, LabelId, Pat, PatId}, import_map, @@ -31,7 +29,7 @@ use hir_expand::{ }; use hir_ty::{ autoderef, - display::{HirDisplayError, HirFormatter}, + display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, method_resolution, traits::{FnTrait, Solution, SolutionVariables}, ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, @@ -1293,6 +1291,20 @@ impl TypeParam { } } +impl HirDisplay for TypeParam { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { + write!(f, "{}", self.name(f.db))?; + let bounds = f.db.generic_predicates_for_param(self.id); + let substs = Substs::type_params(f.db, self.id.parent); + let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::>(); + if !(predicates.is_empty() || f.omit_verbose_types()) { + write!(f, ": ")?; + write_bounds_like_dyn_trait(&predicates, f)?; + } + Ok(()) + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct LifetimeParam { pub(crate) id: LifetimeParamId, diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 0e827a29e7..a54225c18b 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs @@ -595,7 +595,7 @@ impl HirDisplay for FnSig { } } -fn write_bounds_like_dyn_trait( +pub fn write_bounds_like_dyn_trait( predicates: &[GenericPredicate], f: &mut HirFormatter, ) -> Result<(), HirDisplayError> { diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 2737c900f4..61439ae534 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -370,8 +370,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option { } Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), - Definition::TypeParam(_) | Definition::ConstParam(_) => { - // FIXME: Hover for generic param + Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), + Definition::ConstParam(_) => { + // FIXME: Hover for generic const param None } }; @@ -3257,4 +3258,51 @@ fn foo() { "#]], ); } + + #[test] + fn hover_type_param() { + check( + r#" +struct Foo(T); +trait Copy {} +trait Clone {} +trait Sized {} +impl Foo> where T: Sized {} +"#, + expect![[r#" + *T* + + ```rust + T: Copy + Clone + Sized + ``` + "#]], + ); + check( + r#" +struct Foo(T); +impl Foo> {} +"#, + expect![[r#" + *T* + + ```rust + T + ``` + "#]], + ); + // lifetimes aren't being substituted yet + check( + r#" +struct Foo(T); +impl Foo> {} +"#, + expect![[r#" + *T* + + ```rust + T: {error} + ``` + "#]], + ); + } }