AyCode.Core/AyCode.Core.Serializers.Sou.../AcBinarySourceGenerator.cs

80 lines
4.2 KiB
C#

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;
/// <summary>
/// Incremental source generator for <c>[AcBinarySerializable]</c> types. Emits an
/// <c>IGeneratedBinaryWriter</c> + <c>IGeneratedBinaryReader</c> implementation for every annotated
/// type, plus a <c>[ModuleInitializer]</c>-based registry hook that wires the generated instances
/// into the runtime serializer at startup.
///
/// <para><b>Source organization</b> — this generator class is split across multiple partial files
/// for navigational clarity:</para>
/// <list type="bullet">
/// <item><c>AcBinarySourceGenerator.cs</c> (this file) — entry point: <c>[Generator]</c> attribute,
/// <c>Initialize</c> + <c>Execute</c> orchestration.</item>
/// <item><c>AcBinarySourceGenerator.Models.cs</c> — non-partial model types
/// (<c>SerializableClassInfo</c>, <c>PropInfo</c>, <c>PropertyTypeKind</c>).</item>
/// <item><c>AcBinarySourceGenerator.TypeAnalysis.cs</c> — Roslyn-symbol utility passes
/// (kind detection, FNV hashing, name flattening, scan-need recursion).</item>
/// <item><c>AcBinarySourceGenerator.Diagnostics.cs</c> — ACBIN001 (cycle warning) + ACBIN002
/// (polymorph misuse error) descriptors and detection methods.</item>
/// <item><c>AcBinarySourceGenerator.GetClassInfo.cs</c> — class-info extraction pass (attribute
/// flags + property metadata building).</item>
/// <item><c>AcBinarySourceGenerator.GenWriter.cs</c> — writer-side emit (WriteProperties, ScanObject,
/// ScanForDuplicates, per-property emit helpers).</item>
/// <item><c>AcBinarySourceGenerator.GenReader.cs</c> — reader-side emit (ReadProperties, ReadObject,
/// per-property read helpers).</item>
/// <item><c>AcBinarySourceGenerator.GenInit.cs</c> — ModuleInitializer-based registry hook emit.</item>
/// </list>
/// </summary>
[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<SerializableClassInfo?> classes, SourceProductionContext context)
{
if (classes.IsDefaultOrEmpty) return;
var valid = classes.Where(c => c != null).Cast<SerializableClassInfo>().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));
}
}