diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index a4e052a036..3545bf7677 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -471,10 +471,55 @@ impl HirDisplay for ProjectionTy { if f.should_truncate() { return write!(f, "{TYPE_HINT_TRUNCATION}"); } - let trait_ref = self.trait_ref(f.db); + let self_ty = trait_ref.self_type_parameter(Interner); + + // if we are projection on a type parameter, check if the projection target has bounds + // itself, if so, we render them directly as `impl Bound` instead of the less useful + // `::Assoc` + if !f.display_target.is_source_code() { + if let TyKind::Placeholder(idx) = self_ty.kind(Interner) { + let db = f.db; + let id = from_placeholder_idx(db, *idx); + let generics = generics(db.upcast(), id.parent); + + let substs = generics.placeholder_subst(db); + let bounds = db + .generic_predicates(id.parent) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) + .filter(|wc| match wc.skip_binders() { + WhereClause::Implemented(tr) => { + match tr.self_type_parameter(Interner).kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj)) => proj == self, + _ => false, + } + } + WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj)) => proj == self, + _ => false, + }, + // We shouldn't be here if these exist + WhereClause::AliasEq(_) => false, + WhereClause::LifetimeOutlives(_) => false, + }) + .collect::>(); + if !bounds.is_empty() { + return write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), + ), + &bounds, + SizedByDefault::NotSized, + ); + }; + } + } + write!(f, "<")?; - trait_ref.self_type_parameter(Interner).hir_fmt(f)?; + self_ty.hir_fmt(f)?; write!(f, " as ")?; trait_ref.hir_fmt(f)?; write!( diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 8a4b9590a1..f178b4d4f0 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -1164,4 +1164,45 @@ fn main() { }"#, ); } + + #[test] + fn collapses_nested_impl_projections() { + check_types( + r#" +//- minicore: sized +trait T { + type Assoc; + fn f(self) -> Self::Assoc; +} + +trait T2 {} +trait T3 {} + +fn f(it: impl T) { + let l = it.f(); + // ^ impl T2 +} + +fn f2>(it: G) { + let l = it.f(); + //^ impl T2 + 'static +} + +fn f3(it: G) where ::Assoc: T2 { + let l = it.f(); + //^ impl T2 +} + +fn f4>>(it: G) { + let l = it.f(); + //^ impl T2 + T3<()> +} + +fn f5>(it: G) { + let l = it.f(); + //^ () +} +"#, + ); + } }