mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-29 16:44:28 +08:00
fix(generator): 修复ContextGetGenerator中的服务绑定推断逻辑
- 移除了对引用类型的自动服务绑定推断,避免将非上下文服务字段误判为服务依赖 - 更新了GetAll特性的行为描述,明确Service和Services绑定不会自动推断 - 添加了显式GetService特性的测试用例验证正确的绑定行为 - 从测试代码中移除了未使用的GetService扩展方法声明 - 为BattlePanel测试类添加了IStrategy服务集合字段以完善测试覆盖
This commit is contained in:
parent
e692c721e9
commit
d1eafe2c9b
@ -234,7 +234,6 @@ public class ContextGetGeneratorTests
|
|||||||
public static IReadOnlyList<T> GetModels<T>(this object contextAware) => default!;
|
public static IReadOnlyList<T> GetModels<T>(this object contextAware) => default!;
|
||||||
public static T GetSystem<T>(this object contextAware) => default!;
|
public static T GetSystem<T>(this object contextAware) => default!;
|
||||||
public static T GetUtility<T>(this object contextAware) => default!;
|
public static T GetUtility<T>(this object contextAware) => default!;
|
||||||
public static T GetService<T>(this object contextAware) => default!;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +257,7 @@ public class ContextGetGeneratorTests
|
|||||||
private ICombatSystem _system = null!;
|
private ICombatSystem _system = null!;
|
||||||
private IUiUtility _utility = null!;
|
private IUiUtility _utility = null!;
|
||||||
private IStrategy _service = null!;
|
private IStrategy _service = null!;
|
||||||
|
private IReadOnlyList<IStrategy> _services = null!;
|
||||||
private Godot.Node _node = null!;
|
private Godot.Node _node = null!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,6 +279,96 @@ public class ContextGetGeneratorTests
|
|||||||
_models = this.GetModels<global::TestApp.IInventoryModel>();
|
_models = this.GetModels<global::TestApp.IInventoryModel>();
|
||||||
_system = this.GetSystem<global::TestApp.ICombatSystem>();
|
_system = this.GetSystem<global::TestApp.ICombatSystem>();
|
||||||
_utility = this.GetUtility<global::TestApp.IUiUtility>();
|
_utility = this.GetUtility<global::TestApp.IUiUtility>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
""";
|
||||||
|
|
||||||
|
await GeneratorTest<ContextGetGenerator>.RunAsync(
|
||||||
|
source,
|
||||||
|
("TestApp_BattlePanel.ContextGet.g.cs", expected));
|
||||||
|
Assert.Pass();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Generates_Explicit_Service_Binding_For_GetAll_Class()
|
||||||
|
{
|
||||||
|
var source = """
|
||||||
|
using System;
|
||||||
|
using GFramework.SourceGenerators.Abstractions.Rule;
|
||||||
|
|
||||||
|
namespace GFramework.SourceGenerators.Abstractions.Rule
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
|
public sealed class GetAllAttribute : Attribute { }
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Field, Inherited = false)]
|
||||||
|
public sealed class GetServiceAttribute : Attribute { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.Rule
|
||||||
|
{
|
||||||
|
public interface IContextAware { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Rule
|
||||||
|
{
|
||||||
|
public abstract class ContextAwareBase : GFramework.Core.Abstractions.Rule.IContextAware { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.Model
|
||||||
|
{
|
||||||
|
public interface IModel { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.Systems
|
||||||
|
{
|
||||||
|
public interface ISystem { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.Utility
|
||||||
|
{
|
||||||
|
public interface IUtility { }
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace GFramework.Core.Extensions
|
||||||
|
{
|
||||||
|
public static class ContextAwareServiceExtensions
|
||||||
|
{
|
||||||
|
public static T GetModel<T>(this object contextAware) => default!;
|
||||||
|
public static T GetService<T>(this object contextAware) => default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace TestApp
|
||||||
|
{
|
||||||
|
public interface IInventoryModel : GFramework.Core.Abstractions.Model.IModel { }
|
||||||
|
public interface IStrategy { }
|
||||||
|
|
||||||
|
[GetAll]
|
||||||
|
public partial class BattlePanel : GFramework.Core.Rule.ContextAwareBase
|
||||||
|
{
|
||||||
|
private IInventoryModel _model = null!;
|
||||||
|
|
||||||
|
[GetService]
|
||||||
|
private IStrategy _service = null!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""";
|
||||||
|
|
||||||
|
const string expected = """
|
||||||
|
// <auto-generated />
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using GFramework.Core.Extensions;
|
||||||
|
|
||||||
|
namespace TestApp;
|
||||||
|
|
||||||
|
partial class BattlePanel
|
||||||
|
{
|
||||||
|
private void __InjectContextBindings_Generated()
|
||||||
|
{
|
||||||
|
_model = this.GetModel<global::TestApp.IInventoryModel>();
|
||||||
_service = this.GetService<global::TestApp.IStrategy>();
|
_service = this.GetService<global::TestApp.IStrategy>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,4 +43,7 @@ public partial class InventoryPanel
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`[GetAll]` 作用于类本身,会自动扫描字段并推断对应的 `GetX` 调用;已显式标记字段的优先级更高。
|
`[GetAll]` 作用于类本身,会自动扫描字段并推断 `Model`、`System`、`Utility` 相关的 `GetX` 调用;已显式标记字段的优先级更高。
|
||||||
|
|
||||||
|
`Service` 和 `Services` 绑定不会在 `[GetAll]` 下自动推断。对于普通引用类型字段,请显式使用 `[GetService]` 或
|
||||||
|
`[GetServices]`,避免将非上下文服务字段误判为服务依赖。
|
||||||
|
|||||||
@ -582,12 +582,7 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementType.IsReferenceType)
|
// Service collections stay opt-in for the same reason as single services.
|
||||||
{
|
|
||||||
binding = new BindingInfo(fieldSymbol, BindingKind.Services, elementType);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,12 +604,7 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldSymbol.Type.IsReferenceType)
|
// Service bindings stay opt-in because arbitrary reference types are too ambiguous to infer safely.
|
||||||
{
|
|
||||||
binding = new BindingInfo(fieldSymbol, BindingKind.Service, fieldSymbol.Type);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,12 +711,11 @@ public sealed class ContextGetGenerator : IIncrementalGenerator
|
|||||||
if (readOnlyList is null || fieldType is not INamedTypeSymbol targetType)
|
if (readOnlyList is null || fieldType is not INamedTypeSymbol targetType)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (var candidateType in EnumerateCollectionTypeCandidates(targetType))
|
var allTypeCandidates = EnumerateCollectionTypeCandidates(targetType)
|
||||||
{
|
.SelectMany(candidateType => candidateType.TypeArguments);
|
||||||
if (candidateType.TypeArguments.Length != 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var candidateElementType = candidateType.TypeArguments[0];
|
foreach (var candidateElementType in allTypeCandidates)
|
||||||
|
{
|
||||||
var expectedSourceType = readOnlyList.Construct(candidateElementType);
|
var expectedSourceType = readOnlyList.Construct(candidateElementType);
|
||||||
if (!expectedSourceType.IsAssignableTo(targetType))
|
if (!expectedSourceType.IsAssignableTo(targetType))
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user