Skip to content

Commit 9cff3eb

Browse files
committed
prepare source-generator diagnostics
1 parent 36508ee commit 9cff3eb

8 files changed

+284
-26
lines changed

MemoryPack.sln

+1-3
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemoryPack.AspNetCoreMvcFor
2929
EndProject
3030
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SandboxNet6", "sandbox\SandboxNet6\SandboxNet6.csproj", "{87C0CEAA-E511-46AA-93AB-AF742A1F8EE2}"
3131
EndProject
32-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{C56A9A52-EE3A-44A5-A8EA-AE36C79FFB6C}"
33-
EndProject
3432
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassLibrary", "sandbox\ClassLibrary\ClassLibrary.csproj", "{0ADCE3AF-C900-4FCB-938B-654211EDD6BE}"
3533
EndProject
3634
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeAot", "sandbox\NativeAot\NativeAot.csproj", "{6E18AECF-34B2-48F9-9694-54150FB156EB}"
3735
EndProject
3836
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net6VsNet7", "sandbox\Net6VsNet7\Net6VsNet7.csproj", "{5612B811-586A-4EB3-9AE7-60CAD4969A1B}"
3937
EndProject
40-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MemoryPack.UnityShims", "src\MemoryPack.UnityShims\MemoryPack.UnityShims.csproj", "{9339C66C-25E5-4130-A213-9BA1804AD562}"
38+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemoryPack.UnityShims", "src\MemoryPack.UnityShims\MemoryPack.UnityShims.csproj", "{9339C66C-25E5-4130-A213-9BA1804AD562}"
4139
EndProject
4240
Global
4341
GlobalSection(SolutionConfigurationPlatforms) = preSolution

sandbox/SandboxWebApp/wwwroot/js/memorypack/FooBarBazDayonDattayon.js

+60
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sandbox/SandboxWebApp/wwwroot/js/memorypack/FooBarBazDayonDattayon.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { MemoryPackWriter } from "./MemoryPackWriter.js";
2+
import { MemoryPackReader } from "./MemoryPackReader.js";
3+
4+
export class FooBarBazDayonDattayon {
5+
myProperty: number;
6+
7+
constructor() {
8+
this.myProperty = 0;
9+
10+
}
11+
12+
static serialize(value: FooBarBazDayonDattayon | null): Uint8Array {
13+
const writer = MemoryPackWriter.getSharedInstance();
14+
this.serializeCore(writer, value);
15+
return writer.toArray();
16+
}
17+
18+
static serializeCore(writer: MemoryPackWriter, value: FooBarBazDayonDattayon | null): void {
19+
if (value == null) {
20+
writer.writeNullObjectHeader();
21+
return;
22+
}
23+
24+
writer.writeObjectHeader(1);
25+
writer.writeInt32(value.myProperty);
26+
27+
}
28+
29+
static serializeArray(value: (FooBarBazDayonDattayon | null)[] | null): Uint8Array {
30+
const writer = MemoryPackWriter.getSharedInstance();
31+
this.serializeArrayCore(writer, value);
32+
return writer.toArray();
33+
}
34+
35+
static serializeArrayCore(writer: MemoryPackWriter, value: (FooBarBazDayonDattayon | null)[] | null): void {
36+
writer.writeArray(value, (writer, x) => FooBarBazDayonDattayon.serializeCore(writer, x));
37+
}
38+
39+
static deserialize(buffer: ArrayBuffer): FooBarBazDayonDattayon | null {
40+
return this.deserializeCore(new MemoryPackReader(buffer));
41+
}
42+
43+
static deserializeCore(reader: MemoryPackReader): FooBarBazDayonDattayon | null {
44+
const [ok, count] = reader.tryReadObjectHeader();
45+
if (!ok) {
46+
return null;
47+
}
48+
49+
const value = new FooBarBazDayonDattayon();
50+
if (count == 1) {
51+
value.myProperty = reader.readInt32();
52+
53+
}
54+
else if (count > 1) {
55+
throw new Error("Current object's property count is larger than type schema, can't deserialize about versioning.");
56+
}
57+
else {
58+
if (count == 0) return value;
59+
value.myProperty = reader.readInt32(); if (count == 1) return value;
60+
61+
}
62+
return value;
63+
}
64+
65+
static deserializeArray(buffer: ArrayBuffer): (FooBarBazDayonDattayon | null)[] | null {
66+
return this.deserializeArrayCore(new MemoryPackReader(buffer));
67+
}
68+
69+
static deserializeArrayCore(reader: MemoryPackReader): (FooBarBazDayonDattayon | null)[] | null {
70+
return reader.readArray(reader => FooBarBazDayonDattayon.deserializeCore(reader));
71+
}
72+
}

src/MemoryPack.Generator/MemoryPack.Generator.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
<None Include="../../Icon.png" Pack="true" PackagePath="/" />
2121

2222
<!-- https://learn.microsoft.com/en-us/visualstudio/extensibility/roslyn-version-support?view=vs-2022 -->
23-
<!-- require to support SyntaxValueProvider.ForAttributeWithMetadataName(Roslyn 4.3.1, VS2022 17.3 -->
23+
<!-- require to support SyntaxValueProvider.ForAttributeWithMetadataName(Roslyn 4.3.0, VS2022 17.3) -->
24+
<!-- Unity 2022.3.12f1 or newer supports 4.3.0 -->
2425
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.0" PrivateAssets="all" />
2526
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
2627
<PrivateAssets>all</PrivateAssets>

tests/MemoryPack.Tests/GeneratorDiagnosticsTest.TypeScript.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public partial class GeneratorDiagnosticsTest
2121
{
2222
void Compile2(int id, string code, bool allowMultipleError = false)
2323
{
24-
var diagnostics = CSharpGeneratorRunner.RunGenerator(code, options: new TypeScriptOptionProvider());
24+
var (_, diagnostics) = CSharpGeneratorRunner.RunGenerator(code, options: new TypeScriptOptionProvider());
2525
if (!allowMultipleError)
2626
{
2727
diagnostics.Length.Should().Be(1);
@@ -41,7 +41,7 @@ string CompileAndRead(string code, string fileName, bool enableNullableTypes = t
4141
optionProvider["build_property.MemoryPackGenerator_TypeScriptOutputDirectory"] = outputDir;
4242
optionProvider["build_property.MemoryPackGenerator_TypeScriptEnableNullableTypes"] = enableNullableTypes ? "true" : "false";
4343

44-
CSharpGeneratorRunner.RunGenerator(code,options: optionProvider);
44+
CSharpGeneratorRunner.RunGenerator(code, options: optionProvider);
4545

4646
var outputFilePath = Path.Combine(outputDir, fileName);
4747

tests/MemoryPack.Tests/GeneratorDiagnosticsTest.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public partial class GeneratorDiagnosticsTest
1818
{
1919
void Compile(int id, string code, bool allowMultipleError = false)
2020
{
21-
var diagnostics = CSharpGeneratorRunner.RunGenerator(code);
21+
var (_, diagnostics) = CSharpGeneratorRunner.RunGenerator(code);
2222
if (!allowMultipleError)
2323
{
2424
diagnostics.Length.Should().Be(1);
@@ -528,11 +528,11 @@ public partial struct Hoge
528528
""";
529529

530530
{
531-
var diagnostics = CSharpGeneratorRunner.RunGenerator(code, preprocessorSymbols: new[] { "NET7_0_OR_GREATER" });
531+
var (_, diagnostics) = CSharpGeneratorRunner.RunGenerator(code, preprocessorSymbols: new[] { "NET7_0_OR_GREATER" });
532532
diagnostics.Length.Should().Be(0);
533533
}
534534
{
535-
var diagnostics = CSharpGeneratorRunner.RunGenerator(code, preprocessorSymbols: new string[] { });
535+
var (_, diagnostics) = CSharpGeneratorRunner.RunGenerator(code, preprocessorSymbols: new string[] { });
536536
diagnostics.Length.Should().Be(1);
537537
diagnostics[0].Id.Should().Be("MEMPACK034");
538538
}

tests/MemoryPack.Tests/Utils/CSharpGeneratorRunner.cs

+143-17
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
using Microsoft.CodeAnalysis;
33
using Microsoft.CodeAnalysis.CSharp;
44
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;
69
using System.Linq;
710
using 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

Comments
 (0)