@@ -34,6 +34,7 @@ use rustc_apfloat::{
34
34
ieee:: { Half as f16, Quad as f128} ,
35
35
Float ,
36
36
} ;
37
+ use rustc_hash:: FxHashSet ;
37
38
use smallvec:: SmallVec ;
38
39
use span:: Edition ;
39
40
use stdx:: never;
@@ -87,6 +88,35 @@ pub struct HirFormatter<'a> {
87
88
omit_verbose_types : bool ,
88
89
closure_style : ClosureStyle ,
89
90
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 insert ( & mut self , proj : ProjectionTy ) -> bool {
113
+ match self {
114
+ BoundsFormattingCtx :: Entered { projection_tys_met } => {
115
+ projection_tys_met. insert ( proj. clone ( ) )
116
+ }
117
+ BoundsFormattingCtx :: Exited => true ,
118
+ }
119
+ }
90
120
}
91
121
92
122
impl HirFormatter < ' _ > {
@@ -97,6 +127,22 @@ impl HirFormatter<'_> {
97
127
fn end_location_link ( & mut self ) {
98
128
self . fmt . end_location_link ( ) ;
99
129
}
130
+
131
+ fn format_bounds_with < T , F : FnOnce ( & mut Self ) -> T > ( & mut self , format_bounds : F ) -> T {
132
+ match self . bounds_formatting_ctx {
133
+ BoundsFormattingCtx :: Entered { .. } => format_bounds ( self ) ,
134
+ BoundsFormattingCtx :: Exited => {
135
+ self . bounds_formatting_ctx =
136
+ BoundsFormattingCtx :: Entered { projection_tys_met : FxHashSet :: default ( ) } ;
137
+ let res = format_bounds ( self ) ;
138
+ // Since we want to prevent only the infinite recursions in bounds formatting
139
+ // and do not want to skip formatting of other separate bounds, clear context
140
+ // when exiting the formatting of outermost bounds
141
+ self . bounds_formatting_ctx = BoundsFormattingCtx :: Exited ;
142
+ res
143
+ }
144
+ }
145
+ }
100
146
}
101
147
102
148
pub trait HirDisplay {
@@ -220,6 +266,7 @@ pub trait HirDisplay {
220
266
closure_style : ClosureStyle :: ImplFn ,
221
267
display_target : DisplayTarget :: SourceCode { module_id, allow_opaque } ,
222
268
show_container_bounds : false ,
269
+ bounds_formatting_ctx : Default :: default ( ) ,
223
270
} ) {
224
271
Ok ( ( ) ) => { }
225
272
Err ( HirDisplayError :: FmtError ) => panic ! ( "Writing to String can't fail!" ) ,
@@ -427,6 +474,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
427
474
display_target : self . display_target ,
428
475
closure_style : self . closure_style ,
429
476
show_container_bounds : self . show_container_bounds ,
477
+ bounds_formatting_ctx : Default :: default ( ) ,
430
478
} )
431
479
}
432
480
@@ -479,42 +527,54 @@ impl HirDisplay for ProjectionTy {
479
527
// `<Param as Trait>::Assoc`
480
528
if !f. display_target . is_source_code ( ) {
481
529
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 ) ;
530
+ let res = f. format_bounds_with ( |f| {
531
+ // Avoid infinite recursion
532
+ if !f. bounds_formatting_ctx . insert ( self . clone ( ) ) {
533
+ return None ;
534
+ }
485
535
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 ,
536
+ let db = f. db ;
537
+ let id = from_placeholder_idx ( db, * idx) ;
538
+ let generics = generics ( db. upcast ( ) , id. parent ) ;
539
+
540
+ let substs = generics. placeholder_subst ( db) ;
541
+ let bounds = db
542
+ . generic_predicates ( id. parent )
543
+ . iter ( )
544
+ . map ( |pred| pred. clone ( ) . substitute ( Interner , & substs) )
545
+ . filter ( |wc| match wc. skip_binders ( ) {
546
+ WhereClause :: Implemented ( tr) => {
547
+ matches ! (
548
+ tr. self_type_parameter( Interner ) . kind( Interner ) ,
549
+ TyKind :: Alias ( _)
550
+ )
496
551
}
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
- } ;
552
+ WhereClause :: TypeOutlives ( t) => {
553
+ matches ! ( t. ty. kind( Interner ) , TyKind :: Alias ( _) )
554
+ }
555
+ // We shouldn't be here if these exist
556
+ WhereClause :: AliasEq ( _) => false ,
557
+ WhereClause :: LifetimeOutlives ( _) => false ,
558
+ } )
559
+ . collect :: < Vec < _ > > ( ) ;
560
+ if !bounds. is_empty ( ) {
561
+ Some ( write_bounds_like_dyn_trait_with_prefix (
562
+ f,
563
+ "impl" ,
564
+ Either :: Left (
565
+ & TyKind :: Alias ( AliasTy :: Projection ( self . clone ( ) ) ) . intern ( Interner ) ,
566
+ ) ,
567
+ & bounds,
568
+ SizedByDefault :: NotSized ,
569
+ ) )
570
+ } else {
571
+ None
572
+ }
573
+ } ) ;
574
+
575
+ if let Some ( res) = res {
576
+ return res;
577
+ }
518
578
}
519
579
}
520
580
0 commit comments