Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ public static class ContextAwareServiceExtensions
public static IReadOnlyList<T> GetModels<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 GetService<T>(this object contextAware) => default!;
}
}

Expand All @@ -258,6 +257,7 @@ public partial class BattlePanel : GFramework.Core.Rule.ContextAwareBase
private ICombatSystem _system = null!;
private IUiUtility _utility = null!;
private IStrategy _service = null!;
private IReadOnlyList<IStrategy> _services = null!;
private Godot.Node _node = null!;
}
}
Expand All @@ -279,6 +279,96 @@ private void __InjectContextBindings_Generated()
_models = this.GetModels<global::TestApp.IInventoryModel>();
_system = this.GetSystem<global::TestApp.ICombatSystem>();
_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>();
}
}
Expand Down
5 changes: 4 additions & 1 deletion GFramework.SourceGenerators/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ public partial class InventoryPanel
}
```

`[GetAll]` 作用于类本身,会自动扫描字段并推断对应的 `GetX` 调用;已显式标记字段的优先级更高。
`[GetAll]` 作用于类本身,会自动扫描字段并推断 `Model`、`System`、`Utility` 相关的 `GetX` 调用;已显式标记字段的优先级更高。

`Service` 和 `Services` 绑定不会在 `[GetAll]` 下自动推断。对于普通引用类型字段,请显式使用 `[GetService]` 或
`[GetServices]`,避免将非上下文服务字段误判为服务依赖。
18 changes: 4 additions & 14 deletions GFramework.SourceGenerators/Rule/ContextGetGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,12 +582,7 @@
return true;
}

if (elementType.IsReferenceType)
{
binding = new BindingInfo(fieldSymbol, BindingKind.Services, elementType);
return true;
}

// Service collections stay opt-in for the same reason as single services.
return false;
}

Expand All @@ -609,12 +604,7 @@
return true;
}

if (fieldSymbol.Type.IsReferenceType)
{
binding = new BindingInfo(fieldSymbol, BindingKind.Service, fieldSymbol.Type);
return true;
}

// Service bindings stay opt-in because arbitrary reference types are too ambiguous to infer safely.
return false;
}

Expand Down Expand Up @@ -723,10 +713,9 @@

foreach (var candidateType in EnumerateCollectionTypeCandidates(targetType))
{
if (candidateType.TypeArguments.Length != 1)
if (candidateType is not { TypeArguments: [var candidateElementType] })

Check failure on line 716 in GFramework.SourceGenerators/Rule/ContextGetGenerator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Missing compiler required member 'System.Index.GetOffset'

Check failure on line 716 in GFramework.SourceGenerators/Rule/ContextGetGenerator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Predefined type 'System.Index' is not defined or imported

Check failure on line 716 in GFramework.SourceGenerators/Rule/ContextGetGenerator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Missing compiler required member 'System.Index.GetOffset'

Check failure on line 716 in GFramework.SourceGenerators/Rule/ContextGetGenerator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Predefined type 'System.Index' is not defined or imported
continue;

var candidateElementType = candidateType.TypeArguments[0];
var expectedSourceType = readOnlyList.Construct(candidateElementType);
if (!expectedSourceType.IsAssignableTo(targetType))
continue;
Expand All @@ -738,6 +727,7 @@
return false;
}


private static IEnumerable<INamedTypeSymbol> EnumerateCollectionTypeCandidates(INamedTypeSymbol typeSymbol)
{
yield return typeSymbol;
Expand Down
Loading