Unnamed repository; edit this file 'description' to name the repository.
Merge #10202
10202: fix: Type param hover shows correct sized bounds. r=flodiebold a=iDawer
Closes #9949
This adds implicit `: Sized` bound to type parameters at lowering step.
Hovering on type parameter does not show it's `: Sized` bound be it set explicitly or implicitly. This is because it doesn't track that the bound was set implicitly.
### Perf
```rust
./target/rust-analyzer-baseline-3dae94bf -q analysis-stats --memory-usage .
Database loaded: 4.51s, 311minstr, 110mb (metadata 1.08s, 22minstr, 743kb; build 3.20s, 8730kinstr, -237kb)
crates: 38, mods: 770, decls: 17173, fns: 12835
Item Collection: 29.63s, 85ginstr, 372mb
exprs: 353460, ??ty: 364 (0%), ?ty: 232 (0%), !ty: 144
Inference: 118.25s, 284ginstr, 601mb
Total: 147.88s, 370ginstr, 973mb
./target/rust-analyzer-hover-ty-param-dfb15292 -q analysis-stats --memory-usage .
Database loaded: 4.53s, 311minstr, 110mb (metadata 1.10s, 22minstr, 743kb; build 3.20s, 8672kinstr, -189kb)
crates: 38, mods: 770, decls: 17173, fns: 12835
Item Collection: 29.59s, 85ginstr, 372mb
exprs: 353460, ??ty: 364 (0%), ?ty: 232 (0%), !ty: 144
Inference: 121.69s, 296ginstr, 601mb
Total: 151.28s, 382ginstr, 974mb
```
Co-authored-by: Dawer <[email protected]>
| -rw-r--r-- | crates/hir/src/display.rs | 35 | ||||
| -rw-r--r-- | crates/hir_ty/src/lower.rs | 12 | ||||
| -rw-r--r-- | crates/ide/src/hover.rs | 175 |
3 files changed, 201 insertions, 21 deletions
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index dff4372738..4ef7d399de 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -5,16 +5,19 @@ use hir_def::{ type_ref::{TypeBound, TypeRef}, AdtId, GenericDefId, }; -use hir_ty::display::{ - write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, - HirFormatter, SizedByDefault, +use hir_ty::{ + display::{ + write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError, + HirFormatter, SizedByDefault, + }, + Interner, TraitRefExt, WhereClause, }; -use hir_ty::Interner; use syntax::ast::{self, NameOwner}; use crate::{ - Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasVisibility, LifetimeParam, - Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, TypeParam, Union, Variant, + Adt, Const, ConstParam, Enum, Field, Function, GenericParam, HasCrate, HasVisibility, + LifetimeParam, Module, Static, Struct, Trait, TyBuilder, Type, TypeAlias, TypeParam, Union, + Variant, }; impl HirDisplay for Function { @@ -234,12 +237,24 @@ impl HirDisplay for GenericParam { impl HirDisplay for TypeParam { fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { write!(f, "{}", self.name(f.db))?; + if f.omit_verbose_types() { + return Ok(()); + } + let bounds = f.db.generic_predicates_for_param(self.id); let substs = TyBuilder::type_params_subst(f.db, self.id.parent); - let predicates = - bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect::<Vec<_>>(); - if !(predicates.is_empty() || f.omit_verbose_types()) { - let default_sized = SizedByDefault::Sized { anchor: self.module(f.db).krate().id }; + let predicates: Vec<_> = + bounds.iter().cloned().map(|b| b.substitute(&Interner, &substs)).collect(); + let krate = self.id.parent.krate(f.db).id; + let sized_trait = + f.db.lang_item(krate, "sized".into()).and_then(|lang_item| lang_item.as_trait()); + let has_only_sized_bound = predicates.iter().all(move |pred| match pred.skip_binders() { + WhereClause::Implemented(it) => Some(it.hir_trait_id()) == sized_trait, + _ => false, + }); + let has_only_not_sized_bound = predicates.is_empty(); + if !has_only_sized_bound || has_only_not_sized_bound { + let default_sized = SizedByDefault::Sized { anchor: krate }; write_bounds_like_dyn_trait_with_prefix(":", &predicates, default_sized, f)?; } Ok(()) diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index ad45a29346..13785e3a6c 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs @@ -1024,7 +1024,7 @@ pub(crate) fn generic_predicates_for_param_query( let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(TypeParamLoweringMode::Variable); let generics = generics(db.upcast(), param_id.parent); - resolver + let mut predicates: Vec<_> = resolver .where_predicates_in_scope() // we have to filter out all other predicates *first*, before attempting to lower them .filter(|pred| match pred { @@ -1038,7 +1038,15 @@ pub(crate) fn generic_predicates_for_param_query( WherePredicate::Lifetime { .. } => false, }) .flat_map(|pred| ctx.lower_where_predicate(pred, true).map(|p| make_binders(&generics, p))) - .collect() + .collect(); + + let subst = generics.bound_vars_subst(DebruijnIndex::INNERMOST); + let explicitly_unsized_tys = ctx.unsized_types.into_inner(); + let implicitly_sized_predicates = + implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &subst, &resolver) + .map(|p| make_binders(&generics, crate::wrap_empty_binders(p))); + predicates.extend(implicitly_sized_predicates); + predicates.into() } pub(crate) fn generic_predicates_for_param_recover( diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 62a322f976..68b75f3ffc 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -3564,20 +3564,21 @@ fn foo() { r#" //- minicore: sized struct Foo<T>(T); -trait Copy {} -trait Clone {} -impl<T: Copy + Clone> Foo<T$0> where T: Sized {} +trait TraitA {} +trait TraitB {} +impl<T: TraitA + TraitB> Foo<T$0> where T: Sized {} "#, expect![[r#" *T* ```rust - T: Copy + Clone + T: TraitA + TraitB ``` "#]], ); check( r#" +//- minicore: sized struct Foo<T>(T); impl<T> Foo<T$0> {} "#, @@ -3592,6 +3593,7 @@ impl<T> Foo<T$0> {} // lifetimes bounds arent being tracked yet check( r#" +//- minicore: sized struct Foo<T>(T); impl<T: 'static> Foo<T$0> {} "#, @@ -3606,25 +3608,180 @@ impl<T: 'static> Foo<T$0> {} } #[test] - fn hover_type_param_not_sized() { + fn hover_type_param_sized_bounds() { + // implicit `: Sized` bound check( r#" //- minicore: sized +trait Trait {} struct Foo<T>(T); -trait Copy {} -trait Clone {} -impl<T: Copy + Clone> Foo<T$0> where T: ?Sized {} +impl<T: Trait> Foo<T$0> {} +"#, + expect![[r#" + *T* + + ```rust + T: Trait + ``` + "#]], + ); + check( + r#" +//- minicore: sized +trait Trait {} +struct Foo<T>(T); +impl<T: Trait + ?Sized> Foo<T$0> {} "#, expect![[r#" *T* ```rust - T: Copy + Clone + ?Sized + T: Trait + ?Sized ``` "#]], ); } + mod type_param_sized_bounds { + use super::*; + + #[test] + fn single_implicit() { + check( + r#" +//- minicore: sized +fn foo<T$0>() {} +"#, + expect![[r#" + *T* + + ```rust + T + ``` + "#]], + ); + } + + #[test] + fn single_explicit() { + check( + r#" +//- minicore: sized +fn foo<T$0: Sized>() {} +"#, + expect![[r#" + *T* + + ```rust + T + ``` + "#]], + ); + } + + #[test] + fn single_relaxed() { + check( + r#" +//- minicore: sized +fn foo<T$0: ?Sized>() {} +"#, + expect![[r#" + *T* + + ```rust + T: ?Sized + ``` + "#]], + ); + } + + #[test] + fn multiple_implicit() { + check( + r#" +//- minicore: sized +trait Trait {} +fn foo<T$0: Trait>() {} +"#, + expect![[r#" + *T* + + ```rust + T: Trait + ``` + "#]], + ); + } + + #[test] + fn multiple_explicit() { + check( + r#" +//- minicore: sized +trait Trait {} +fn foo<T$0: Trait + Sized>() {} +"#, + expect![[r#" + *T* + + ```rust + T: Trait + ``` + "#]], + ); + } + + #[test] + fn multiple_relaxed() { + check( + r#" +//- minicore: sized +trait Trait {} +fn foo<T$0: Trait + ?Sized>() {} +"#, + expect![[r#" + *T* + + ```rust + T: Trait + ?Sized + ``` + "#]], + ); + } + + #[test] + fn mixed() { + check( + r#" +//- minicore: sized +fn foo<T$0: ?Sized + Sized + Sized>() {} +"#, + expect![[r#" + *T* + + ```rust + T + ``` + "#]], + ); + check( + r#" +//- minicore: sized +trait Trait {} +fn foo<T$0: Sized + ?Sized + Sized + Trait>() {} +"#, + expect![[r#" + *T* + + ```rust + T: Trait + ``` + "#]], + ); + } + } + #[test] fn hover_const_param() { check( |