@@ -301,18 +301,20 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default)
301
301
{
302
302
var jobData = Jobs [ ctx . JobKey ] ;
303
303
var proj = ctx . SourceProject ;
304
- var rewriter = new Rewriter ( jobData , logger ) ;
304
+
305
+ // Rewrite phase 1
306
+ var rewriter1 = new EnumRewriterPhase1 ( jobData , logger ) ;
305
307
foreach ( var docId in proj ? . DocumentIds ?? [ ] )
306
308
{
307
- var doc =
308
- proj ! . GetDocument ( docId ) ?? throw new InvalidOperationException ( "Document missing" ) ;
309
+ var doc = proj ! . GetDocument ( docId ) ?? throw new InvalidOperationException ( "Document missing" ) ;
309
310
proj = doc . WithSyntaxRoot (
310
- rewriter . Visit ( await doc . GetSyntaxRootAsync ( ct ) ) ? . NormalizeWhitespace ( )
311
+ rewriter1 . Visit ( await doc . GetSyntaxRootAsync ( ct ) ) ? . NormalizeWhitespace ( )
311
312
?? throw new InvalidOperationException ( "Visit returned null." )
312
313
) . Project ;
313
314
}
314
315
315
- foreach ( var ( filePath , node ) in rewriter . GetNewSyntaxTrees ( ) )
316
+ // Add missing enum types
317
+ foreach ( var ( filePath , node ) in rewriter1 . GetMissingEnums ( ) )
316
318
{
317
319
proj = proj
318
320
? . AddDocument (
@@ -323,6 +325,17 @@ public async Task ExecuteAsync(IModContext ctx, CancellationToken ct = default)
323
325
. Project ;
324
326
}
325
327
328
+ // Rewrite phase 2
329
+ var rewriter2 = new EnumRewriterPhase2 ( jobData ) ;
330
+ foreach ( var docId in proj ? . DocumentIds ?? [ ] )
331
+ {
332
+ var doc = proj ! . GetDocument ( docId ) ?? throw new InvalidOperationException ( "Document missing" ) ;
333
+ proj = doc . WithSyntaxRoot (
334
+ rewriter2 . Visit ( await doc . GetSyntaxRootAsync ( ct ) ) ? . NormalizeWhitespace ( )
335
+ ?? throw new InvalidOperationException ( "Visit returned null." )
336
+ ) . Project ;
337
+ }
338
+
326
339
ctx . SourceProject = proj ;
327
340
}
328
341
@@ -1673,11 +1686,19 @@ jobKey is null
1673
1686
) ]
1674
1687
private static partial Regex EndingsNotToTrim ( ) ;
1675
1688
1676
- private class Rewriter ( JobData job , ILogger logger ) : CSharpSyntaxRewriter ( true )
1689
+ /// <summary>
1690
+ /// Extracts enum constants that are defined as fields and moves them to their actual enum types.
1691
+ /// Begins renaming FlagBits enums to Flags.
1692
+ /// </summary>
1693
+ /// <remarks>
1694
+ /// This rewriter is split into two phases because NamespaceFromSyntaxNode breaks due to
1695
+ /// the FieldDeclarationSyntax being modified.
1696
+ /// </remarks>
1697
+ private class EnumRewriterPhase1 ( JobData job , ILogger logger ) : CSharpSyntaxRewriter
1677
1698
{
1678
1699
public HashSet < string > AlreadyPresentGroups { get ; } = [ ] ;
1679
1700
1680
- public IEnumerable < ( string FilePath , SyntaxNode Node ) > GetNewSyntaxTrees ( )
1701
+ public IEnumerable < ( string FilePath , SyntaxNode Node ) > GetMissingEnums ( )
1681
1702
{
1682
1703
var results = new List < ( string FilePath , SyntaxNode Node ) > ( ) ;
1683
1704
@@ -1786,40 +1807,26 @@ private class Rewriter(JobData job, ILogger logger) : CSharpSyntaxRewriter(true)
1786
1807
}
1787
1808
}
1788
1809
1789
- // Rewrite syntax trees
1790
- // This is to ensure that these trees are processed similarly to all other trees in the project
1791
- results = results . Select ( r => ( r . FilePath , Visit ( r . Node ) ) ) . ToList ( ) ;
1792
-
1793
1810
return results ;
1794
1811
}
1795
1812
1796
1813
public override SyntaxNode ? VisitClassDeclaration ( ClassDeclarationSyntax node )
1797
1814
{
1815
+ // Remove empty classes
1798
1816
var ret = base . VisitClassDeclaration ( node ) ;
1799
1817
return ret is ClassDeclarationSyntax { Members . Count : 0 } ? null : ret ;
1800
1818
}
1801
1819
1802
1820
public override SyntaxNode ? VisitEnumDeclaration ( EnumDeclarationSyntax node )
1803
1821
{
1822
+ // Track which enums already exist
1804
1823
var identifier = node . Identifier . ToString ( ) ;
1805
1824
identifier = identifier . Replace ( "FlagBits" , "Flags" ) ;
1806
1825
1807
- if (
1808
- job . Groups . TryGetValue ( identifier , out var group )
1809
- && ! node . Ancestors ( ) . OfType < BaseTypeDeclarationSyntax > ( ) . Any ( )
1810
- )
1826
+ if ( job . Groups . TryGetValue ( identifier , out var group )
1827
+ && ! node . Ancestors ( ) . OfType < BaseTypeDeclarationSyntax > ( ) . Any ( ) )
1811
1828
{
1812
1829
AlreadyPresentGroups . Add ( identifier ) ;
1813
-
1814
- if ( group . KnownBitmask )
1815
- {
1816
- // Add [Flags] attribute
1817
- var flagsAttribute = AttributeList (
1818
- SingletonSeparatedList (
1819
- Attribute ( IdentifierName ( "Flags" ) ) ) ) ;
1820
-
1821
- node = node . WithAttributeLists ( node . AttributeLists . Add ( flagsAttribute ) ) ;
1822
- }
1823
1830
}
1824
1831
1825
1832
return base . VisitEnumDeclaration ( node . WithIdentifier ( Identifier ( identifier ) ) ) ;
@@ -1894,6 +1901,29 @@ private class Rewriter(JobData job, ILogger logger) : CSharpSyntaxRewriter(true)
1894
1901
1895
1902
return base . VisitFieldDeclaration ( node ) ;
1896
1903
}
1904
+ }
1905
+
1906
+ /// <summary>
1907
+ /// Finishes renaming FlagBits enums to Flags.
1908
+ /// Marks bitmask enums with the [Flags] attribute.
1909
+ /// </summary>
1910
+ private class EnumRewriterPhase2 ( JobData job ) : CSharpSyntaxRewriter ( true )
1911
+ {
1912
+ public override SyntaxNode ? VisitEnumDeclaration ( EnumDeclarationSyntax node )
1913
+ {
1914
+ var identifier = node . Identifier . ToString ( ) ;
1915
+ if ( job . Groups . TryGetValue ( identifier , out var group ) && group . KnownBitmask )
1916
+ {
1917
+ // Add [Flags] attribute
1918
+ var flagsAttribute = AttributeList (
1919
+ SingletonSeparatedList (
1920
+ Attribute ( IdentifierName ( "Flags" ) ) ) ) ;
1921
+
1922
+ node = node . WithAttributeLists ( node . AttributeLists . Add ( flagsAttribute ) ) ;
1923
+ }
1924
+
1925
+ return base . VisitEnumDeclaration ( node ) ;
1926
+ }
1897
1927
1898
1928
public override SyntaxNode ? VisitIdentifierName ( IdentifierNameSyntax node ) => IdentifierName ( node . Identifier . ToString ( ) . Replace ( "FlagBits" , "Flags" ) ) ;
1899
1929
}
0 commit comments