diff --git a/GFramework.Godot.SourceGenerators.Tests/Core/GeneratorTest.cs b/GFramework.Godot.SourceGenerators.Tests/Core/GeneratorTest.cs index 8e98e31..cca8546 100644 --- a/GFramework.Godot.SourceGenerators.Tests/Core/GeneratorTest.cs +++ b/GFramework.Godot.SourceGenerators.Tests/Core/GeneratorTest.cs @@ -1,7 +1,4 @@ -using Microsoft.CodeAnalysis.CSharp.Testing; -using Microsoft.CodeAnalysis.Testing; - -namespace GFramework.Godot.SourceGenerators.Tests.Core; +namespace GFramework.Godot.SourceGenerators.Tests.Core; /// /// 提供源代码生成器测试的通用功能。 @@ -30,8 +27,21 @@ public static class GeneratorTest foreach (var (filename, content) in generatedSources) test.TestState.GeneratedSources.Add( - (typeof(TGenerator), filename, content)); + (typeof(TGenerator), filename, NormalizeLineEndings(content))); await test.RunAsync(); } + + /// + /// 将测试内联快照统一为当前平台换行符,避免不同系统上的源生成输出比较出现伪差异。 + /// + /// 原始快照内容。 + /// 使用当前平台换行符的快照内容。 + private static string NormalizeLineEndings(string content) + { + return content + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Replace("\r", "\n", StringComparison.Ordinal) + .Replace("\n", Environment.NewLine, StringComparison.Ordinal); + } } \ No newline at end of file diff --git a/GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs b/GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs index 29138e1..a622d38 100644 --- a/GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs +++ b/GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs @@ -1,7 +1,4 @@ -using Microsoft.CodeAnalysis.CSharp.Testing; -using Microsoft.CodeAnalysis.Testing; - -namespace GFramework.SourceGenerators.Tests.Core; +namespace GFramework.SourceGenerators.Tests.Core; /// /// 提供源代码生成器测试的通用功能 @@ -32,8 +29,21 @@ public static class GeneratorTest // 添加期望的生成源文件到测试状态中 foreach (var (filename, content) in generatedSources) test.TestState.GeneratedSources.Add( - (typeof(TGenerator), filename, content)); + (typeof(TGenerator), filename, NormalizeLineEndings(content))); await test.RunAsync(); } + + /// + /// 将测试快照统一为当前平台换行符,避免不同系统上的源生成输出比较出现伪差异。 + /// + /// 原始快照内容。 + /// 使用当前平台换行符的快照内容。 + private static string NormalizeLineEndings(string content) + { + return content + .Replace("\r\n", "\n", StringComparison.Ordinal) + .Replace("\r", "\n", StringComparison.Ordinal) + .Replace("\n", Environment.NewLine, StringComparison.Ordinal); + } } \ No newline at end of file diff --git a/GFramework.SourceGenerators.Tests/Rule/ContextGetGeneratorTests.cs b/GFramework.SourceGenerators.Tests/Rule/ContextGetGeneratorTests.cs index a69a6f1..0fc78a9 100644 --- a/GFramework.SourceGenerators.Tests/Rule/ContextGetGeneratorTests.cs +++ b/GFramework.SourceGenerators.Tests/Rule/ContextGetGeneratorTests.cs @@ -1,9 +1,5 @@ using GFramework.SourceGenerators.Rule; using GFramework.SourceGenerators.Tests.Core; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Testing; -using Microsoft.CodeAnalysis.Testing; -using NUnit.Framework; namespace GFramework.SourceGenerators.Tests.Rule; @@ -381,6 +377,98 @@ public class ContextGetGeneratorTests Assert.Pass(); } + [Test] + public async Task Skips_Nullable_Service_Like_Field_For_ContextAware_GetAll_Class() + { + var source = """ + using System; + using GFramework.SourceGenerators.Abstractions.Rule; + + namespace GFramework.SourceGenerators.Abstractions.Rule + { + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public sealed class ContextAwareAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, Inherited = false)] + public sealed class GetAllAttribute : Attribute { } + } + + namespace GFramework.Core.Abstractions.Rule + { + public interface 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(this object contextAware) => default!; + public static T GetSystem(this object contextAware) => default!; + } + } + + namespace Godot + { + public class Control { } + } + + namespace TestApp + { + public interface IGridModel : GFramework.Core.Abstractions.Model.IModel { } + public interface IRunLoopSystem : GFramework.Core.Abstractions.Systems.ISystem { } + public interface IUiPageBehavior { } + + [ContextAware] + [GetAll] + public partial class GameplayHud : Godot.Control + { + private IGridModel _gridModel = null!; + private IUiPageBehavior? _page; + private IRunLoopSystem _runLoopSystem = null!; + } + } + """; + + const string expected = """ + // + #nullable enable + + using GFramework.Core.Extensions; + + namespace TestApp; + + partial class GameplayHud + { + private void __InjectContextBindings_Generated() + { + _gridModel = this.GetModel(); + _runLoopSystem = this.GetSystem(); + } + } + + """; + + await GeneratorTest.RunAsync( + source, + ("TestApp_GameplayHud.ContextGet.g.cs", expected)); + Assert.Pass(); + } + [Test] public async Task Generates_Bindings_For_IContextAware_Class() { diff --git a/GFramework.SourceGenerators/Rule/ContextGetGenerator.cs b/GFramework.SourceGenerators/Rule/ContextGetGenerator.cs index 4186e52..7702aa1 100644 --- a/GFramework.SourceGenerators/Rule/ContextGetGenerator.cs +++ b/GFramework.SourceGenerators/Rule/ContextGetGenerator.cs @@ -1,13 +1,7 @@ -using System.Collections.Immutable; -using System.Text; using GFramework.SourceGenerators.Common.Constants; using GFramework.SourceGenerators.Common.Diagnostics; -using GFramework.SourceGenerators.Common.Extensions; using GFramework.SourceGenerators.Common.Info; using GFramework.SourceGenerators.Diagnostics; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; namespace GFramework.SourceGenerators.Rule; @@ -711,11 +705,12 @@ public sealed class ContextGetGenerator : IIncrementalGenerator if (readOnlyList is null || fieldType is not INamedTypeSymbol targetType) return false; - var allTypeCandidates = EnumerateCollectionTypeCandidates(targetType) - .SelectMany(candidateType => candidateType.TypeArguments); - - foreach (var candidateElementType in allTypeCandidates) + foreach (var candidateType in EnumerateCollectionTypeCandidates(targetType)) { + if (candidateType.TypeArguments.Length != 1) + continue; + + var candidateElementType = candidateType.TypeArguments[0]; var expectedSourceType = readOnlyList.Construct(candidateElementType); if (!expectedSourceType.IsAssignableTo(targetType)) continue; @@ -724,7 +719,6 @@ public sealed class ContextGetGenerator : IIncrementalGenerator return true; } - return false; }