@@ -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 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
+ }
90
120
}
91
121
92
122
impl HirFormatter < ' _ > {
@@ -97,6 +127,30 @@ 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 > (
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
+ }
100
154
}
101
155
102
156
pub trait HirDisplay {
@@ -220,6 +274,7 @@ pub trait HirDisplay {
220
274
closure_style : ClosureStyle :: ImplFn ,
221
275
display_target : DisplayTarget :: SourceCode { module_id, allow_opaque } ,
222
276
show_container_bounds : false ,
277
+ bounds_formatting_ctx : Default :: default ( ) ,
223
278
} ) {
224
279
Ok ( ( ) ) => { }
225
280
Err ( HirDisplayError :: FmtError ) => panic ! ( "Writing to String can't fail!" ) ,
@@ -427,6 +482,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
427
482
display_target : self . display_target ,
428
483
closure_style : self . closure_style ,
429
484
show_container_bounds : self . show_container_bounds ,
485
+ bounds_formatting_ctx : Default :: default ( ) ,
430
486
} )
431
487
}
432
488
@@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy {
479
535
// `<Param as Trait>::Assoc`
480
536
if !f. display_target . is_source_code ( ) {
481
537
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
+ )
496
554
}
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
+ }
518
578
}
519
579
}
520
580
0 commit comments