Skip to content

Commit 2d94c69

Browse files
authored
Merge pull request #19020 from ShoyuVanilla/issues-19007
fix: Prevent infinite recursion of bounds formatting
2 parents c1db5da + 9691d22 commit 2d94c69

File tree

2 files changed

+129
-35
lines changed

2 files changed

+129
-35
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/display.rs

+95-35
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use rustc_apfloat::{
3434
ieee::{Half as f16, Quad as f128},
3535
Float,
3636
};
37+
use rustc_hash::FxHashSet;
3738
use smallvec::SmallVec;
3839
use span::Edition;
3940
use stdx::never;
@@ -87,6 +88,35 @@ pub struct HirFormatter<'a> {
8788
omit_verbose_types: bool,
8889
closure_style: ClosureStyle,
8990
display_target: DisplayTarget,
91+
bounds_formatting_ctx: BoundsFormattingCtx,
92+
}
93+
94+
#[derive(Default)]
95+
enum BoundsFormattingCtx {
96+
Entered {
97+
/// We can have recursive bounds like the following case:
98+
/// ```rust
99+
/// where
100+
/// T: Foo,
101+
/// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
102+
/// ```
103+
/// So, record the projection types met while formatting bounds and
104+
//. prevent recursing into their bounds to avoid infinite loops.
105+
projection_tys_met: FxHashSet<ProjectionTy>,
106+
},
107+
#[default]
108+
Exited,
109+
}
110+
111+
impl BoundsFormattingCtx {
112+
fn contains(&mut self, proj: &ProjectionTy) -> bool {
113+
match self {
114+
BoundsFormattingCtx::Entered { projection_tys_met } => {
115+
projection_tys_met.contains(proj)
116+
}
117+
BoundsFormattingCtx::Exited => false,
118+
}
119+
}
90120
}
91121

92122
impl HirFormatter<'_> {
@@ -97,6 +127,30 @@ impl HirFormatter<'_> {
97127
fn end_location_link(&mut self) {
98128
self.fmt.end_location_link();
99129
}
130+
131+
fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
132+
&mut self,
133+
target: ProjectionTy,
134+
format_bounds: F,
135+
) -> T {
136+
match self.bounds_formatting_ctx {
137+
BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
138+
projection_tys_met.insert(target);
139+
format_bounds(self)
140+
}
141+
BoundsFormattingCtx::Exited => {
142+
let mut projection_tys_met = FxHashSet::default();
143+
projection_tys_met.insert(target);
144+
self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
145+
let res = format_bounds(self);
146+
// Since we want to prevent only the infinite recursions in bounds formatting
147+
// and do not want to skip formatting of other separate bounds, clear context
148+
// when exiting the formatting of outermost bounds
149+
self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
150+
res
151+
}
152+
}
153+
}
100154
}
101155

102156
pub trait HirDisplay {
@@ -220,6 +274,7 @@ pub trait HirDisplay {
220274
closure_style: ClosureStyle::ImplFn,
221275
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
222276
show_container_bounds: false,
277+
bounds_formatting_ctx: Default::default(),
223278
}) {
224279
Ok(()) => {}
225280
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -427,6 +482,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
427482
display_target: self.display_target,
428483
closure_style: self.closure_style,
429484
show_container_bounds: self.show_container_bounds,
485+
bounds_formatting_ctx: Default::default(),
430486
})
431487
}
432488

@@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy {
479535
// `<Param as Trait>::Assoc`
480536
if !f.display_target.is_source_code() {
481537
if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
482-
let db = f.db;
483-
let id = from_placeholder_idx(db, *idx);
484-
let generics = generics(db.upcast(), id.parent);
485-
486-
let substs = generics.placeholder_subst(db);
487-
let bounds = db
488-
.generic_predicates(id.parent)
489-
.iter()
490-
.map(|pred| pred.clone().substitute(Interner, &substs))
491-
.filter(|wc| match wc.skip_binders() {
492-
WhereClause::Implemented(tr) => {
493-
match tr.self_type_parameter(Interner).kind(Interner) {
494-
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
495-
_ => false,
538+
if !f.bounds_formatting_ctx.contains(self) {
539+
let db = f.db;
540+
let id = from_placeholder_idx(db, *idx);
541+
let generics = generics(db.upcast(), id.parent);
542+
543+
let substs = generics.placeholder_subst(db);
544+
let bounds = db
545+
.generic_predicates(id.parent)
546+
.iter()
547+
.map(|pred| pred.clone().substitute(Interner, &substs))
548+
.filter(|wc| match wc.skip_binders() {
549+
WhereClause::Implemented(tr) => {
550+
matches!(
551+
tr.self_type_parameter(Interner).kind(Interner),
552+
TyKind::Alias(_)
553+
)
496554
}
497-
}
498-
WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
499-
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
500-
_ => false,
501-
},
502-
// We shouldn't be here if these exist
503-
WhereClause::AliasEq(_) => false,
504-
WhereClause::LifetimeOutlives(_) => false,
505-
})
506-
.collect::<Vec<_>>();
507-
if !bounds.is_empty() {
508-
return write_bounds_like_dyn_trait_with_prefix(
509-
f,
510-
"impl",
511-
Either::Left(
512-
&TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
513-
),
514-
&bounds,
515-
SizedByDefault::NotSized,
516-
);
517-
};
555+
WhereClause::TypeOutlives(t) => {
556+
matches!(t.ty.kind(Interner), TyKind::Alias(_))
557+
}
558+
// We shouldn't be here if these exist
559+
WhereClause::AliasEq(_) => false,
560+
WhereClause::LifetimeOutlives(_) => false,
561+
})
562+
.collect::<Vec<_>>();
563+
if !bounds.is_empty() {
564+
return f.format_bounds_with(self.clone(), |f| {
565+
write_bounds_like_dyn_trait_with_prefix(
566+
f,
567+
"impl",
568+
Either::Left(
569+
&TyKind::Alias(AliasTy::Projection(self.clone()))
570+
.intern(Interner),
571+
),
572+
&bounds,
573+
SizedByDefault::NotSized,
574+
)
575+
});
576+
}
577+
}
518578
}
519579
}
520580

src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs

+34
Original file line numberDiff line numberDiff line change
@@ -1203,6 +1203,40 @@ fn f5<G: T<Assoc = ()>>(it: G) {
12031203
let l = it.f();
12041204
//^ ()
12051205
}
1206+
"#,
1207+
);
1208+
}
1209+
1210+
#[test]
1211+
fn regression_19007() {
1212+
check_types(
1213+
r#"
1214+
trait Foo {
1215+
type Assoc;
1216+
1217+
fn foo(&self) -> Self::Assoc;
1218+
}
1219+
1220+
trait Bar {
1221+
type Target;
1222+
}
1223+
1224+
trait Baz<T> {}
1225+
1226+
struct Struct<T: Foo> {
1227+
field: T,
1228+
}
1229+
1230+
impl<T> Struct<T>
1231+
where
1232+
T: Foo,
1233+
T::Assoc: Baz<<T::Assoc as Bar>::Target> + Bar,
1234+
{
1235+
fn f(&self) {
1236+
let x = self.field.foo();
1237+
//^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
1238+
}
1239+
}
12061240
"#,
12071241
);
12081242
}

0 commit comments

Comments
 (0)