@@ -34,6 +34,7 @@ use rustc_apfloat::{
3434 ieee:: { Half as f16, Quad as f128} ,
3535 Float ,
3636} ;
37+ use rustc_hash:: FxHashSet ;
3738use smallvec:: SmallVec ;
3839use span:: Edition ;
3940use 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
92122impl 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
102156pub 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
0 commit comments