From 3fadba2d790a83114a7f1c2ddbbc0edddfd4b665 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:27:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(generator):=20=E6=B7=BB=E5=8A=A0=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E9=9B=86=E5=90=88=E8=87=AA=E5=8A=A8=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了 AutoRegisterExportedCollectionsGenerator 源生成器 - 支持扫描标记了 AutoRegisterExportedCollectionsAttribute 的 partial 类型 - 为使用 RegisterExportedCollectionAttribute 声明的集合成员生成集中注册方法 - 添加了类型验证和诊断报告功能 - 实现了集合元素类型推导和注册方法兼容性检查 - 生成批量注册样板代码以简化手动注册流程 - 添加了完整的单元测试覆盖各种使用场景 --- ...gisterExportedCollectionsGeneratorTests.cs | 66 +++++++++++++++++++ ...utoRegisterExportedCollectionsGenerator.cs | 29 +++++++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs b/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs index cd255d1d..db68697c 100644 --- a/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs +++ b/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs @@ -133,4 +133,70 @@ public class AutoRegisterExportedCollectionsGeneratorTests await test.RunAsync(); } + + [Test] + public async Task Generates_Batch_Registration_Method_When_Register_Method_Uses_Array_Parameter() + { + const string source = """ + #nullable enable + using System; + using System.Collections.Generic; + using GFramework.Godot.SourceGenerators.Abstractions; + + namespace GFramework.Godot.SourceGenerators.Abstractions + { + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] + public sealed class RegisterExportedCollectionAttribute : Attribute + { + public RegisterExportedCollectionAttribute(string registryMemberName, string registerMethodName) { } + } + } + + namespace TestApp + { + public sealed class ArrayRegistry + { + public void Register(int[] value) { } + } + + [AutoRegisterExportedCollections] + public partial class Bootstrapper + { + private readonly ArrayRegistry _registry = new(); + + [RegisterExportedCollection(nameof(_registry), nameof(ArrayRegistry.Register))] + public List Values { get; } = new(); + } + } + """; + + const string expected = """ + // + #nullable enable + + namespace TestApp; + + partial class Bootstrapper + { + private void __RegisterExportedCollections_Generated() + { + if (this.Values is not null && this._registry is not null) + { + foreach (var __generatedItem in this.Values) + { + this._registry.Register(__generatedItem); + } + } + } + } + + """; + + await GeneratorTest.RunAsync( + source, + ("TestApp_Bootstrapper.AutoRegisterExportedCollections.g.cs", expected)); + } } diff --git a/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs b/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs index b22bc003..8e094b13 100644 --- a/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs +++ b/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs @@ -104,6 +104,7 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener var registrations = CollectRegistrations( context, + compilation, candidate.TypeSymbol, registerCollectionAttribute, enumerableType); @@ -138,6 +139,7 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener private static List CollectRegistrations( SourceProductionContext context, + Compilation compilation, INamedTypeSymbol typeSymbol, INamedTypeSymbol registerCollectionAttribute, INamedTypeSymbol enumerableType) @@ -156,8 +158,17 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener if (attribute is null) continue; - if (!TryCreateRegistration(context, typeSymbol, member, attribute, enumerableType, out var registration)) + if (!TryCreateRegistration( + context, + compilation, + typeSymbol, + member, + attribute, + enumerableType, + out var registration)) + { continue; + } registrations.Add(registration); } @@ -167,6 +178,7 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener private static bool TryCreateRegistration( SourceProductionContext context, + Compilation compilation, INamedTypeSymbol ownerType, ISymbol collectionMember, AttributeData attribute, @@ -238,7 +250,7 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener .Any(method => !method.IsStatic && method.Parameters.Length == 1 && - elementType.IsAssignableTo(method.Parameters[0].Type as INamedTypeSymbol)); + CanAcceptElementType(compilation, elementType, method.Parameters[0].Type)); if (!hasCompatibleMethod) { @@ -255,6 +267,19 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener return true; } + private static bool CanAcceptElementType( + Compilation compilation, + ITypeSymbol elementType, + ITypeSymbol parameterType) + { + if (elementType.IsAssignableTo(parameterType as INamedTypeSymbol)) + return true; + + // Fall back to Roslyn's conversion rules so arrays and other non-named types are + // validated the same way the generated invocation will be bound by the compiler. + return compilation.ClassifyConversion(elementType, parameterType).IsImplicit; + } + private static bool TryGetRegistrationAttributeArguments( AttributeData attribute, out string registryMemberName,