Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/display.rs130
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs34
2 files changed, 129 insertions, 35 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index d960aaf99f..ae8fbe2ce6 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -34,6 +34,7 @@ use rustc_apfloat::{
ieee::{Half as f16, Quad as f128},
Float,
};
+use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use span::Edition;
use stdx::never;
@@ -87,6 +88,35 @@ pub struct HirFormatter<'a> {
omit_verbose_types: bool,
closure_style: ClosureStyle,
display_target: DisplayTarget,
+ bounds_formatting_ctx: BoundsFormattingCtx,
+}
+
+#[derive(Default)]
+enum BoundsFormattingCtx {
+ Entered {
+ /// We can have recursive bounds like the following case:
+ /// ```rust
+ /// where
+ /// T: Foo,
+ /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
+ /// ```
+ /// So, record the projection types met while formatting bounds and
+ //. prevent recursing into their bounds to avoid infinite loops.
+ projection_tys_met: FxHashSet<ProjectionTy>,
+ },
+ #[default]
+ Exited,
+}
+
+impl BoundsFormattingCtx {
+ fn contains(&mut self, proj: &ProjectionTy) -> bool {
+ match self {
+ BoundsFormattingCtx::Entered { projection_tys_met } => {
+ projection_tys_met.contains(proj)
+ }
+ BoundsFormattingCtx::Exited => false,
+ }
+ }
}
impl HirFormatter<'_> {
@@ -97,6 +127,30 @@ impl HirFormatter<'_> {
fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
+
+ fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
+ &mut self,
+ target: ProjectionTy,
+ format_bounds: F,
+ ) -> T {
+ match self.bounds_formatting_ctx {
+ BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
+ projection_tys_met.insert(target);
+ format_bounds(self)
+ }
+ BoundsFormattingCtx::Exited => {
+ let mut projection_tys_met = FxHashSet::default();
+ projection_tys_met.insert(target);
+ self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
+ let res = format_bounds(self);
+ // Since we want to prevent only the infinite recursions in bounds formatting
+ // and do not want to skip formatting of other separate bounds, clear context
+ // when exiting the formatting of outermost bounds
+ self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
+ res
+ }
+ }
+ }
}
pub trait HirDisplay {
@@ -220,6 +274,7 @@ pub trait HirDisplay {
closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
show_container_bounds: false,
+ bounds_formatting_ctx: Default::default(),
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -427,6 +482,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
display_target: self.display_target,
closure_style: self.closure_style,
show_container_bounds: self.show_container_bounds,
+ bounds_formatting_ctx: Default::default(),
})
}
@@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy {
// `<Param as Trait>::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,
+ if !f.bounds_formatting_ctx.contains(self) {
+ 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) => {
+ matches!(
+ tr.self_type_parameter(Interner).kind(Interner),
+ TyKind::Alias(_)
+ )
}
- }
- 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::<Vec<_>>();
- 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,
- );
- };
+ WhereClause::TypeOutlives(t) => {
+ matches!(t.ty.kind(Interner), TyKind::Alias(_))
+ }
+ // We shouldn't be here if these exist
+ WhereClause::AliasEq(_) => false,
+ WhereClause::LifetimeOutlives(_) => false,
+ })
+ .collect::<Vec<_>>();
+ if !bounds.is_empty() {
+ return f.format_bounds_with(self.clone(), |f| {
+ write_bounds_like_dyn_trait_with_prefix(
+ f,
+ "impl",
+ Either::Left(
+ &TyKind::Alias(AliasTy::Projection(self.clone()))
+ .intern(Interner),
+ ),
+ &bounds,
+ SizedByDefault::NotSized,
+ )
+ });
+ }
+ }
}
}
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index ab5464156f..01a1a4545c 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -1206,4 +1206,38 @@ fn f5<G: T<Assoc = ()>>(it: G) {
"#,
);
}
+
+ #[test]
+ fn regression_19007() {
+ check_types(
+ r#"
+trait Foo {
+ type Assoc;
+
+ fn foo(&self) -> Self::Assoc;
+}
+
+trait Bar {
+ type Target;
+}
+
+trait Baz<T> {}
+
+struct Struct<T: Foo> {
+ field: T,
+}
+
+impl<T> Struct<T>
+where
+ T: Foo,
+ T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
+{
+ fn f(&self) {
+ let x = self.field.foo();
+ //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
+ }
+}
+"#,
+ );
+ }
}