feat(generator): 添加对继承层次结构中注册方法的支持

- 实现 EnumerateCandidateMethods 方法以搜索基类和接口中的注册方法
- 修改 AutoRegisterExportedCollectionsGenerator 以支持从继承链中查找兼容的注册方法
- 添加完整的单元测试覆盖继承、接口实现和泛型场景
- 修复静态成员和不可访问方法的诊断报告功能
- 增强源代码生成器对复杂继承结构的支持能力
This commit is contained in:
GeWuYou 2026-04-13 19:17:06 +08:00
parent 3f237ef32e
commit 5e1e16f86e
2 changed files with 99 additions and 2 deletions

View File

@ -200,6 +200,84 @@ public class AutoRegisterExportedCollectionsGeneratorTests
("TestApp_Bootstrapper.AutoRegisterExportedCollections.g.cs", expected)); ("TestApp_Bootstrapper.AutoRegisterExportedCollections.g.cs", expected));
} }
[Test]
public async Task Generates_Batch_Registration_Method_When_Register_Method_Comes_From_Inherited_Interface()
{
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 interface IKeyValue<TKey, TValue>
{
}
public interface IRegistry<TKey, TValue>
{
void Registry(IKeyValue<TKey, TValue> mapping);
}
public interface IAssetRegistry<TValue> : IRegistry<string, TValue>
{
}
public sealed class IntConfig : IKeyValue<string, int>
{
}
[AutoRegisterExportedCollections]
public partial class Bootstrapper
{
private readonly IAssetRegistry<int>? _registry = null;
[RegisterExportedCollection(nameof(_registry), "Registry")]
public List<IntConfig>? Values { get; } = new();
}
}
""";
const string expected = """
// <auto-generated />
#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.Registry(__generatedItem);
}
}
}
}
""";
await GeneratorTest<AutoRegisterExportedCollectionsGenerator>.RunAsync(
source,
("TestApp_Bootstrapper.AutoRegisterExportedCollections.g.cs", expected));
}
[Test] [Test]
public async Task Reports_Diagnostic_When_Collection_Member_Is_Not_Instance_Readable() public async Task Reports_Diagnostic_When_Collection_Member_Is_Not_Instance_Readable()
{ {

View File

@ -268,8 +268,7 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener
return false; return false;
} }
var hasCompatibleMethod = registryType.GetMembers(registerMethodName) var hasCompatibleMethod = EnumerateCandidateMethods(registryType, registerMethodName)
.OfType<IMethodSymbol>()
.Any(method => .Any(method =>
!method.IsStatic && !method.IsStatic &&
method.Parameters.Length == 1 && method.Parameters.Length == 1 &&
@ -319,6 +318,26 @@ public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGener
return compilation.ClassifyConversion(elementType, parameterType).IsImplicit; return compilation.ClassifyConversion(elementType, parameterType).IsImplicit;
} }
private static IEnumerable<IMethodSymbol> EnumerateCandidateMethods(
INamedTypeSymbol registryType,
string registerMethodName)
{
foreach (var method in registryType.GetMembers(registerMethodName).OfType<IMethodSymbol>())
yield return method;
for (var baseType = registryType.BaseType; baseType is not null; baseType = baseType.BaseType)
{
foreach (var method in baseType.GetMembers(registerMethodName).OfType<IMethodSymbol>())
yield return method;
}
foreach (var interfaceType in registryType.AllInterfaces)
{
foreach (var method in interfaceType.GetMembers(registerMethodName).OfType<IMethodSymbol>())
yield return method;
}
}
private static bool TryGetRegistrationAttributeArguments( private static bool TryGetRegistrationAttributeArguments(
SourceProductionContext context, SourceProductionContext context,
ISymbol collectionMember, ISymbol collectionMember,