From 535743e824c23bc61444a73436e28ab75c98ead7 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sat, 28 Mar 2026 19:45:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(generator):=20=E8=A7=A3=E5=86=B3=E6=BA=90?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E6=8D=A2=E8=A1=8C=E7=AC=A6=E5=85=BC=E5=AE=B9=E6=80=A7=E5=92=8C?= =?UTF-8?q?=E9=9B=86=E5=90=88=E7=B1=BB=E5=9E=8B=E5=80=99=E9=80=89=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在测试中添加换行符标准化功能,确保跨平台测试一致性 - 修复ContextGetGenerator中集合类型候选检测逻辑,跳过无效类型参数 - 添加针对可空服务字段的单元测试用例 - 优化生成器对不同系统换行符的处理机制 --- .../Core/GeneratorTest.cs | 20 +++- .../Core/GeneratorTest.cs | 20 +++- .../Rule/ContextGetGeneratorTests.cs | 96 ++++++++++++++++++- .../Rule/ContextGetGenerator.cs | 16 +--- 4 files changed, 127 insertions(+), 25 deletions(-) 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; }