diff --git a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs index 35a4b01..dbc9369 100644 --- a/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs +++ b/AyCode.Core.Serializers.SourceGenerator/AcBinarySourceGenerator.cs @@ -53,6 +53,33 @@ public class AcBinarySourceGenerator : IIncrementalGenerator defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + /// + /// ACCORE-BIN-I-T7K3 compile-time guard: a property declared as System.Object requires + /// polymorphic-prefix emit (ObjectWithTypeName) so the deserializer can resolve the + /// concrete runtime type. When is false, the prefix is + /// suppressed and the wire silently corrupts on round-trip (FixObj slot byte against + /// typeof(object) at read-time → 0-byte object wrapper → reader position drifts → + /// downstream DECIMAL_DRIFT / IndexOutOfRangeException). + /// + /// Surface the misconfiguration at build time so the silent corruption is structurally + /// impossible. Three escape hatches for the developer: + /// 1. Enable the polymorphic feature ( = true, or — once the + /// planned [AcBinarySerializable(EnablePolymorphicFeature = true)] flag lands — set + /// it on the type). + /// 2. Change the property type to a concrete type (no polymorphism needed). + /// 3. Mark the property with [AcBinaryIgnore] — ignored properties are filtered + /// out at property enumeration, so this diagnostic does not fire for them. + /// + 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)); } + /// + /// ACCORE-BIN-I-T7K3 guard: emits + /// (ACBIN002) for every System.Object-declared property on any + /// [AcBinarySerializable] type while is false. + /// Short-circuits when the feature is enabled — no per-property work needed. + /// + private static void DetectAndReportPolymorphicMisuse(List 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)); + } + } + } + } + /// /// Detects circular reference chains among [AcBinarySerializable] types at compile time /// and reports ACBIN001 warnings. Uses DFS with 3-color marking to find back-edges.