using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace AyCode.Core.Serializers.SourceGenerator; /// /// Incremental source generator for [AcBinarySerializable] types. Emits an /// IGeneratedBinaryWriter + IGeneratedBinaryReader implementation for every annotated /// type, plus a [ModuleInitializer]-based registry hook that wires the generated instances /// into the runtime serializer at startup. /// /// Source organization — this generator class is split across multiple partial files /// for navigational clarity: /// /// AcBinarySourceGenerator.cs (this file) — entry point: [Generator] attribute, /// Initialize + Execute orchestration. /// AcBinarySourceGenerator.Models.cs — non-partial model types /// (SerializableClassInfo, PropInfo, PropertyTypeKind). /// AcBinarySourceGenerator.TypeAnalysis.cs — Roslyn-symbol utility passes /// (kind detection, FNV hashing, name flattening, scan-need recursion). /// AcBinarySourceGenerator.Diagnostics.cs — ACBIN001 (cycle warning) + ACBIN002 /// (polymorph misuse error) descriptors and detection methods. /// AcBinarySourceGenerator.GetClassInfo.cs — class-info extraction pass (attribute /// flags + property metadata building). /// AcBinarySourceGenerator.GenWriter.cs — writer-side emit (WriteProperties, ScanObject, /// ScanForDuplicates, per-property emit helpers). /// AcBinarySourceGenerator.GenReader.cs — reader-side emit (ReadProperties, ReadObject, /// per-property read helpers). /// AcBinarySourceGenerator.GenInit.cs — ModuleInitializer-based registry hook emit. /// /// [Generator] public partial class AcBinarySourceGenerator : IIncrementalGenerator { private const string AttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute"; // Feature gates on the SGen-emitted writer / scan code are driven by `[AcBinarySerializable]` // attribute flags. Two such gates are wired through SerializableClassInfo: // • EnablePropertyFilter → omits the per-property `HasPropertyFilter` branch when false. // • EnablePolymorphDetect → omits the `ObjectWithTypeName + AQN` prefix on `System.Object`- // declared properties when false (then ACBIN002 guards misuse). // • EnableInternString → omits StringInterned* case-emit in the reader switch when false. // All default `true`; opt-out is per type via the attribute ctor parameters. public void Initialize(IncrementalGeneratorInitializationContext context) { var classDeclarations = context.SyntaxProvider .ForAttributeWithMetadataName( AttributeName, predicate: static (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax, transform: static (ctx, _) => GetClassInfo(ctx)) .Where(static info => info != null); context.RegisterSourceOutput(classDeclarations.Collect(), static (spc, classes) => Execute(classes!, spc)); } private static void Execute(ImmutableArray classes, SourceProductionContext context) { if (classes.IsDefaultOrEmpty) return; var valid = classes.Where(c => c != null).Cast().ToList(); if (valid.Count == 0) return; DetectAndReportCycles(valid, context); DetectAndReportPolymorphicMisuse(valid, context); foreach (var ci in valid) { context.AddSource($"{ci.ClassName}_GeneratedWriter.g.cs", SourceText.From(GenWriter(ci), Encoding.UTF8)); context.AddSource($"{ci.ClassName}_GeneratedReader.g.cs", SourceText.From(GenReader(ci), Encoding.UTF8)); } context.AddSource("AcBinaryGeneratedWriters_Init.g.cs", SourceText.From(GenInit(valid), Encoding.UTF8)); } }