[LOADED_DOCS: 3 files, no new loads]

Add compile-time error for object-typed props w/o polymorph

Introduces ACBIN002: a compile-time diagnostic that errors if a [AcBinarySerializable] type declares a System.Object property while UsePolymorphType is false. This prevents silent wire corruption by requiring the developer to either enable polymorphic serialization, use a concrete type, or ignore the property. The check is integrated into the source generator initialization.
This commit is contained in:
Loretta 2026-05-15 08:11:51 +02:00
parent f051f32bfa
commit a7f2d3605b
1 changed files with 52 additions and 0 deletions

View File

@ -53,6 +53,33 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
/// <summary>
/// ACCORE-BIN-I-T7K3 compile-time guard: a property declared as <c>System.Object</c> requires
/// polymorphic-prefix emit (<c>ObjectWithTypeName</c>) so the deserializer can resolve the
/// concrete runtime type. When <see cref="UsePolymorphType"/> is <c>false</c>, the prefix is
/// suppressed and the wire silently corrupts on round-trip (FixObj slot byte against
/// <c>typeof(object)</c> at read-time → 0-byte object wrapper → reader position drifts →
/// downstream <c>DECIMAL_DRIFT</c> / <c>IndexOutOfRangeException</c>).
///
/// Surface the misconfiguration at build time so the silent corruption is structurally
/// impossible. Three escape hatches for the developer:
/// 1. Enable the polymorphic feature (<see cref="UsePolymorphType"/> = true, or — once the
/// planned <c>[AcBinarySerializable(EnablePolymorphicFeature = true)]</c> flag lands — set
/// it on the type).
/// 2. Change the property type to a concrete type (no polymorphism needed).
/// 3. Mark the property with <c>[AcBinaryIgnore]</c> — ignored properties are filtered
/// out at property enumeration, so this diagnostic does not fire for them.
/// </summary>
private static readonly DiagnosticDescriptor PolymorphicPropertyWithFeatureDisabledError = new(
id: "ACBIN002",
title: "Polymorphic property requires polymorphic feature enabled",
messageFormat: "Type '{0}' contains property '{1}' declared as System.Object, but polymorphic serialization in the source generator is disabled (UsePolymorphType=false). " +
"The generated writer would silently corrupt the wire on round-trip. " +
"To fix: (1) enable polymorphic serialization, (2) change '{1}' to a concrete type, or (3) exclude it with [AcBinaryIgnore].",
category: "AcBinarySerializer",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classDeclarations = context.SyntaxProvider
@ -406,6 +433,7 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
if (valid.Count == 0) return;
DetectAndReportCycles(valid, context);
DetectAndReportPolymorphicMisuse(valid, context);
foreach (var ci in valid)
{
@ -416,6 +444,30 @@ public class AcBinarySourceGenerator : IIncrementalGenerator
context.AddSource("AcBinaryGeneratedWriters_Init.g.cs", SourceText.From(GenInit(valid), Encoding.UTF8));
}
/// <summary>
/// ACCORE-BIN-I-T7K3 guard: emits <see cref="PolymorphicPropertyWithFeatureDisabledError"/>
/// (ACBIN002) for every <c>System.Object</c>-declared property on any
/// <c>[AcBinarySerializable]</c> type while <see cref="UsePolymorphType"/> is <c>false</c>.
/// Short-circuits when the feature is enabled — no per-property work needed.
/// </summary>
private static void DetectAndReportPolymorphicMisuse(List<SerializableClassInfo> classes, SourceProductionContext spc)
{
if (UsePolymorphType) return; // Feature enabled → polymorphic prefix is emitted, no misuse possible.
foreach (var ci in classes)
{
foreach (var p in ci.Properties)
{
if (p.IsObjectDeclaredType)
{
spc.ReportDiagnostic(Diagnostic.Create(
PolymorphicPropertyWithFeatureDisabledError, Location.None,
ci.ClassName, p.Name));
}
}
}
}
/// <summary>
/// Detects circular reference chains among [AcBinarySerializable] types at compile time
/// and reports ACBIN001 warnings. Uses DFS with 3-color marking to find back-edges.