11use crate :: HirAnalysisDb ;
2- use hir:: { analysis_pass:: ModuleAnalysisPass , hir_def:: TopLevelMod } ;
2+ use hir:: { analysis_pass:: ModuleAnalysisPass , hir_def:: TopLevelMod , span:: DynLazySpan } ;
3+ use indexmap:: indexset;
34
45use self :: {
56 def_analysis:: {
@@ -8,7 +9,8 @@ use self::{
89 } ,
910 diagnostics:: {
1011 AdtDefDiagAccumulator , FuncDefDiagAccumulator , ImplDefDiagAccumulator ,
11- ImplTraitDefDiagAccumulator , TraitDefDiagAccumulator , TypeAliasDefDiagAccumulator ,
12+ ImplTraitDefDiagAccumulator , TraitDefDiagAccumulator , TyDiagCollection , TyLowerDiag ,
13+ TypeAliasDefDiagAccumulator ,
1214 } ,
1315 ty_def:: AdtRefId ,
1416} ;
@@ -60,12 +62,82 @@ impl<'db> ModuleAnalysisPass for TypeDefAnalysisPass<'db> {
6062 . iter ( )
6163 . map ( |c| AdtRefId :: from_contract ( self . db , * c) ) ,
6264 ) ;
65+ let mut cycles = vec ! [ ] ;
66+ let mut diags = adts
67+ . flat_map ( |adt| {
68+ cycles. append ( & mut analyze_adt:: accumulated :: <
69+ RecursiveAdtConstituentAccumulator ,
70+ > ( self . db , adt) ) ;
71+ analyze_adt:: accumulated :: < AdtDefDiagAccumulator > ( self . db , adt) . into_iter ( )
72+ } )
73+ . map ( |diag| diag. to_voucher ( ) )
74+ . collect ( ) ;
75+
76+ if cycles. is_empty ( ) {
77+ diags
78+ } else {
79+ diags. append (
80+ & mut recursive_adt_diags ( & cycles)
81+ . into_iter ( )
82+ . map ( |constituent| TyDiagCollection :: Ty ( constituent) . to_voucher ( ) )
83+ . collect ( ) ,
84+ ) ;
85+ diags
86+ }
87+ }
88+ }
89+
90+ #[ salsa:: accumulator]
91+ pub struct RecursiveAdtConstituentAccumulator ( pub ( super ) AdtRecursionConstituent ) ;
92+
93+ pub fn recursive_adt_diags ( constituents : & [ AdtRecursionConstituent ] ) -> Vec < TyLowerDiag > {
94+ let mut diags = vec ! [ ] ;
95+ let mut unified_constituents = indexset ! { } ;
96+
97+ while let Some ( mut cur) =
98+ ( 0 ..constituents. len ( ) ) . find ( |index| !unified_constituents. contains ( index) )
99+ {
100+ unified_constituents. insert ( cur) ;
101+ let mut recursion = vec ! [ cur] ;
102+
103+ while constituents[ recursion[ 0 ] ] . from . 0 != constituents[ cur] . to {
104+ if let Some ( index) = ( 0 ..constituents. len ( ) ) . find ( |index| {
105+ !unified_constituents. contains ( index)
106+ && constituents[ cur] . to == constituents[ * index] . from . 0
107+ } ) {
108+ cur = index;
109+ unified_constituents. insert ( index) ;
110+ recursion. push ( index) ;
111+ } else {
112+ break ;
113+ } ;
114+ }
115+
116+ diags. push ( TyLowerDiag :: recursive_type (
117+ recursion
118+ . iter ( )
119+ . map ( |index| constituents[ * index] . to_owned ( ) )
120+ . collect ( ) ,
121+ ) ) ;
122+ }
123+
124+ diags
125+ }
126+
127+ #[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
128+ pub struct AdtRecursionConstituent {
129+ pub from : ( AdtRefId , DynLazySpan ) ,
130+ pub to : AdtRefId ,
131+ pub field_span : DynLazySpan ,
132+ }
63133
64- adts. flat_map ( |adt| {
65- analyze_adt:: accumulated :: < AdtDefDiagAccumulator > ( self . db , adt) . into_iter ( )
66- } )
67- . map ( |diag| diag. to_voucher ( ) )
68- . collect ( )
134+ impl AdtRecursionConstituent {
135+ pub fn new ( from : ( AdtRefId , DynLazySpan ) , to : AdtRefId , field_span : DynLazySpan ) -> Self {
136+ Self {
137+ from,
138+ to,
139+ field_span,
140+ }
69141 }
70142}
71143
0 commit comments