@@ -10,12 +10,14 @@ use hir::{
1010 HirDb , SpannedHirDb ,
1111} ;
1212use itertools:: Itertools ;
13+ use rustc_hash:: FxHashSet ;
14+
15+ use crate :: HirAnalysisDb ;
1316
1417use super :: {
1518 constraint:: PredicateId ,
16- ty_def:: { Kind , TyId } ,
19+ ty_def:: { AdtRefId , Kind , TyId } ,
1720} ;
18- use crate :: HirAnalysisDb ;
1921
2022#[ salsa:: accumulator]
2123pub struct AdtDefDiagAccumulator ( pub ( super ) TyDiagCollection ) ;
@@ -29,6 +31,8 @@ pub struct ImplDefDiagAccumulator(pub(super) TyDiagCollection);
2931pub struct FuncDefDiagAccumulator ( pub ( super ) TyDiagCollection ) ;
3032#[ salsa:: accumulator]
3133pub struct TypeAliasDefDiagAccumulator ( pub ( super ) TyDiagCollection ) ;
34+ #[ salsa:: accumulator]
35+ pub struct AdtRecursionConstituentAccumulator ( pub ( super ) AdtRecursionConstituent ) ;
3236
3337#[ derive( Debug , PartialEq , Eq , Hash , Clone , derive_more:: From ) ]
3438pub enum TyDiagCollection {
@@ -53,10 +57,7 @@ impl TyDiagCollection {
5357pub enum TyLowerDiag {
5458 ExpectedStarKind ( DynLazySpan ) ,
5559 InvalidTypeArgKind ( DynLazySpan , String ) ,
56- RecursiveType {
57- primary_span : DynLazySpan ,
58- field_span : DynLazySpan ,
59- } ,
60+ AdtRecursion ( Vec < AdtRecursionConstituent > ) ,
6061
6162 UnboundTypeAliasParam {
6263 span : DynLazySpan ,
@@ -140,11 +141,8 @@ impl TyLowerDiag {
140141 Self :: InvalidTypeArgKind ( span, msg)
141142 }
142143
143- pub ( super ) fn recursive_type ( primary_span : DynLazySpan , field_span : DynLazySpan ) -> Self {
144- Self :: RecursiveType {
145- primary_span,
146- field_span,
147- }
144+ pub ( super ) fn adt_recursion ( constituents : Vec < AdtRecursionConstituent > ) -> Self {
145+ Self :: AdtRecursion ( constituents)
148146 }
149147
150148 pub ( super ) fn unbound_type_alias_param (
@@ -249,7 +247,7 @@ impl TyLowerDiag {
249247 match self {
250248 Self :: ExpectedStarKind ( _) => 0 ,
251249 Self :: InvalidTypeArgKind ( _, _) => 1 ,
252- Self :: RecursiveType { .. } => 2 ,
250+ Self :: AdtRecursion { .. } => 2 ,
253251 Self :: UnboundTypeAliasParam { .. } => 3 ,
254252 Self :: TypeAliasCycle { .. } => 4 ,
255253 Self :: InconsistentKindBound ( _, _) => 5 ,
@@ -270,7 +268,7 @@ impl TyLowerDiag {
270268 match self {
271269 Self :: ExpectedStarKind ( _) => "expected `*` kind in this context" . to_string ( ) ,
272270 Self :: InvalidTypeArgKind ( _, _) => "invalid type argument kind" . to_string ( ) ,
273- Self :: RecursiveType { .. } => "recursive type is not allowed" . to_string ( ) ,
271+ Self :: AdtRecursion { .. } => "recursive type is not allowed" . to_string ( ) ,
274272
275273 Self :: UnboundTypeAliasParam { .. } => {
276274 "all type parameters of type alias must be given" . to_string ( )
@@ -326,22 +324,23 @@ impl TyLowerDiag {
326324 span. resolve( db) ,
327325 ) ] ,
328326
329- Self :: RecursiveType {
330- primary_span,
331- field_span,
332- } => {
333- vec ! [
334- SubDiagnostic :: new(
327+ Self :: AdtRecursion ( constituents) => {
328+ let mut diags = vec ! [ ] ;
329+
330+ for AdtRecursionConstituent { from, to } in constituents {
331+ diags. push ( SubDiagnostic :: new (
335332 LabelStyle :: Primary ,
336333 "recursive type definition" . to_string ( ) ,
337- primary_span . resolve( db) ,
338- ) ,
339- SubDiagnostic :: new(
334+ from . 1 . resolve ( db) ,
335+ ) ) ;
336+ diags . push ( SubDiagnostic :: new (
340337 LabelStyle :: Secondary ,
341338 "recursion occurs here" . to_string ( ) ,
342- field_span. resolve( db) ,
343- ) ,
344- ]
339+ to. 1 . resolve ( db) ,
340+ ) ) ;
341+ }
342+
343+ diags
345344 }
346345
347346 Self :: UnboundTypeAliasParam {
@@ -1260,3 +1259,64 @@ impl DiagnosticVoucher for ImplDiag {
12601259 CompleteDiagnostic :: new ( severity, message, sub_diags, vec ! [ ] , error_code)
12611260 }
12621261}
1262+
1263+ /// Generates diagnostics from a list of ADT recursion constituents.
1264+ pub fn adt_recursion_diags ( constituents : & [ AdtRecursionConstituent ] ) -> Vec < TyDiagCollection > {
1265+ let mut diags = vec ! [ ] ;
1266+
1267+ // `unified_constituents` tracks constituents that have been included in recursions.
1268+ // Constituents in this set cannot be used to construct other recursions.
1269+ let mut unified_constituents = FxHashSet :: default ( ) ;
1270+
1271+ // `cur` is set to the first item in `constituents` that has not been included in another recursion.
1272+ while let Some ( mut cur) =
1273+ ( 0 ..constituents. len ( ) ) . find ( |index| !unified_constituents. contains ( index) )
1274+ {
1275+ unified_constituents. insert ( cur) ;
1276+ let mut recursion = vec ! [ cur] ;
1277+
1278+ // The recursion is complete if the `from` of the first constituent is equal to the `to` of `cur`.
1279+ while constituents[ recursion[ 0 ] ] . from . 0 != constituents[ cur] . to . 0 {
1280+ // The next constituent of the recursion is found by comparing the `to` of `cur` with `from` of the candidate constituent.
1281+ if let Some ( index) = ( 0 ..constituents. len ( ) ) . find ( |index| {
1282+ !unified_constituents. contains ( index)
1283+ && constituents[ cur] . to . 0 == constituents[ * index] . from . 0
1284+ } ) {
1285+ cur = index;
1286+ unified_constituents. insert ( index) ;
1287+ recursion. push ( index) ;
1288+ } else {
1289+ break ;
1290+ } ;
1291+ }
1292+
1293+ diags. push (
1294+ TyLowerDiag :: adt_recursion (
1295+ recursion
1296+ . iter ( )
1297+ . map ( |index| constituents[ * index] . to_owned ( ) )
1298+ . collect ( ) ,
1299+ )
1300+ . into ( ) ,
1301+ ) ;
1302+ }
1303+
1304+ diags
1305+ }
1306+
1307+ /// Constituent of an ADT recursion.
1308+ ///
1309+ /// A full ADT recursion can be represented using a list of `AdtRecursionConstituent`s.
1310+ #[ derive( Clone , Debug , Eq , PartialEq , Hash ) ]
1311+ pub struct AdtRecursionConstituent {
1312+ /// The ADT definition from which the constituent originates and its name span.
1313+ pub from : ( AdtRefId , DynLazySpan ) ,
1314+ /// The ADT to which this recursion continues and the span where this occurs
1315+ pub to : ( AdtRefId , DynLazySpan ) ,
1316+ }
1317+
1318+ impl AdtRecursionConstituent {
1319+ pub fn new ( from : ( AdtRefId , DynLazySpan ) , to : ( AdtRefId , DynLazySpan ) ) -> Self {
1320+ Self { from, to }
1321+ }
1322+ }
0 commit comments