diff --git a/Robust.Serialization.Generator/Generator.cs b/Robust.Serialization.Generator/Generator.cs index c02c3d550c..119bc65b4c 100644 --- a/Robust.Serialization.Generator/Generator.cs +++ b/Robust.Serialization.Generator/Generator.cs @@ -19,112 +19,117 @@ public class Generator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext initContext) { - IncrementalValuesProvider<(string name, string code)?> dataDefinitions = initContext.SyntaxProvider - .CreateSyntaxProvider( - static (node, _) => node is TypeDeclarationSyntax, - static (context, _) => - { - var type = (TypeDeclarationSyntax)context.Node; - var symbol = (ITypeSymbol)context.SemanticModel.GetDeclaredSymbol(type)!; - if (!IsDataDefinition(symbol)) - return null; - - return GenerateForDataDefinition(type, symbol); - } - ) - .Where(static type => type != null); + IncrementalValuesProvider dataDefinitions = initContext.SyntaxProvider.CreateSyntaxProvider( + static (node, _) => node is TypeDeclarationSyntax, + static (context, _) => + { + var type = (TypeDeclarationSyntax) context.Node; + var symbol = (ITypeSymbol) context.SemanticModel.GetDeclaredSymbol(type)!; + return IsDataDefinition(symbol) ? type : null; + } + ).Where(static type => type != null)!; + var comparer = new DataDefinitionComparer(); initContext.RegisterSourceOutput( - dataDefinitions, + initContext.CompilationProvider.Combine(dataDefinitions.WithComparer(comparer).Collect()), static (sourceContext, source) => { - // TODO: deduplicate based on name? - var (name, code) = source!.Value; + var (compilation, declarations) = source; + var builder = new StringBuilder(); + var containingTypes = new Stack(); + var declarationsGenerated = new HashSet(); + var deltaType = compilation.GetTypeByMetadataName(ComponentDeltaInterfaceName)!; - sourceContext.AddSource(name, code); + foreach (var declaration in declarations) + { + builder.Clear(); + containingTypes.Clear(); + + var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!; + + var symbolName = type + .ToDisplayString() + .Replace('<', '{') + .Replace('>', '}'); + + if (!declarationsGenerated.Add(symbolName)) + continue; + + var nonPartial = !IsPartial(declaration); + + var namespaceString = type.ContainingNamespace.IsGlobalNamespace + ? string.Empty + : $"namespace {type.ContainingNamespace.ToDisplayString()};"; + + var containingType = type.ContainingType; + while (containingType != null) + { + containingTypes.Push(containingType); + containingType = containingType.ContainingType; + } + + var containingTypesStart = new StringBuilder(); + var containingTypesEnd = new StringBuilder(); + foreach (var parent in containingTypes) + { + var syntax = (ClassDeclarationSyntax) parent.DeclaringSyntaxReferences[0].GetSyntax(); + if (!IsPartial(syntax)) + { + nonPartial = true; + continue; + } + + containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{"); + containingTypesEnd.AppendLine("}"); + } + + var definition = GetDataDefinition(type); + if (nonPartial || definition.InvalidFields) + continue; + + builder.AppendLine($$""" +#nullable enable +using System; +using Robust.Shared.Analyzers; +using Robust.Shared.IoC; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Manager.Exceptions; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0612 // Type or member is obsolete +#pragma warning disable CS0108 // Member hides inherited member; missing new keyword +#pragma warning disable RA0002 // Robust access analyzer + +{{namespaceString}} + +{{containingTypesStart}} + +{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}> +{ + {{GetConstructor(definition)}} + + {{GetCopyMethods(definition, deltaType)}} + + {{GetInstantiators(definition, deltaType)}} +} + +{{containingTypesEnd}} +"""); + + var sourceText = CSharpSyntaxTree + .ParseText(builder.ToString()) + .GetRoot() + .NormalizeWhitespace() + .ToFullString(); + + sourceContext.AddSource($"{symbolName}.g.cs", sourceText); + } } ); } - private static (string, string)? GenerateForDataDefinition( - TypeDeclarationSyntax declaration, - ITypeSymbol type) - { - var builder = new StringBuilder(); - var containingTypes = new Stack(); - containingTypes.Clear(); - - var symbolName = type - .ToDisplayString() - .Replace('<', '{') - .Replace('>', '}'); - - var nonPartial = !IsPartial(declaration); - - var namespaceString = type.ContainingNamespace.IsGlobalNamespace - ? string.Empty - : $"namespace {type.ContainingNamespace.ToDisplayString()};"; - - var containingType = type.ContainingType; - while (containingType != null) - { - containingTypes.Push(containingType); - containingType = containingType.ContainingType; - } - - var containingTypesStart = new StringBuilder(); - var containingTypesEnd = new StringBuilder(); - foreach (var parent in containingTypes) - { - var syntax = (ClassDeclarationSyntax)parent.DeclaringSyntaxReferences[0].GetSyntax(); - if (!IsPartial(syntax)) - { - nonPartial = true; - continue; - } - - containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{"); - containingTypesEnd.AppendLine("}"); - } - - var definition = GetDataDefinition(type); - if (nonPartial || definition.InvalidFields) - return null; - - builder.AppendLine($$""" - #nullable enable - using System; - using Robust.Shared.Analyzers; - using Robust.Shared.IoC; - using Robust.Shared.GameObjects; - using Robust.Shared.Serialization; - using Robust.Shared.Serialization.Manager; - using Robust.Shared.Serialization.Manager.Exceptions; - using Robust.Shared.Serialization.TypeSerializers.Interfaces; - #pragma warning disable CS0618 // Type or member is obsolete - #pragma warning disable CS0612 // Type or member is obsolete - #pragma warning disable CS0108 // Member hides inherited member; missing new keyword - #pragma warning disable RA0002 // Robust access analyzer - - {{namespaceString}} - - {{containingTypesStart}} - - {{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}> - { - {{GetConstructor(definition)}} - - {{GetCopyMethods(definition)}} - - {{GetInstantiators(definition)}} - } - - {{containingTypesEnd}} - """); - - return ($"{symbolName}.g.cs", builder.ToString()); - } - private static DataDefinition GetDataDefinition(ITypeSymbol definition) { var fields = new List(); @@ -191,7 +196,7 @@ public class Generator : IIncrementalGenerator return builder.ToString(); } - private static string GetCopyMethods(DataDefinition definition) + private static string GetCopyMethods(DataDefinition definition, ITypeSymbol deltaType) { var builder = new StringBuilder(); @@ -262,36 +267,36 @@ public class Generator : IIncrementalGenerator {{baseCopy}} """); - foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true)) + foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true, deltaType)) { - var interfaceModifiers = baseType != null && - baseType.AllInterfaces.Any(i => i.ToDisplayString() == interfaceName) + var interfaceModifiers = baseType != null && baseType.AllInterfaces.Contains(@interface, SymbolEqualityComparer.Default) ? "override " : modifiers; + var interfaceName = @interface.ToDisplayString(); builder.AppendLine($$""" - /// - [Obsolete("Use ISerializationManager.CopyTo instead")] - public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null) - { - var def = ({{definition.GenericTypeName}}) target; - Copy(ref def, serialization, hookCtx, context); - target = def; - } + /// + [Obsolete("Use ISerializationManager.CopyTo instead")] + public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null) + { + var def = ({{definition.GenericTypeName}}) target; + Copy(ref def, serialization, hookCtx, context); + target = def; + } - /// - [Obsolete("Use ISerializationManager.CopyTo instead")] - public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null) - { - InternalCopy(ref target, serialization, hookCtx, context); - } - """); + /// + [Obsolete("Use ISerializationManager.CopyTo instead")] + public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null) + { + InternalCopy(ref target, serialization, hookCtx, context); + } + """); } return builder.ToString(); } - private static string GetInstantiators(DataDefinition definition) + private static string GetInstantiators(DataDefinition definition, ITypeSymbol deltaType) { var builder = new StringBuilder(); var modifiers = string.Empty; @@ -325,28 +330,27 @@ public class Generator : IIncrementalGenerator """); } - foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false)) + foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false, deltaType)) { + var interfaceName = @interface.ToDisplayString(); builder.AppendLine($$""" - {{interfaceName}} {{interfaceName}}.Instantiate() - { - return Instantiate(); - } + {{interfaceName}} {{interfaceName}}.Instantiate() + { + return Instantiate(); + } - {{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate() - { - return Instantiate(); - } - """); + {{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate() + { + return Instantiate(); + } + """); } return builder.ToString(); } [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private static IEnumerable InternalGetImplicitDataDefinitionInterfaces( - ITypeSymbol type, - bool all) + private static IEnumerable InternalGetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all, ITypeSymbol deltaType) { var symbols = GetImplicitDataDefinitionInterfaces(type, all); @@ -364,10 +368,10 @@ public class Generator : IIncrementalGenerator return symbols; } - if (symbols.Any(x => x == ComponentDeltaInterfaceName)) + if (symbols.Any(x => x.ToDisplayString() == deltaType.ToDisplayString())) return symbols; - return symbols.Append(ComponentDeltaInterfaceName); + return symbols.Append(deltaType); } // TODO serveronly? do we care? who knows!! diff --git a/Robust.Serialization.Generator/Types.cs b/Robust.Serialization.Generator/Types.cs index c07e3530d5..fd34351437 100644 --- a/Robust.Serialization.Generator/Types.cs +++ b/Robust.Serialization.Generator/Types.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -94,13 +93,13 @@ internal static class Types return false; } - internal static IEnumerable GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all) + internal static IEnumerable GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all) { var interfaces = all ? type.AllInterfaces : type.Interfaces; foreach (var @interface in interfaces) { if (IsImplicitDataDefinitionInterface(@interface)) - yield return @interface.ToDisplayString(); + yield return @interface; } }