22using Microsoft . CodeAnalysis ;
33using Microsoft . CodeAnalysis . CSharp ;
44using Microsoft . CodeAnalysis . Diagnostics ;
5- using System . IO ;
5+ using System ;
6+ using System . Collections . Generic ;
7+ using System . Collections . Immutable ;
8+ using System . Diagnostics . CodeAnalysis ;
69using System . Linq ;
710using System . Runtime . CompilerServices ;
811
@@ -15,29 +18,34 @@ public static class CSharpGeneratorRunner
1518 [ ModuleInitializer ]
1619 public static void InitializeCompilation ( )
1720 {
18- // running .NET Core system assemblies dir path
19- var baseAssemblyPath = Path . GetDirectoryName ( typeof ( object ) . Assembly . Location ) ! ;
20- var systemAssemblies = Directory . GetFiles ( baseAssemblyPath )
21- . Where ( x =>
22- {
23- var fileName = Path . GetFileName ( x ) ;
24- if ( fileName . EndsWith ( "Native.dll" ) ) return false ;
25- return fileName . StartsWith ( "System" ) || ( fileName is "mscorlib.dll" or "netstandard.dll" ) ;
26- } ) ;
21+ var globalUsings = """
22+ global using System;
23+ global using System.Linq;
24+ global using System.Collections;
25+ global using System.Collections.Generic;
26+ global using System.Threading;
27+ global using System.Threading.Tasks;
28+ global using System.ComponentModel.DataAnnotations;
29+ global using MemoryPack;
30+ """ ;
31+
32+ var systemAssemblies = AppDomain . CurrentDomain . GetAssemblies ( )
33+ . Where ( x => ! x . IsDynamic && ! string . IsNullOrWhiteSpace ( x . Location ) ) ;
2734
2835 var references = systemAssemblies
29- . Append ( typeof ( MemoryPackableAttribute ) . Assembly . Location ) // System Assemblies + MemoryPack.Core.dll
30- . Select ( x => MetadataReference . CreateFromFile ( x ) )
36+ . Append ( typeof ( MemoryPackableAttribute ) . Assembly ) // System Assemblies + MemoryPack.Core.dll
37+ . Select ( x => MetadataReference . CreateFromFile ( x . Location ) )
3138 . ToArray ( ) ;
3239
3340 var compilation = CSharpCompilation . Create ( "generatortest" ,
3441 references : references ,
35- options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary ) ) ;
42+ syntaxTrees : [ CSharpSyntaxTree . ParseText ( globalUsings , path : "GlobalUsings.cs" ) ] ,
43+ options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , allowUnsafe : true ) ) ;
3644
3745 baseCompilation = compilation ;
3846 }
3947
40- public static Diagnostic [ ] RunGenerator ( string source , string [ ] ? preprocessorSymbols = null , AnalyzerConfigOptionsProvider ? options = null )
48+ public static ( Compilation , ImmutableArray < Diagnostic > ) RunGenerator ( string source , string [ ] ? preprocessorSymbols = null , AnalyzerConfigOptionsProvider ? options = null )
4149 {
4250 if ( preprocessorSymbols == null )
4351 {
@@ -55,8 +63,126 @@ public static Diagnostic[] RunGenerator(string source, string[]? preprocessorSym
5563
5664 driver . RunGeneratorsAndUpdateCompilation ( compilation , out var newCompilation , out var diagnostics ) ;
5765
58- // combine diagnostics as result.(ignore warning)
59- var compilationDiagnostics = newCompilation . GetDiagnostics ( ) ;
60- return diagnostics . Concat ( compilationDiagnostics ) . Where ( x => x . Severity == DiagnosticSeverity . Error ) . ToArray ( ) ;
66+ return ( newCompilation , diagnostics ) ;
67+ }
68+
69+ public static ( string Key , string Reasons ) [ ] [ ] GetIncrementalGeneratorTrackedStepsReasons ( string keyPrefixFilter , params string [ ] sources )
70+ {
71+ var parseOptions = new CSharpParseOptions ( LanguageVersion . CSharp11 ) ;
72+ var driver = CSharpGeneratorDriver . Create (
73+ [ new MemoryPackGenerator ( ) . AsSourceGenerator ( ) ] ,
74+ driverOptions : new GeneratorDriverOptions ( IncrementalGeneratorOutputKind . None , trackIncrementalGeneratorSteps : true ) )
75+ . WithUpdatedParseOptions ( parseOptions ) ;
76+
77+ var generatorResults = sources
78+ . Select ( source =>
79+ {
80+ var compilation = baseCompilation . AddSyntaxTrees ( CSharpSyntaxTree . ParseText ( source , parseOptions ) ) ;
81+ driver = driver . RunGenerators ( compilation ) ;
82+ return driver . GetRunResult ( ) . Results [ 0 ] ;
83+ } )
84+ . ToArray ( ) ;
85+
86+ var reasons = generatorResults
87+ . Select ( x => x . TrackedSteps
88+ . Where ( x => x . Key . StartsWith ( keyPrefixFilter ) || x . Key == "SourceOutput" )
89+ . Select ( x =>
90+ {
91+ if ( x . Key == "SourceOutput" )
92+ {
93+ var values = x . Value . Where ( x => x . Inputs [ 0 ] . Source . Name ? . StartsWith ( keyPrefixFilter ) ?? false ) ;
94+ return (
95+ x . Key ,
96+ Reasons : string . Join ( ", " , values . SelectMany ( x => x . Outputs ) . Select ( x => x . Reason ) . ToArray ( ) )
97+ ) ;
98+ }
99+ else
100+ {
101+ return (
102+ Key : x . Key . Substring ( keyPrefixFilter . Length ) ,
103+ Reasons : string . Join ( ", " , x . Value . SelectMany ( x => x . Outputs ) . Select ( x => x . Reason ) . ToArray ( ) )
104+ ) ;
105+ }
106+ } )
107+ . OrderBy ( x => x . Key )
108+ . ToArray ( ) )
109+ . ToArray ( ) ;
110+
111+ return reasons ;
112+ }
113+ }
114+
115+ public class VerifyHelper ( ITestOutputHelper output , string idPrefix )
116+ {
117+ // Diagnostics Verify
118+
119+ public void Ok ( [ StringSyntax ( "C#-test" ) ] string code , [ CallerArgumentExpression ( "code" ) ] string ? codeExpr = null )
120+ {
121+ output . WriteLine ( codeExpr ) ;
122+
123+ var ( compilation , diagnostics ) = CSharpGeneratorRunner . RunGenerator ( code ) ;
124+ foreach ( var item in diagnostics )
125+ {
126+ output . WriteLine ( item . ToString ( ) ) ;
127+ }
128+ OutputGeneratedCode ( compilation ) ;
129+
130+ diagnostics . Length . Should ( ) . Be ( 0 ) ;
131+ }
132+
133+ public void Verify ( int id , [ StringSyntax ( "C#-test" ) ] string code , string diagnosticsCodeSpan , [ CallerArgumentExpression ( "code" ) ] string ? codeExpr = null )
134+ {
135+ output . WriteLine ( codeExpr ) ;
136+
137+ var ( compilation , diagnostics ) = CSharpGeneratorRunner . RunGenerator ( code ) ;
138+ foreach ( var item in diagnostics )
139+ {
140+ output . WriteLine ( item . ToString ( ) ) ;
141+ }
142+ OutputGeneratedCode ( compilation ) ;
143+
144+ diagnostics . Length . Should ( ) . Be ( 1 ) ;
145+ diagnostics [ 0 ] . Id . Should ( ) . Be ( idPrefix + id . ToString ( "000" ) ) ;
146+
147+ var text = GetLocationText ( diagnostics [ 0 ] , compilation . SyntaxTrees ) ;
148+ text . Should ( ) . Be ( diagnosticsCodeSpan ) ;
149+ }
150+
151+ public ( string , string ) [ ] Verify ( [ StringSyntax ( "C#-test" ) ] string code , [ CallerArgumentExpression ( "code" ) ] string ? codeExpr = null )
152+ {
153+ output . WriteLine ( codeExpr ) ;
154+
155+ var ( compilation , diagnostics ) = CSharpGeneratorRunner . RunGenerator ( code ) ;
156+ OutputGeneratedCode ( compilation ) ;
157+ return diagnostics . Select ( x => ( x . Id , GetLocationText ( x , compilation . SyntaxTrees ) ) ) . ToArray ( ) ;
158+ }
159+
160+ string GetLocationText ( Diagnostic diagnostic , IEnumerable < SyntaxTree > syntaxTrees )
161+ {
162+ var location = diagnostic . Location ;
163+
164+ var textSpan = location . SourceSpan ;
165+ var sourceTree = location . SourceTree ;
166+ if ( sourceTree == null )
167+ {
168+ var lineSpan = location . GetLineSpan ( ) ;
169+ if ( lineSpan . Path == null ) return "" ;
170+
171+ sourceTree = syntaxTrees . FirstOrDefault ( x => x . FilePath == lineSpan . Path ) ;
172+ if ( sourceTree == null ) return "" ;
173+ }
174+
175+ var text = sourceTree . GetText ( ) . GetSubText ( textSpan ) . ToString ( ) ;
176+ return text ;
177+ }
178+
179+ void OutputGeneratedCode ( Compilation compilation )
180+ {
181+ foreach ( var syntaxTree in compilation . SyntaxTrees )
182+ {
183+ // only shows ConsoleApp.Run/Builder generated code
184+ if ( ! syntaxTree . FilePath . Contains ( "g.cs" ) ) continue ;
185+ output . WriteLine ( syntaxTree . ToString ( ) ) ;
186+ }
61187 }
62188}
0 commit comments