diff --git a/README.ja.md b/README.ja.md index 99c031e..f62ba52 100644 --- a/README.ja.md +++ b/README.ja.md @@ -73,7 +73,7 @@ Blittable Struct は全ケースで offset table を持たない raw payload と `[ZeroSerializer]` が付いていても Blittable Struct はネスト View 化しません。親が非 Blittable 型なら親の field offset table は存在しますが、Blittable Struct payload 内部には table を生成しません。 -全フィールド型が Blittable 対応済みで、自身の `StructLayout(LayoutKind.Sequential, Pack = 1)` だけが不足する `[ZeroSerializer]` struct には、型名 identifier へ `ZEROS006` warning を出します。その struct が有効な `[ZeroSerializer]` ネスト型として使われている場合は、raw payload 化による性能改善を案内する `ZEROS007` info もネスト型の identifier へ1回だけ出します。`[ZeroSerializer]` がない間は親型に `ZEROS003` error が発生し、`ZEROS007` は生成エラーが解消されるまで出ません。 +全フィールド型が Blittable 対応済みで、自身の `StructLayout(LayoutKind.Sequential, Pack = 1)` だけが不足する `[ZeroSerializer]` struct には、型名 identifier へ `ZEROS006` warning を出します。 ネストした class/struct にも `[ZeroSerializer]` が必要です。未修飾の型は `ZEROS003` error になります。 diff --git a/src/ZeroSerializerGenerator.cs b/src/ZeroSerializerGenerator.cs index bf0ae53..f723de7 100644 --- a/src/ZeroSerializerGenerator.cs +++ b/src/ZeroSerializerGenerator.cs @@ -76,10 +76,10 @@ public sealed class ZeroSerializerGenerator : ISourceGenerator DiagnosticSeverity.Warning, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor BlittableCompatibleNestedStruct = new( + private static readonly DiagnosticDescriptor UseFlagsEnumToReducePayloadSize = new( "ZEROS007", - "Nested struct can use faster Blittable serialization", - "Nested struct '{0}' can use StructLayout(LayoutKind.Sequential, Pack = 1) to improve serialization performance with raw payload serialization", + "Use flags enum to reduce payload size", + "Property '{0}' uses bool type; consider using a flags enum (byte) to reduce payload size by combining up to 8 booleans into one byte", SerializerName, DiagnosticSeverity.Info, isEnabledByDefault: true); @@ -249,11 +249,6 @@ private static void ExecuteCore( return; } - ReportBlittableCompatibleNestedStructDiagnostics( - executionContext, - validModels, - generationModels); - var modelLookup = new Dictionary(SymbolEqualityComparer.Default); // Each type owns one generated file and contributes one method to its namespace-local partial extension class. foreach (TypeGenerationModel validModel in validModels) @@ -373,6 +368,14 @@ private static TypeGenerationModel CreateGenerationModel( continue; } + if (serializableProperty.Type.SpecialType == SpecialType.System_Boolean) + { + executionContext.ReportDiagnostic(Diagnostic.Create( + UseFlagsEnumToReducePayloadSize, + GetPropertyTypeLocation(serializableProperty), + serializableProperty.Name)); + } + generationModel.Fields.Add(propertyModel); } @@ -761,36 +764,6 @@ private static bool HasBlittableCompatibleFieldShape(INamedTypeSymbol candidateS return true; } - private static void ReportBlittableCompatibleNestedStructDiagnostics( - GeneratorExecutionContext executionContext, - IReadOnlyList validGenerationModels, - IReadOnlyDictionary generationModels) - { - var reportedNestedStructs = new HashSet(SymbolEqualityComparer.Default); - foreach (TypeGenerationModel containingGenerationModel in validGenerationModels) - { - foreach (FieldGenerationModel nestedField in containingGenerationModel.Fields) - { - if (nestedField.Kind != FieldSerializationKind.Nested - || nestedField.NestedSerializableType is not INamedTypeSymbol nestedSerializableStruct - || nestedSerializableStruct.TypeKind != TypeKind.Struct - || !generationModels.TryGetValue(nestedSerializableStruct, out TypeGenerationModel? nestedGenerationModel) - || nestedGenerationModel.IsBlittableStruct - || !HasBlittableCompatibleFieldShape(nestedSerializableStruct) - || !reportedNestedStructs.Add(nestedSerializableStruct)) - { - continue; - } - - // Report only after dependency validation so this performance advice never replaces ZEROS003 or another generation error. - executionContext.ReportDiagnostic(Diagnostic.Create( - BlittableCompatibleNestedStruct, - GetTypeIdentifierLocation(nestedSerializableStruct), - nestedSerializableStruct.ToDisplayString())); - } - } - } - private static Location? GetTypeIdentifierLocation(INamedTypeSymbol declaredType) { foreach (SyntaxReference declaringSyntaxReference in declaredType.DeclaringSyntaxReferences) diff --git a/tests/Diagnostics/DiagnosticTests.cs b/tests/Diagnostics/DiagnosticTests.cs index 649b83b..af72cae 100644 --- a/tests/Diagnostics/DiagnosticTests.cs +++ b/tests/Diagnostics/DiagnosticTests.cs @@ -507,54 +507,37 @@ await CSharpSourceGeneratorVerifier.VerifySourceGenerat } [Fact] - public async Task ZEROS007_Violation_BlittableCompatibleNestedStruct() + public async Task ZEROS007_Violation_BoolProperty() { string source = @" using ZeroSerializer; [ZeroSerializer] -public struct {|#0:NestedStruct|} +public class MyBoolClass { - public int Value { get; set; } -} - -[ZeroSerializer] -public class ParentClass -{ - public NestedStruct Child { get; set; } + public {|#0:bool|} IsActive { get; set; } } "; await CSharpSourceGeneratorVerifier.VerifySourceGeneratorAsync( source, - new DiagnosticResult("ZEROS006", DiagnosticSeverity.Warning) - .WithLocation(0) - .WithMessage("Struct 'NestedStruct' has a Blittable-compatible field shape; use StructLayout(LayoutKind.Sequential, Pack = 1) to enable raw payload serialization"), new DiagnosticResult("ZEROS007", DiagnosticSeverity.Info) .WithLocation(0) - .WithMessage("Nested struct 'NestedStruct' can use StructLayout(LayoutKind.Sequential, Pack = 1) to improve serialization performance with raw payload serialization") + .WithMessage("Property 'IsActive' uses bool type; consider using a flags enum (byte) to reduce payload size by combining up to 8 booleans into one byte") ); } [Fact] - public async Task ZEROS007_Compliant_BlittableNestedStructWithLayout() + public async Task ZEROS007_Compliant_NonBoolProperty() { string source = @" -using System.Runtime.InteropServices; using ZeroSerializer; [ZeroSerializer] -[StructLayout(LayoutKind.Sequential, Pack = 1)] -public struct NestedStructWithLayout +public class MyNonBoolClass { public int Value { get; set; } } - -[ZeroSerializer] -public class ParentClassWithBlittable -{ - public NestedStructWithLayout Child { get; set; } -} "; await CSharpSourceGeneratorVerifier.VerifySourceGeneratorAsync(