2
2
using Microsoft . CodeAnalysis ;
3
3
using Microsoft . CodeAnalysis . CSharp ;
4
4
using 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 ;
6
9
using System . Linq ;
7
10
using System . Runtime . CompilerServices ;
8
11
@@ -15,29 +18,34 @@ public static class CSharpGeneratorRunner
15
18
[ ModuleInitializer ]
16
19
public static void InitializeCompilation ( )
17
20
{
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 ) ) ;
27
34
28
35
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 ) )
31
38
. ToArray ( ) ;
32
39
33
40
var compilation = CSharpCompilation . Create ( "generatortest" ,
34
41
references : references ,
35
- options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary ) ) ;
42
+ syntaxTrees : [ CSharpSyntaxTree . ParseText ( globalUsings , path : "GlobalUsings.cs" ) ] ,
43
+ options : new CSharpCompilationOptions ( OutputKind . DynamicallyLinkedLibrary , allowUnsafe : true ) ) ;
36
44
37
45
baseCompilation = compilation ;
38
46
}
39
47
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 )
41
49
{
42
50
if ( preprocessorSymbols == null )
43
51
{
@@ -55,8 +63,126 @@ public static Diagnostic[] RunGenerator(string source, string[]? preprocessorSym
55
63
56
64
driver . RunGeneratorsAndUpdateCompilation ( compilation , out var newCompilation , out var diagnostics ) ;
57
65
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
+ }
61
187
}
62
188
}
0 commit comments