@@ -56,23 +56,23 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct =
5656 {
5757 await base . ExecuteAsync ( ctx , ct ) ;
5858
59- var proj = ctx . SourceProject ;
60- if ( proj == null )
59+ var project = ctx . SourceProject ;
60+ if ( project == null )
6161 {
6262 return ;
6363 }
6464
65- var compilation = await proj . GetCompilationAsync ( ct ) ;
65+ var compilation = await project . GetCompilationAsync ( ct ) ;
6666 if ( compilation == null )
6767 {
68- return ;
68+ throw new InvalidOperationException ( "Failed to get compilation" ) ;
6969 }
7070
7171 var cfg = config . Get ( ctx . JobKey ) ;
7272
7373 // Phase 1. Gather data before modifying
7474 // Find handle documents
75- var handleTypeDiscoverer = new HandleTypeDiscoverer ( proj , compilation , ct ) ;
75+ var handleTypeDiscoverer = new HandleTypeDiscoverer ( project , compilation , ct ) ;
7676 var handleTypes = await handleTypeDiscoverer . GetHandleTypesAsync ( ) ;
7777
7878 // Store handle document IDs for later
@@ -88,24 +88,37 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct =
8888 }
8989
9090 var declaringSyntaxReference = handleTypeSymbol . DeclaringSyntaxReferences . Single ( ) ;
91- var documentId = proj . GetDocumentId ( declaringSyntaxReference . SyntaxTree ) ;
91+ var documentId = project . GetDocumentId ( declaringSyntaxReference . SyntaxTree ) ;
9292 if ( documentId != null )
9393 {
9494 handleTypeDocumentIds . Add ( ( handleTypeSymbol . Name , documentId ) ) ;
9595 }
9696 }
9797
98+ // Get fully qualified metadata names for each handle type
99+ // This is because symbols are invalidated after modifying the project they come from
100+ // We use these names to restore the symbols
101+ // TODO: This actually requires rewriting the fully qualified metadata names since the names will be different after the rename below. AHHHH...
102+ // Rewriting RenameAllAsync to allow providing a list of CSharpSyntaxRewriters might work best
103+ var handleTypeMetadataNames = handleTypes
104+ . Select ( t => {
105+ var ns = t . NamespaceFromSymbol ( ) ;
106+ var name = string . IsNullOrEmpty ( ns ) ? t . Name : $ "{ ns } .{ t . Name } ";
107+
108+ return name . Replace ( t . Name , $ "{ t . Name } Handle") ;
109+ } ) ;
110+
98111 // Phase 2. Modify project after gathering data
99112 // Add -Handle suffix
100- ctx . SourceProject = proj ;
113+ ctx . SourceProject = project ;
101114 await NameUtils . RenameAllAsync ( ctx , logger , handleTypes . Select ( t => ( ( ISymbol ) t , $ "{ t . Name } Handle") ) , ct ) ;
102- proj = ctx . SourceProject ;
115+ project = ctx . SourceProject ;
103116
104117 // Use document IDs from earlier
105118 var handleTypeRewriter = new HandleTypeRewriter ( cfg . UseDSL ) ;
106119 foreach ( var ( originalName , documentId ) in handleTypeDocumentIds )
107120 {
108- var document = proj . GetDocument ( documentId ) ?? throw new InvalidOperationException ( "Failed to find document" ) ;
121+ var document = project . GetDocument ( documentId ) ?? throw new InvalidOperationException ( "Failed to find document" ) ;
109122
110123 var syntaxTree = await document . GetSyntaxTreeAsync ( ct ) ;
111124 if ( syntaxTree == null )
@@ -124,33 +137,29 @@ public override async Task ExecuteAsync(IModContext ctx, CancellationToken ct =
124137 )
125138 . WithName ( document . Name . Replace ( originalName , $ "{ originalName } Handle") ) ;
126139
127- proj = document . Project ;
140+ project = document . Project ;
141+ }
142+
143+ // Phase 3. Reduce pointer dimensions
144+ // Get the compilation again
145+ compilation = await project . GetCompilationAsync ( ct ) ;
146+ if ( compilation == null )
147+ {
148+ throw new InvalidOperationException ( "Failed to get compilation" ) ;
128149 }
129150
130- // TODO: Reduce handle pointer dimensions by 1
131-
132- // TODO: Old code. Cleanup needed
133- // // Before the execution of this foreach loop, the handle structs are empty
134- // //
135- // // During this foreach loop, we do two things:
136- // // 1. Rewrite all type references to refer to the handle structs
137- // // 2. Add members to handle structs (as identified the handles variable)
138- // var rewriter = new PointerDimensionReducer(handles, cfg.UseDSL);
139- // foreach (var docId in proj?.DocumentIds ?? [])
140- // {
141- // var doc =
142- // proj?.GetDocument(docId) ?? throw new InvalidOperationException("Document missing");
143- // if (await doc.GetSyntaxRootAsync(ct) is not { } root)
144- // {
145- // continue;
146- // }
147- //
148- // doc = doc.WithSyntaxRoot(rewriter.Visit(root).NormalizeWhitespace());
149- //
150- // proj = doc.Project;
151- // }
152-
153- ctx . SourceProject = proj ;
151+ // Restore symbols
152+ handleTypes = handleTypeMetadataNames . SelectMany ( name => compilation . GetTypesByMetadataName ( name ) ) . ToList ( ) ;
153+
154+ // Reduce pointer dimensions
155+ ctx . SourceProject = project ;
156+ var pointerDimensionReducer = new PointerDimensionReducer ( ctx , ct ) ;
157+ await pointerDimensionReducer . ReducePointerDimensionAsync ( handleTypes ) ;
158+ project = ctx . SourceProject ;
159+
160+ // At the time of writing this comment, this line effectively does nothing
161+ // However, if the code above is removed, then this line ensures that the context's project is updated properly
162+ ctx . SourceProject = project ;
154163 }
155164
156165 private class HandleTypeDiscoverer ( Project project , Compilation compilation , CancellationToken ct ) : SymbolVisitor
@@ -578,11 +587,82 @@ private static IEnumerable<MemberDeclarationSyntax> GetDSLHandleMembers(string s
578587 }
579588 }
580589
581- // TODO: Perksey said he wanted pointer reduction to be handled alongside NameUtils.RenameAllAsync
582- // for performance reasons
583- // TODO: Make this take in a list of references (eg: Type*, Type**) to rewrite. If a non-pointer reference
584- // is encountered, throw an error
585- private class PointerDimensionReducer ( Dictionary < string , Dictionary < string , string > > handles ) : CSharpSyntaxRewriter
590+ private class PointerDimensionReducer ( IModContext ctx , CancellationToken ct ) : CSharpSyntaxRewriter
591+ {
592+ /// <summary>
593+ /// Reduces the pointer dimension of all references to the specified symbols.
594+ /// </summary>
595+ public async Task ReducePointerDimensionAsync ( List < INamedTypeSymbol > symbols )
596+ {
597+ var project = ctx . SourceProject ;
598+ if ( project == null )
599+ {
600+ return ;
601+ }
602+
603+ var compilation = await project . GetCompilationAsync ( ct ) ;
604+ if ( compilation == null )
605+ {
606+ return ;
607+ }
608+
609+ // Find all locations where the symbols are referenced
610+ var locations = new List < Location > ( ) ;
611+ var documents = project . Documents . ToImmutableHashSet ( ) ;
612+ foreach ( var symbol in symbols )
613+ {
614+ var references = await SymbolFinder . FindReferencesAsync ( symbol , project . Solution , documents , ct ) ;
615+ locations . AddRange ( references . SelectMany ( r => r . Locations ) . Select ( rl => rl . Location ) ) ;
616+ }
617+
618+ // Reduce the pointer dimension of all reference locations
619+ foreach ( var location in locations )
620+ {
621+ var syntaxTree = location . SourceTree ;
622+ if ( syntaxTree == null )
623+ {
624+ continue ;
625+ }
626+
627+ var document = project . GetDocument ( syntaxTree ) ;
628+ if ( document == null )
629+ {
630+ continue ;
631+ }
632+
633+ var syntaxRoot = await syntaxTree . GetRootAsync ( ct ) ;
634+ var syntaxNode = syntaxRoot . FindNode ( location . SourceSpan ) ;
635+
636+ var nodeToModify = GetNodeToModify ( syntaxNode ) ;
637+ if ( nodeToModify == null )
638+ {
639+ continue ;
640+ }
641+
642+ var newNode = Visit ( nodeToModify ) ;
643+ var newRoot = syntaxRoot . ReplaceNode ( nodeToModify , newNode ) ;
644+ var newDocument = document . WithSyntaxRoot ( newRoot ) ;
645+
646+ project = newDocument . Project ;
647+ }
648+
649+ ctx . SourceProject = project ;
650+ }
651+
652+ private SyntaxNode ? GetNodeToModify ( SyntaxNode current )
653+ {
654+ if ( current . Parent is PointerTypeSyntax parent )
655+ {
656+ return parent ;
657+ }
658+
659+ return null ;
660+ }
661+
662+ public override SyntaxNode ? VisitPointerType ( PointerTypeSyntax node ) => node . ElementType ;
663+ }
664+
665+ private class OldPointerDimensionReducer ( Dictionary < string , Dictionary < string , string > > handles ) : CSharpSyntaxRewriter
586666 {
587667 /// <summary>
588668 /// The current scope i.e. fully qualified type name.
0 commit comments