Conversation
- 修复 YamlConfigLoaderTests 中 Assert.ThrowsAsync 的冗余 async/await 写法 - 补充 WaitForTaskWithinAsync 的 ConfigureAwait(false) 以消除剩余 MA0004 - 更新 analyzer-warning-reduction 的 RP-059 跟踪与验证记录
- 修复 Assert.ThrowsAsync 中机械型 async 包装\n- 保持锚点缺失场景的异常与未安装断言语义不变
- 修复三个配置测试文件中 Assert.ThrowsAsync 与 Assert.DoesNotThrowAsync 的冗余 async/await 包装 - 调整文本校验异步异常测试签名以匹配去包装后的同步断言写法
- 修复 Scene 与 UI 过渡管道中间件链的多余 async 包装 - 更新低风险 await 调用以显式使用 ConfigureAwait(false)
- 重构四个纯加载测试的固定布置为文件内私有 helper,缩短方法体长度而不改变断言语义 - 保持 schema 内容、异常路径与 item/monster 注册顺序不变,并避免触碰热重载测试
- 更新 analyzer-warning-reduction 的 RP-060 跟踪与 trace - 记录并行 warning-reduction 批次的验证结果与当前分支体积 - 补充下一轮继续朝 75 文件阈值推进的恢复建议
- 修复 SettingsSystem 中不依赖上下文的 await,补充 ConfigureAwait(false) - 修复 ScopedStorage.DeleteAsync 的 await,保持作用域前缀语义不变
- 重构 Replace、Push、Pop、Clear 的 around pipeline 核心委托,移除匿名 async 包装 - 补充 BeforeChange 与 AfterChange 的 ConfigureAwait(false),收敛明显低风险 MA0004 - 保留场景生命周期与栈操作相关 await 的默认上下文行为,并在代码中说明原因
- 修复 FileStorage 在锁获取与异步释放路径上的 ConfigureAwait(false) 缺失 - 保持文件锁、临时文件写入和原子替换流程不变
- 修复 RouterBase 进入守卫异步调用缺少 ConfigureAwait(false) 的机械型 MA0004 - 修复 RouterBase 离开守卫异步调用缺少 ConfigureAwait(false) 的机械型 MA0004
- 调整 UiRouterBase 的异步过渡调用以显式保留同步上下文 - 清理 Push Pop Replace Clear 流程中的低风险 MA0004 - 保持 UI 生命周期顺序与过渡阶段语义不变
- 优化 allOf、enum、not 测试中的 Assert.ThrowsAsync 委托写法,移除冗余 async/await 包装 - 保持 schema 用例语义、断言内容和异常路径不变
- 简化 YAML 配置加载测试中的 Assert.ThrowsAsync Task 包装 - 简化持久化测试中的异步异常断言包装并保持原有断言语义
- 简化 Architecture、Command、Query 与 AsyncArchitecture 测试中的机械型 async/await 异步断言包装 - 更新 AsyncKeyLockManagerTests 中的 Task 断言写法以消除低风险 analyzer 噪音
- 简化 StateMachineSystemTests 中的 Task 异常断言包装 - 简化 StateMachineTests 中的异步异常断言包装并保持测试语义不变
- 更新 analyzer-warning-reduction 跟踪文档到 RP-061 并记录当前 HEAD 与 branch diff 真值 - 补充最近批次的验证结论与当前 subagent 恢复入口
- 简化 ResultExtensionsTests 中的异步异常断言包装 - 更新 AsyncOperationTests 的 Task 断言写法以消除低风险 analyzer 噪音
- 简化 ArchitectureConfigIntegrationTests 中的异步异常断言包装 - 保持重复初始化场景的测试语义与验证路径不变
- 补充 LocalizationTableTests 中 string key Dictionary 的 Ordinal comparer - 补充 QueryCoroutineExtensionsTests 中 Metadata 字典的 Ordinal comparer 并保持测试语义不变
- 优化 ContextAwareServiceExtensionsTests 中的字符串相等断言,显式使用 Ordinal 比较 - 优化 RollingFileAppenderTests 中的 StartsWith、EndsWith、排序比较与文件名判等写法,补充 Ordinal 比较并保持测试语义不变
- 为 LogEntryTests 与格式化器测试中的字符串字典补充 StringComparer.Ordinal - 保持日志相关测试断言与行为路径不变
- 补齐 WaitForTask、ResourceManager、State 与 StateMachine 测试中的低风险 ConfigureAwait(false) - 更新 AsyncKeyLock、ResultExtensions、ResultT 与 Pipe 测试中的 culture 和异步等待写法
- 更新 OptionTests 中的 culture-sensitive 转换与 TryParse 写法 - 修正 AsyncExtensionsTests 与 CollectionExtensionsTests 的低风险异步和字符串比较写法
- 更新 ArchitectureContext、ArchitectureServices、GameContext 与环境测试桩的异常类型以满足 analyzer 约束 - 补齐 AsyncTestModel 与 AsyncTestSystem 的异步等待配置并保持测试语义不变 - 调整 ResultTests 的异类异常断言样例以避免新增编译与 analyzer 噪音
- 更新 ContextAware、Store 与通用扩展中的参数空校验写法以满足 analyzer 约束 - 简化 coroutine、pause、log 与 async 测试中的等待和断言包装并保持测试语义不变 - 调整测试替身异常类型与 Result 系列断言样例以减少低风险 warning 噪音
- 更新 active tracking 与 trace 到 RP-062 并收口到当前真值 - 记录本轮已达到 75 files 阈值的停止结论与最新 branch 指标 - 补充 Core build 通过结果与 Core.Tests 的 MSB4276 环境阻塞说明
- 更新 active tracking 与 trace 中的当前 branch line 统计 - 将恢复点表述改为代码 stop commit 以避免后续 docs 提交导致失真
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 44 minutes and 27 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (21)
📝 WalkthroughWalkthrough该PR通过将 Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅
|
| Descriptor | Linter | Files | Fixed | Errors | Warnings | Elapsed time |
|---|---|---|---|---|---|---|
| dotnet-format | yes | 1 | no | 5.33s | ||
| ✅ REPOSITORY | gitleaks | yes | no | no | 7.48s | |
| ✅ REPOSITORY | trufflehog | yes | no | no | 6.43s |
Detailed Issues
⚠️ CSHARP / dotnet-format - 1 error
Welcome to .NET 9.0!
---------------------
SDK Version: 9.0.114
----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, run 'dotnet dev-certs https --trust'
Learn about HTTPS: https://aka.ms/dotnet-https
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Unhandled exception: System.Exception: Restore operation failed.
at Microsoft.CodeAnalysis.Tools.CodeFormatter.OpenMSBuildWorkspaceAsync(String solutionOrProjectPath, WorkspaceType workspaceType, Boolean noRestore, Boolean requiresSemantics, String binaryLogPath, Boolean logWorkspaceWarnings, ILogger logger, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.Tools.CodeFormatter.FormatWorkspaceAsync(FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken, String binaryLogPath)
at Microsoft.CodeAnalysis.Tools.FormatCommandCommon.FormatAsync(FormatOptions formatOptions, ILogger`1 logger, CancellationToken cancellationToken)
at Microsoft.CodeAnalysis.Tools.Commands.RootFormatCommand.FormatCommandDefaultHandler.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
at System.CommandLine.Invocation.InvocationPipeline.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

Show us your support by starring ⭐ the repository
|
| Filename | Overview |
|---|---|
| GFramework.Game/Storage/FileStorage.cs | Refactored await using scoped blocks to await using var pattern with leaveOpen: true to fix double-disposal; previous thread concern addressed. ConfigureAwait(false) added throughout. |
| GFramework.Game/Scene/SceneRouterBase.cs | Inline lambdas extracted to named helper methods (ExecuteReplaceCoreAsync, etc.) with explicit ConfigureAwait(true) throughout; outer pipeline call now uses ConfigureAwait(true), aligning with UiRouterBase. |
| GFramework.Game/Scene/SceneTransitionPipeline.cs | Added ConfigureAwait(false) to all internal awaits; async () => await lambda pattern replaced with direct method group to remove unnecessary state-machine allocation. |
| GFramework.Game/UI/UiRouterBase.cs | Added explicit ConfigureAwait(true) to all awaits inside lambdas and on outer pipeline calls; consistent with scene router intent. |
| GFramework.Game/UI/UiTransitionPipeline.cs | Same ConfigureAwait(false) additions and async lambda simplification as SceneTransitionPipeline. |
| GFramework.Core/StateManagement/StoreSelection.cs | Introduced #if NET9_0_OR_GREATER to use System.Threading.Lock on .NET 9+ and fall back to object on .NET 8; null checks expanded from ThrowIfNull to verbose if-throw pattern. |
| GFramework.Core/Extensions/CollectionExtensions.cs | Replaced ArgumentNullException.ThrowIfNull with explicit if (x is null) throw blocks — functionally equivalent, reduces analyzer warnings. |
| GFramework.Core/Extensions/GuardExtensions.cs | Same ThrowIfNull → explicit null check replacement; uses single-line if without braces (minor style inconsistency vs. multi-line blocks in other changed files). |
| GFramework.Game/Storage/ScopedStorage.cs | DeleteAsync simplified from async/await wrapper to direct return inner.DeleteAsync(Key(key)), eliminating an unnecessary state machine allocation. |
| GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs | Test method converted from async Task to void; Assert.ThrowsAsync correctly called without async () => await wrapper (NUnit internally blocks until the task completes). |
Sequence Diagram
sequenceDiagram
participant Caller as Caller (engine thread)
participant Router as SceneRouterBase
participant Pipeline as SceneTransitionPipeline
participant Handler as Around Handler
participant Core as ExecuteReplaceCoreAsync
Caller->>Router: ReplaceAsync()
Router->>Pipeline: ExecuteAroundAsync(event, coreAction) ConfigureAwait(true)
alt No around handlers
Pipeline->>Core: coreAction() ConfigureAwait(false)
Note over Core: BeforeChangeAsync().ConfigureAwait(true)<br/>ClearInternalAsync().ConfigureAwait(true)<br/>PushInternalAsync().ConfigureAwait(true)<br/>AfterChangeAsync().ConfigureAwait(true)
Core-->>Pipeline: Task completes (engine thread inside)
Pipeline-->>Router: returns
else Has around handlers
Pipeline->>Handler: HandleAsync(event, next, ct) ConfigureAwait(false)
Handler->>Core: next() → coreAction()
Note over Core: Runs with ConfigureAwait(true) internally
Core-->>Handler: completes
Note over Handler: Post-next code resumes on thread-pool
Handler-->>Pipeline: completes
Pipeline-->>Router: Task completes
end
Router-->>Caller: resumes on engine thread (ConfigureAwait(true))
Reviews (4): Last reviewed commit: "fix(pr-review): 收敛PR建议并修复构建验证" | Re-trigger Greptile
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (9)
GFramework.Core/StateManagement/StoreSelection.cs (1)
30-30:⚠️ Potential issue | 🟠 Major修复 MA0158:将
object锁替换为System.Threading.Lock第 30 行的
private readonly object _lock = new();触发了 Meziantou.Analyzer 的 MA0158 告警。项目目标框架包含 net9.0,System.Threading.Lock完全可用。根据编码规范,应遵循 Meziantou.Analyzer 建议,直接改用System.Threading.Lock,现有的lock (_lock)调用无需改动。建议修改
- private readonly object _lock = new(); + private readonly System.Threading.Lock _lock = new();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core/StateManagement/StoreSelection.cs` at line 30, 在 StoreSelection 类中将当前字段 private readonly object _lock = new(); 替换为使用 System.Threading.Lock(例如 private readonly System.Threading.Lock _lock = new();),并保留现有的 lock (_lock) 调用不变;定位到 StoreSelection 类和字段名 _lock,改用 System.Threading.Lock 类型以消除 MA0158 警告,确保构造时初始化并保持 readonly 修饰符。GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs (2)
282-306:⚠️ Potential issue | 🟡 Minor与第 126 行同类问题:
async Task方法体没有await
CleanupDuringAcquire_Should_NotCauseRaceCondition同样是async Task但没有任何await(外层循环中的await都在Task.Run的 Lambda 内),会触发 CS1998。建议把第 305 行的Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks))替换为直接await Task.WhenAll(tasks).ConfigureAwait(false),与本文件其它并发测试一致。♻️ 建议修改
- // Assert - Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)); + // Assert - 任意未捕获异常都会导致 await 失败,从而使测试失败 + await Task.WhenAll(tasks).ConfigureAwait(false);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines 282 - 306, The test method CleanupDuringAcquire_Should_NotCauseRaceCondition is declared async Task but contains no await (all awaits are inside Task.Run lambdas), causing CS1998; replace the Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)) call with a direct await of Task.WhenAll(tasks).ConfigureAwait(false) so the outer method actually awaits the tasks and removes the compiler warning, keeping the rest of the test (AsyncKeyLockManager creation and Task.Run loop) unchanged.
107-127:⚠️ Potential issue | 🟡 Minor
async Task测试方法缺少await,会触发 CS1998 警告,与本 PR 目标相悖
ConcurrentAcquire_Should_NotThrowException声明为async Task,但方法体内没有任何await(Assert.DoesNotThrowAsync在 NUnit 中返回void,且这里也未对其await)。这会触发CS1998: This async method lacks 'await' operators,恰好是本批次想要消除的分析器警告类型。同时由于断言不会向上抛出Task异常,逻辑上也只是同步调用。建议直接
awaitTask.WhenAll(tasks)(与本文件其它测试保持一致),让真实的并发异常自然冒泡,断言改为隐式(无异常即通过);或将方法签名改为void。♻️ 建议修改
[Test] - public async Task ConcurrentAcquire_Should_NotThrowException() + public async Task ConcurrentAcquire_Should_NotThrowException() { // Arrange using var manager = new AsyncKeyLockManager(); var tasks = new List<Task>(); // Act for (var i = 0; i < 100; i++) { var key = $"key-{i % 10}"; tasks.Add(Task.Run(async () => { await using var handle = await manager.AcquireLockAsync(key).ConfigureAwait(false); await Task.Delay(1).ConfigureAwait(false); })); } - // Assert - Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)); + // Assert - 任意未捕获异常都会导致 await 失败,从而使测试失败 + await Task.WhenAll(tasks).ConfigureAwait(false); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines 107 - 127, The test ConcurrentAcquire_Should_NotThrowException is declared async Task but never awaits, causing CS1998; replace the Assert.DoesNotThrowAsync(...) call with an actual await of Task.WhenAll(tasks) so the test awaits the concurrent tasks (use await Task.WhenAll(tasks).ConfigureAwait(false)) and let exceptions bubble implicitly; locate this change around the AsyncKeyLockManager usage and the manager.AcquireLockAsync calls.GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs (2)
369-414:⚠️ Potential issue | 🟡 MinorXML 文档与实际抛出的异常类型不一致
SendCommandAsync/SendCommand/SendQueryAsync/SendQuery这几处实现已改为抛出NotSupportedException,但对应的<exception cref="NotImplementedException">文档仍未更新,会误导读者并产生不一致的 API 文档。同样的问题出现在第 369、383、396、410 行。📝 建议的修正(示例)
- /// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception> + /// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>每处
<exception cref="NotImplementedException">均需同步替换。依据编码指南:「Comments MUST NOT be trivial, redundant, or misleading」。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` around lines 369 - 414, Update the XML <exception> tags for the stub methods SendCommandAsync, SendCommand, SendQueryAsync, and SendQuery to reflect that they throw NotSupportedException (replace cref="NotImplementedException" with cref="NotSupportedException") so the documentation matches the actual thrown exception; ensure the descriptive text (e.g., "该测试桩未实现此成员。") is updated if needed to avoid implying NotImplementedException.
274-307:⚠️ Potential issue | 🟡 Minor修复 TestArchitectureContextV3 中的可空返回类型不匹配
IArchitectureContext接口声明的GetService、GetModel、GetSystem、GetUtility方法返回非可空类型(TService、TModel、TSystem、TUtility),并且接口文档明确说明这些方法"如果不存在则抛出异常"。TestArchitectureContextV3的实现将返回类型改为可空(TService?等),引发 CS8766 警告。应删除返回类型中的?以对齐接口签名。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` around lines 274 - 307, The implementation in TestArchitectureContextV3 has nullable return types for methods GetService<TService>(), GetModel<TModel>(), GetSystem<TSystem>(), and GetUtility<TUtility>() (e.g. TService?) which mismatches the IArchitectureContext interface and causes CS8766; update those method signatures to return non-nullable types (remove the '?' from TService?, TModel?, TSystem?, TUtility?) so they match the interface declarations and semantics that missing items should throw.GFramework.Core.Tests/Architectures/GameContextTests.cs (1)
404-532:⚠️ Potential issue | 🟡 MinorXML 文档中的
<exception cref="NotImplementedException">与实际抛出的NotSupportedException不一致本次将 10 处
throw new NotImplementedException();改为throw new NotSupportedException();,但这些方法上方的 XML 注释仍然写着:/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>具体涉及行:404、417、430、444、457、471、484、498、513、527。同 PR 内的
RegistryInitializationHookBaseTests.cs已经把<exception>同步更新为NotSupportedException(参见其行 193/203/338/348),本文件应保持一致。按照《coding guidelines》中“Any change to public API, lifecycle semantics, module behavior, or extension points MUST update the related XML docs”的要求,建议同步更新文档:
♻️ 建议修改(每处类似)
- /// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception> + /// <exception cref="NotSupportedException">该测试桩未实现此成员。</exception>As per coding guidelines: "Any change to public API, lifecycle semantics, module behavior, or extension points MUST update the related XML docs".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Architectures/GameContextTests.cs` around lines 404 - 532, The XML <exception> tags above the test-stub methods (SendRequestAsync, SendRequest, SendCommandAsync, SendCommand, SendQueryAsync, SendQuery, PublishAsync, CreateStream, SendAsync<TCommand>, SendAsync<TResponse>) are out of sync— they reference NotImplementedException while the methods throw NotSupportedException; update each method's XML <exception cref="..."> to cref="System.NotSupportedException" and adjust the description text if needed so the docs match the thrown exception.GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs (1)
290-298:⚠️ Potential issue | 🟠 Major修复 CS8603 可空性返回告警
CI 流水线报告
CS8603: Possible null reference return.(行 294、297)。该 override 的返回类型为TUtility(非可空),但:
- 第 294 行
return _registry as TUtility;中,as运算符在不匹配时返回null;- 第 297 行
return base.GetUtility<TUtility>();调用的基类方法签名为TUtility? GetUtility<TUtility>()(来自TestArchitectureContext),返回类型本就是可空。这两个返回点都可能产生
null,与当前签名声明不匹配。按照《coding guidelines》中“Write null-safe code that respects nullable annotations instead of suppressing warnings by default”的要求,建议让 override 与基类签名保持一致:
♻️ 建议修复
- public override TUtility GetUtility<TUtility>() + public override TUtility? GetUtility<TUtility>() where TUtility : class { if (typeof(TUtility) == typeof(TestRegistry)) { return _registry as TUtility; } return base.GetUtility<TUtility>(); }As per coding guidelines: "Write null-safe code that respects nullable annotations instead of suppressing warnings by default."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs` around lines 290 - 298, The override GetUtility<TUtility>() currently returns a non-nullable TUtility but uses nullable-returning expressions (_registry as TUtility and base.GetUtility<TUtility>()), causing CS8603; change the method signature to match the base and return a nullable by declaring public override TUtility? GetUtility<TUtility>() so the compiler and callers see the nullable contract, keep the existing cast (return _registry as TUtility) and the base call (return base.GetUtility<TUtility>()) unchanged, and update any callers if they assumed a non-null result to handle possible nulls.GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs (1)
92-102:⚠️ Potential issue | 🟡 Minor测试方法保留
async但不再 await,可能触发 CS1998 告警行 96 已将
Assert.ThrowsAsync的委托从async () => await ...简化为() => Architecture.InitializeAsync(),但方法签名仍为async Task且方法体内不再包含任何await。这会触发CS1998: This async method lacks 'await' operators,与 PR "减少分析器告警"的目标相反。同样的问题在
InitializeAsync_Should_Handle_Exception_Correctly(行 137-146)也存在。建议二选一处理:
♻️ 方案 A:去掉
async关键字- public async Task Architecture_Should_Stop_Initialization_When_Model_Init_Fails() + public void Architecture_Should_Stop_Initialization_When_Model_Init_Fails() { Architecture!.AddPostRegistrationHook(a => { a.RegisterModel(new FailingModel()); }); Assert.ThrowsAsync<InvalidOperationException>(() => Architecture.InitializeAsync()); Assert.That( Architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.FailedInitialization) ); }♻️ 方案 B:保留 async,补充 await
- Assert.ThrowsAsync<InvalidOperationException>(() => Architecture.InitializeAsync()); + await Task.Run(() => Assert.ThrowsAsync<InvalidOperationException>( + () => Architecture.InitializeAsync()));(更推荐方案 A)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs` around lines 92 - 102, The test methods Architecture_Should_Stop_Initialization_When_Model_Init_Fails and InitializeAsync_Should_Handle_Exception_Correctly are declared async but contain no await (triggering CS1998); fix by removing the async modifier and keeping the return type Task for both test methods (e.g., change "public async Task ..." to "public Task ..."), or alternatively retain async and await the call inside Assert.ThrowsAsync (e.g., use "await Assert.ThrowsAsync(...)" with an async lambda that awaits Architecture.InitializeAsync()); update the corresponding method signatures and bodies for the chosen option so there are no async methods without awaits.GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs (1)
97-106:⚠️ Potential issue | 🟡 Minor改后该测试方法不再包含任何
await,会触发 CS1998 告警,与本 PR“减少分析器告警”的目标相悖。
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization仍声明为async Task,但在去掉Assert.ThrowsAsync内部的async/await包装后,方法体里已经没有任何await。Assert.ThrowsAsync同步返回捕获的异常,无需在外层方法再异步化。建议将其改为同步签名:
♻️ 建议变更
- public async Task InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization() + public void InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization() { var architecture = new PhaseTrackingArchitecture(() => throw new InvalidOperationException("boom")); var exception = Assert.ThrowsAsync<InvalidOperationException>(() => architecture.InitializeAsync()); Assert.That(exception, Is.Not.Null); Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.FailedInitialization)); Assert.ThrowsAsync<InvalidOperationException>(() => architecture.WaitUntilReadyAsync()); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs` around lines 97 - 106, The test InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization is declared async Task but contains no awaits (uses Assert.ThrowsAsync which returns the exception synchronously), causing CS1998; change the test to a synchronous test by removing the async modifier and changing the signature to public void InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization(), keep the existing calls to Assert.ThrowsAsync(() => architecture.InitializeAsync()) and Assert.ThrowsAsync(() => architecture.WaitUntilReadyAsync()), and keep the PhaseTrackingArchitecture(...) setup and the assertion that architecture.CurrentPhase == ArchitecturePhase.FailedInitialization.
🧹 Nitpick comments (10)
GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs (1)
71-72: 排序对该断言无实际意义,可考虑移除以减少噪音。此处仅断言
files.Length > 1,OrderBy(..., StringComparer.Ordinal)的排序结果未被使用。虽然加上StringComparer.Ordinal能消除 CA1304/CA1311 等分析器告警,但更简洁的做法是直接去掉OrderBy调用。如果保留是为了与第 111-114 行保持一致风格,则当前写法也可接受。♻️ 建议简化
- var files = Directory.GetFiles(_testDir, "*.log").OrderBy(f => f, System.StringComparer.Ordinal).ToArray(); + var files = Directory.GetFiles(_testDir, "*.log"); Assert.That(files.Length, Is.GreaterThan(1));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs` around lines 71 - 72, The assertion collects log files into the local variable files but the subsequent OrderBy(..., StringComparer.Ordinal) has no effect on the assertion; remove the unnecessary OrderBy call in the RollingFileAppenderTests method (replace Directory.GetFiles(_testDir, "*.log").OrderBy(...).ToArray() with Directory.GetFiles(_testDir, "*.log").ToArray()) so the test still asserts files.Length > 1 without the redundant sorting step.GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs (1)
263-265: 残留的async () => await ...包装与 PR 整体目标不一致本 PR 的目标之一是去除
Assert.ThrowsAsync/Assert.CatchAsync/Assert.DoesNotThrowAsync委托中多余的async/await包装(参见第 246 行已改为() => ... .AsTask()),但此处仍保留了async () => await manager.AcquireLockAsync(...).ConfigureAwait(false)。建议与第 246 行保持一致,直接传递ValueTask.AsTask()。注意:去掉
async包装后,ConfigureAwait也随之失去意义(它仅影响await的续约线程行为),因此一并移除。♻️ 建议修改
- Assert.CatchAsync<OperationCanceledException>(async () => - await manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false)); + Assert.CatchAsync<OperationCanceledException>( + () => manager.AcquireLockAsync("test-key", cts.Token).AsTask());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines 263 - 265, Replace the unnecessary async/await wrapper in the Assert.CatchAsync call: instead of using async () => await manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false), pass a non-async delegate that returns the ValueTask as a Task by calling manager.AcquireLockAsync("test-key", cts.Token).AsTask(); remove ConfigureAwait since there is no await in the delegate and ensure Assert.CatchAsync<OperationCanceledException>(() => manager.AcquireLockAsync(...).AsTask()) uses the same pattern as the other test at line ~246.GFramework.Core.Tests/Functional/OptionTests.cs (1)
494-496: 建议考虑为Option<T>.ToString()显式指定文化设置测试代码在第 494-496 行期望
Option<int>.Some(42).ToString()返回"Some(42)"。当前的Option<T>.ToString()实现使用字符串插值隐式调用值的ToString()方法,这依赖于当前的区域性设置。虽然整数42在所有文化中都格式化为相同的字符串,因此测试本身是确定性的,但为与项目中其他文化无关的改进保持一致,建议在实现中显式使用InvariantCulture:public override string ToString() => _isSome ? $"Some({_value?.ToString(System.Globalization.CultureInfo.InvariantCulture) ?? _value})" : "None";这样可以确保所有值类型的格式化都与文化无关,提高代码的一致性和可预测性。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Functional/OptionTests.cs` around lines 494 - 496, The test assumes Option<int>.ToString() yields a culture-independent representation; update the Option<T>.ToString() implementation (the method that checks _isSome and formats _value) to call _value?.ToString(System.Globalization.CultureInfo.InvariantCulture) (falling back to _value if null) when formatting the Some(...) branch so that value formatting is culture-invariant and consistent across locales.GFramework.Game/Storage/ScopedStorage.cs (1)
103-106: 建议:与同类方法保持一致,使用 Task 直返而非async/await本类的
ExistsAsync/ReadAsync/WriteAsync等都是直接return inner.XxxAsync(Key(key));(任务消隐 / task elision),不包async-await,自然也无需ConfigureAwait。当前DeleteAsync单独使用了async + await + ConfigureAwait(false),与本 PR “去除冗余 async/await 包装” 的整体方向相反,且在该方法中没有捕获异常或 finally 等需要状态机的逻辑。♻️ 建议改动
- public async Task DeleteAsync(string key) - { - await inner.DeleteAsync(Key(key)).ConfigureAwait(false); - } + public Task DeleteAsync(string key) + { + return inner.DeleteAsync(Key(key)); + }如果保留
async是为了未来加入装饰逻辑(计时、日志等),可忽略此建议。As per coding guidelines: "Match existing async patterns and naming conventions with
Asyncsuffix for asynchronous methods".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Game/Storage/ScopedStorage.cs` around lines 103 - 106, DeleteAsync currently uses async/await with ConfigureAwait(false) while other methods (ExistsAsync/ReadAsync/WriteAsync) use task elision; change DeleteAsync to directly return inner.DeleteAsync(Key(key)) (remove async/await and ConfigureAwait) so it matches the existing pattern and avoids creating an unnecessary state machine; keep Key(key) and inner.DeleteAsync as the referenced symbols.GFramework.Game/Storage/FileStorage.cs (1)
245-256: 可选:StreamReader与外层await using的所有权重叠
new StreamReader(fs, Encoding.UTF8)默认leaveOpen: false,会在sr.Dispose()时关闭fs;随后configuredFileStream再次DisposeAsync(fs)。FileStream的 Dispose 是幂等的,目前不会出错,但出现了同一资源被两层using同时拥有的语义。若想让所有权更清晰,可让StreamReader不接管底层流:♻️ 建议改动
- using var sr = new StreamReader(fs, Encoding.UTF8); + using var sr = new StreamReader(fs, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);属于风格性建议,不是必须改动。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Game/Storage/FileStorage.cs` around lines 245 - 256, The FileStream created (fs) and the configuredFileStream awaitable are both disposing the same stream while the StreamReader (sr) is constructed with default leaveOpen=false, causing overlapping ownership; update the StreamReader construction in the Read method (where fs, configuredFileStream and sr are used) to pass leaveOpen: true so the StreamReader does not close the underlying FileStream, and let the outer await using configuredFileStream handle disposing the FileStream.GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs (2)
67-72: 第 69 行的同步等待无实际作用,建议移除。
task是Task.FromResult(42),构造时已完成;ConfigureAwait(false).GetAwaiter().GetResult()既不影响instruction.Result的可用性,结果也被丢弃,等同于一次空操作。该行仅是从原.Wait()模式机械替换而来,建议直接删除以减少误导。♻️ 建议变更
- task.ConfigureAwait(false).GetAwaiter().GetResult(); - Assert.That(instruction.Result, Is.EqualTo(42));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs` around lines 67 - 72, 在 TaskCoroutineExtensionsTests 的该测试中,变量 task 是 Task.FromResult(42),因此 调用 task.ConfigureAwait(false).GetAwaiter().GetResult() 是多余的并且不影响 instruction.Result;请删除该调用行(即移除 ConfigureAwait(false).GetAwaiter().GetResult())以避免误导并保持测试简洁,保留前后的 AsCoroutineInstruction() 和 Assert.That(instruction.Result, Is.EqualTo(42)) 不变。
168-168:ConfigureAwait(false).GetAwaiter().GetResult()与.Wait()等价,仅形式更冗长。这几处均是在测试线程上同步阻塞等待
Task.Delay,本质上仍是 sync-over-async;将.Wait()替换为ConfigureAwait(false).GetAwaiter().GetResult()并未改善行为,反而增加噪声。如果此改动是为了消除某条具体的分析器告警(例如 VSTHRD002/MA0042),建议改用await Task.Delay(50).ConfigureAwait(false);并将测试方法标记为async Task,或者使用TaskCompletionSource直接驱动调度器,避免依赖时序。Also applies to: 192-192, 268-268
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs` at line 168, The test code is synchronously blocking on Task.Delay via ConfigureAwait(false).GetAwaiter().GetResult(), which is redundant and causes sync-over-async; replace these calls in TaskCoroutineExtensionsTests (the Task.Delay(...) occurrences) with asynchronous awaits (await Task.Delay(50).ConfigureAwait(false)) and make the enclosing test methods return async Task, or alternatively drive the scheduler deterministically using a TaskCompletionSource to avoid timing-dependent sleeps; update each test method that currently calls ConfigureAwait(false).GetAwaiter().GetResult() (the instances around the current occurrences) accordingly.GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs (1)
282-299: 该测试中ConfigureAwait(false)改动与其他测试不一致,建议保持风格统一。整个文件其它测试仍在使用
Task.Delay(...).Wait()阻塞写法(如第 45、58、62、78、93、97、136、152、165、169、186、273、295、312 行),仅此测试改为ConfigureAwait(false)。本测试方法已经是async Task,第 295 行又用.Wait()阻塞,整体风格不一致:
- 若目标是统一为异步等待,建议把第 295 行也改成
await Task.Delay(100).ConfigureAwait(false);,并对文件其它测试做同样处理;- 若目标只是消除某条特定分析器告警,建议在改动文件内保持单一风格,避免出现混合写法对后续维护者产生困惑。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs` around lines 282 - 299, In WaitForTaskOfT_Should_Get_Result_From_Async_Operation replace the blocking Task.Delay(100).Wait() with an asynchronous await (await Task.Delay(100).ConfigureAwait(false)); keep the test method async and maintain the existing ConfigureAwait(false) style used inside the Task.Run; this makes the waiting consistent with the earlier await Task.Delay(50).ConfigureAwait(false) and matches the file’s async-await style.GFramework.Game/Scene/SceneRouterBase.cs (1)
375-426: 建议:补齐其它三个 Core 方法的设计注释,或提升为 region 级说明行 380 在
ExecuteReplaceCoreAsync中已注明"场景生命周期回调可能依赖引擎线程,因此这里保留默认 await 行为",但ExecutePushCoreAsync/ExecutePopCoreAsync/ExecuteClearCoreAsync依赖的是同一设计意图却未注释,未来读者容易把这些裸 await 误读为遗漏。建议把说明提升为Helper Methodsregion 顶端的统一注释,或在每个 Core 方法上各自标注一行。可选项:若启用了 Meziantou MA0004,将这些 await 显式改为
.ConfigureAwait(true)既能消除告警,也能与UiRouterBase的写法保持一致;若 MA0004 不强制要求,则保持默认 await + 集中注释即可。♻️ 建议变更示例(任选其一)
集中注释:
`#region` Helper Methods + // 注:以下 ExecuteXCoreAsync 内的 await 故意保留默认 ConfigureAwait(true), + // 因为场景生命周期回调(OnLoad/OnEnter/OnExit/OnUnload/OnPause/OnResume) + // 通常需要回到引擎主线程执行。 + /// <summary> /// 执行 Replace 的核心切换顺序。 /// </summary> @@ private async Task ExecuteReplaceCoreAsync( SceneTransitionEvent `@event`, string sceneKey, ISceneEnterParam? param) { - // 场景生命周期回调可能依赖引擎线程,因此这里保留默认 await 行为。 await BeforeChangeAsync(`@event`); await ClearInternalAsync(); await PushInternalAsync(sceneKey, param); await AfterChangeAsync(`@event`); }或显式
ConfigureAwait(true):- await BeforeChangeAsync(`@event`); - await ClearInternalAsync(); - await PushInternalAsync(sceneKey, param); - await AfterChangeAsync(`@event`); + await BeforeChangeAsync(`@event`).ConfigureAwait(true); + await ClearInternalAsync().ConfigureAwait(true); + await PushInternalAsync(sceneKey, param).ConfigureAwait(true); + await AfterChangeAsync(`@event`).ConfigureAwait(true);As per coding guidelines, "Add inline comments for non-trivial logic, concurrency or threading behavior" 以及 "Treat
Meziantou.Analyzerfeedback as part of the coding standard"。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Game/Scene/SceneRouterBase.cs` around lines 375 - 426, The comment notes that the threading rationale currently documented in ExecuteReplaceCoreAsync should be applied to the other core switch methods to avoid confusion or MA0004 warnings; update ExecutePushCoreAsync, ExecutePopCoreAsync, and ExecuteClearCoreAsync (or add a single region-level summary above these Helper Methods) to include the same note about lifecycle callbacks depending on the engine/UI thread and intentionally keeping the default await behavior, or alternatively change each await to use .ConfigureAwait(true) to silence Meziantou MA0004 and match UiRouterBase; reference the methods ExecutePushCoreAsync, ExecutePopCoreAsync, ExecuteClearCoreAsync and the existing ExecuteReplaceCoreAsync when making the change.GFramework.Game/UI/UiRouterBase.cs (1)
102-235: LGTM:显式ConfigureAwait(true)表达 UI 线程亲和意图
_pipeline内部对 lambda 的 await 使用ConfigureAwait(false),而 lambda 自身续延的上下文由其内部 await 的 ConfigureAwait 决定,所以这里在 lambda 内逐个 await 与外层ExecuteAroundAsync的 await 都标注ConfigureAwait(true)是必要的,可在保留引擎线程亲和的同时满足 Meziantou MA0004。BeforeChangeAsync/AfterChangeAsync中的_pipeline.ExecuteAsync(...).ConfigureAwait(true)与SceneRouterBase中的ConfigureAwait(false)形成对照(场景生命周期与 UI 线程亲和需求不同),是预期的差异。可选项(不强制):与
SceneRouterBase.cs一致地把Push/Replace/Pop/Clear中的 inlineasync () => { ... }抽取为命名的ExecuteXCoreAsync私有方法,可降低嵌套 lambda 的状态机开销并提升可读性。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@GFramework.Game/UI/UiRouterBase.cs` around lines 102 - 235, Summary: The ConfigureAwait(true) usage inside the _pipeline.ExecuteAroundAsync lambdas is intentional to preserve UI-thread affinity, so keep them and optionally refactor lambdas to named private methods to reduce state-machine overhead. Leave the current ConfigureAwait(true) on awaits of BeforeChangeAsync/AfterChangeAsync and the outer ExecuteAroundAsync await as-is; if you want to refactor, extract each inline async () => { ... } block into a private method (e.g., ExecutePushCoreAsync, ExecuteReplaceCoreAsync, ExecutePopCoreAsync, ExecuteClearCoreAsync) that performs the same sequence (BeforeChangeAsync, Do*Internal/DoPushPageInternal, AfterChangeAsync) and pass that method to _pipeline.ExecuteAroundAsync to improve readability and avoid nested lambda state machines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md`:
- Line 51: Replace the ambiguous term "缺件" with a clearer phrase like "缺失组件" or
"缺失工作负载组件" wherever it appears in the analyzer-warning-reduction tracking note
mentioning Microsoft.NET.SDK.WorkloadAutoImportPropsLocator; update the sentence
"优先修复或绕过 `Microsoft.NET.SDK.WorkloadAutoImportPropsLocator` 缺件" to read e.g.
"优先修复或绕过 `Microsoft.NET.SDK.WorkloadAutoImportPropsLocator` 的缺失工作负载组件" and
ensure the surrounding tracking text follows the guideline by explicitly stating
completed actions, discovered issues, validation results, and the recommended
next recovery point.
In `@GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs`:
- Around line 124-128: The test uses a non-async lambda that returns a
ConfiguredTaskAwaitable (ct => Task.Delay(..., ct).ConfigureAwait(false)), which
cannot be converted to the expected Func<CancellationToken, Task> for
AsyncExtensions.WithTimeoutAsync; remove the unnecessary ConfigureAwait(false)
so the lambda returns a Task (e.g., use ct =>
Task.Delay(TimeSpan.FromSeconds(2), ct)), or alternatively make the lambda async
(async ct => { await Task.Delay(..., ct); }) — update the assertion in
AsyncExtensionsTests to pass a Func<CancellationToken, Task> to
WithTimeoutAsync.
In `@GFramework.Core.Tests/State/StateMachineSystemTests.cs`:
- Around line 138-142: The test method DestroyAsync_Should_Not_Throw_Exception
is declared with the async modifier but contains no awaits (it uses
Assert.That(() => _stateMachine!.DestroyAsync(), Throws.Nothing)), which
triggers CS1998; remove the async keyword and make the method return Task (i.e.,
change the signature of DestroyAsync_Should_Not_Throw_Exception from "async
Task" to "Task") so the test returns the Task produced by
_stateMachine!.DestroyAsync() without introducing an unused async state machine.
In `@GFramework.Core/Extensions/ContextAwareCommandExtensions.cs`:
- Around line 22-30: The explicit null-checks in
ContextAwareCommandExtensions.cs (and other changed files like
ContextAwareQueryExtensions.cs, AsyncExtensions.cs,
ContextAwareEventExtensions.cs, NumericExtensions.cs) replace
ArgumentNullException.ThrowIfNull(...) and therefore conflict with
CA1510/Meziantou.Analyzer; revert those explicit if-throws back to using
ArgumentNullException.ThrowIfNull(contextAware) and
ArgumentNullException.ThrowIfNull(command) (and equivalent parameters in the
other files) so the analyzer's recommended pattern is followed, or alternatively
add an .editorconfig entry to explicitly configure CA1510 if you intend to keep
the explicit checks.
In `@GFramework.Core/Extensions/NumericExtensions.cs`:
- Around line 28-35: The three null-checks in Between<T> (checking value, min,
max) use single-line if statements; change them to use Allman-style braces with
4-space indentation (e.g., if (value is null) { throw new
ArgumentNullException(nameof(value)); }) for each of value, min, and max to
comply with the project's brace style and avoid the CA1510 reverse-refactor
issue; update the method Between<T> accordingly so all three checks consistently
use braces.
In `@GFramework.Core/Extensions/StringExtensions.cs`:
- Around line 41-46: Replace single-line if statements in
GFramework.Core.Extensions.StringExtensions (e.g., the null checks for str and
suffix) with Allman-style braces; specifically change the single-line checks "if
(str is null) throw new ArgumentNullException(nameof(str));" and "if (suffix is
null) throw new ArgumentNullException(nameof(suffix));" and the other
single-line ifs around the later checks (the ones at the 72-76 range) to use
block braces on their own lines (open brace on the same line as the if, close
brace on its own line) and keep existing indentation to conform to the
repository's Allman C# brace style.
In `@GFramework.Core/StateManagement/StoreBuilder.cs`:
- Around line 42-44: The null-checks in StoreBuilder.cs (notably in
UseMiddleware and the two AddReducer overloads AddReducer<TAction>(Func<...>)
and AddReducer<TAction>(IReducer<...>)) use single-line if statements without
braces which violates the Allman brace style and triggers the CA1510
reverse-refactor issue; update these checks to match the PR's other files by
either replacing the single-line ifs with braced Allman-style blocks or,
preferably for consistency with ContextAwareCommandExtensions.cs, simplify them
to ArgumentNullException.ThrowIfNull(...) for each parameter so the null checks
are uniform across the codebase.
---
Outside diff comments:
In `@GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs`:
- Around line 97-106: The test
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization is
declared async Task but contains no awaits (uses Assert.ThrowsAsync which
returns the exception synchronously), causing CS1998; change the test to a
synchronous test by removing the async modifier and changing the signature to
public void
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization(),
keep the existing calls to Assert.ThrowsAsync(() =>
architecture.InitializeAsync()) and Assert.ThrowsAsync(() =>
architecture.WaitUntilReadyAsync()), and keep the PhaseTrackingArchitecture(...)
setup and the assertion that architecture.CurrentPhase ==
ArchitecturePhase.FailedInitialization.
In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs`:
- Around line 369-414: Update the XML <exception> tags for the stub methods
SendCommandAsync, SendCommand, SendQueryAsync, and SendQuery to reflect that
they throw NotSupportedException (replace cref="NotImplementedException" with
cref="NotSupportedException") so the documentation matches the actual thrown
exception; ensure the descriptive text (e.g., "该测试桩未实现此成员。") is updated if
needed to avoid implying NotImplementedException.
- Around line 274-307: The implementation in TestArchitectureContextV3 has
nullable return types for methods GetService<TService>(), GetModel<TModel>(),
GetSystem<TSystem>(), and GetUtility<TUtility>() (e.g. TService?) which
mismatches the IArchitectureContext interface and causes CS8766; update those
method signatures to return non-nullable types (remove the '?' from TService?,
TModel?, TSystem?, TUtility?) so they match the interface declarations and
semantics that missing items should throw.
In `@GFramework.Core.Tests/Architectures/GameContextTests.cs`:
- Around line 404-532: The XML <exception> tags above the test-stub methods
(SendRequestAsync, SendRequest, SendCommandAsync, SendCommand, SendQueryAsync,
SendQuery, PublishAsync, CreateStream, SendAsync<TCommand>,
SendAsync<TResponse>) are out of sync— they reference NotImplementedException
while the methods throw NotSupportedException; update each method's XML
<exception cref="..."> to cref="System.NotSupportedException" and adjust the
description text if needed so the docs match the thrown exception.
In `@GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs`:
- Around line 290-298: The override GetUtility<TUtility>() currently returns a
non-nullable TUtility but uses nullable-returning expressions (_registry as
TUtility and base.GetUtility<TUtility>()), causing CS8603; change the method
signature to match the base and return a nullable by declaring public override
TUtility? GetUtility<TUtility>() so the compiler and callers see the nullable
contract, keep the existing cast (return _registry as TUtility) and the base
call (return base.GetUtility<TUtility>()) unchanged, and update any callers if
they assumed a non-null result to handle possible nulls.
In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs`:
- Around line 282-306: The test method
CleanupDuringAcquire_Should_NotCauseRaceCondition is declared async Task but
contains no await (all awaits are inside Task.Run lambdas), causing CS1998;
replace the Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)) call with a
direct await of Task.WhenAll(tasks).ConfigureAwait(false) so the outer method
actually awaits the tasks and removes the compiler warning, keeping the rest of
the test (AsyncKeyLockManager creation and Task.Run loop) unchanged.
- Around line 107-127: The test ConcurrentAcquire_Should_NotThrowException is
declared async Task but never awaits, causing CS1998; replace the
Assert.DoesNotThrowAsync(...) call with an actual await of Task.WhenAll(tasks)
so the test awaits the concurrent tasks (use await
Task.WhenAll(tasks).ConfigureAwait(false)) and let exceptions bubble implicitly;
locate this change around the AsyncKeyLockManager usage and the
manager.AcquireLockAsync calls.
In `@GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs`:
- Around line 92-102: The test methods
Architecture_Should_Stop_Initialization_When_Model_Init_Fails and
InitializeAsync_Should_Handle_Exception_Correctly are declared async but contain
no await (triggering CS1998); fix by removing the async modifier and keeping the
return type Task for both test methods (e.g., change "public async Task ..." to
"public Task ..."), or alternatively retain async and await the call inside
Assert.ThrowsAsync (e.g., use "await Assert.ThrowsAsync(...)" with an async
lambda that awaits Architecture.InitializeAsync()); update the corresponding
method signatures and bodies for the chosen option so there are no async methods
without awaits.
In `@GFramework.Core/StateManagement/StoreSelection.cs`:
- Line 30: 在 StoreSelection 类中将当前字段 private readonly object _lock = new(); 替换为使用
System.Threading.Lock(例如 private readonly System.Threading.Lock _lock =
new();),并保留现有的 lock (_lock) 调用不变;定位到 StoreSelection 类和字段名 _lock,改用
System.Threading.Lock 类型以消除 MA0158 警告,确保构造时初始化并保持 readonly 修饰符。
---
Nitpick comments:
In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs`:
- Around line 263-265: Replace the unnecessary async/await wrapper in the
Assert.CatchAsync call: instead of using async () => await
manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false), pass a
non-async delegate that returns the ValueTask as a Task by calling
manager.AcquireLockAsync("test-key", cts.Token).AsTask(); remove ConfigureAwait
since there is no await in the delegate and ensure
Assert.CatchAsync<OperationCanceledException>(() =>
manager.AcquireLockAsync(...).AsTask()) uses the same pattern as the other test
at line ~246.
In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs`:
- Around line 67-72: 在 TaskCoroutineExtensionsTests 的该测试中,变量 task 是
Task.FromResult(42),因此 调用 task.ConfigureAwait(false).GetAwaiter().GetResult()
是多余的并且不影响 instruction.Result;请删除该调用行(即移除
ConfigureAwait(false).GetAwaiter().GetResult())以避免误导并保持测试简洁,保留前后的
AsCoroutineInstruction() 和 Assert.That(instruction.Result, Is.EqualTo(42)) 不变。
- Line 168: The test code is synchronously blocking on Task.Delay via
ConfigureAwait(false).GetAwaiter().GetResult(), which is redundant and causes
sync-over-async; replace these calls in TaskCoroutineExtensionsTests (the
Task.Delay(...) occurrences) with asynchronous awaits (await
Task.Delay(50).ConfigureAwait(false)) and make the enclosing test methods return
async Task, or alternatively drive the scheduler deterministically using a
TaskCompletionSource to avoid timing-dependent sleeps; update each test method
that currently calls ConfigureAwait(false).GetAwaiter().GetResult() (the
instances around the current occurrences) accordingly.
In `@GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs`:
- Around line 282-299: In WaitForTaskOfT_Should_Get_Result_From_Async_Operation
replace the blocking Task.Delay(100).Wait() with an asynchronous await (await
Task.Delay(100).ConfigureAwait(false)); keep the test method async and maintain
the existing ConfigureAwait(false) style used inside the Task.Run; this makes
the waiting consistent with the earlier await
Task.Delay(50).ConfigureAwait(false) and matches the file’s async-await style.
In `@GFramework.Core.Tests/Functional/OptionTests.cs`:
- Around line 494-496: The test assumes Option<int>.ToString() yields a
culture-independent representation; update the Option<T>.ToString()
implementation (the method that checks _isSome and formats _value) to call
_value?.ToString(System.Globalization.CultureInfo.InvariantCulture) (falling
back to _value if null) when formatting the Some(...) branch so that value
formatting is culture-invariant and consistent across locales.
In `@GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs`:
- Around line 71-72: The assertion collects log files into the local variable
files but the subsequent OrderBy(..., StringComparer.Ordinal) has no effect on
the assertion; remove the unnecessary OrderBy call in the
RollingFileAppenderTests method (replace Directory.GetFiles(_testDir,
"*.log").OrderBy(...).ToArray() with Directory.GetFiles(_testDir,
"*.log").ToArray()) so the test still asserts files.Length > 1 without the
redundant sorting step.
In `@GFramework.Game/Scene/SceneRouterBase.cs`:
- Around line 375-426: The comment notes that the threading rationale currently
documented in ExecuteReplaceCoreAsync should be applied to the other core switch
methods to avoid confusion or MA0004 warnings; update ExecutePushCoreAsync,
ExecutePopCoreAsync, and ExecuteClearCoreAsync (or add a single region-level
summary above these Helper Methods) to include the same note about lifecycle
callbacks depending on the engine/UI thread and intentionally keeping the
default await behavior, or alternatively change each await to use
.ConfigureAwait(true) to silence Meziantou MA0004 and match UiRouterBase;
reference the methods ExecutePushCoreAsync, ExecutePopCoreAsync,
ExecuteClearCoreAsync and the existing ExecuteReplaceCoreAsync when making the
change.
In `@GFramework.Game/Storage/FileStorage.cs`:
- Around line 245-256: The FileStream created (fs) and the configuredFileStream
awaitable are both disposing the same stream while the StreamReader (sr) is
constructed with default leaveOpen=false, causing overlapping ownership; update
the StreamReader construction in the Read method (where fs, configuredFileStream
and sr are used) to pass leaveOpen: true so the StreamReader does not close the
underlying FileStream, and let the outer await using configuredFileStream handle
disposing the FileStream.
In `@GFramework.Game/Storage/ScopedStorage.cs`:
- Around line 103-106: DeleteAsync currently uses async/await with
ConfigureAwait(false) while other methods (ExistsAsync/ReadAsync/WriteAsync) use
task elision; change DeleteAsync to directly return inner.DeleteAsync(Key(key))
(remove async/await and ConfigureAwait) so it matches the existing pattern and
avoids creating an unnecessary state machine; keep Key(key) and
inner.DeleteAsync as the referenced symbols.
In `@GFramework.Game/UI/UiRouterBase.cs`:
- Around line 102-235: Summary: The ConfigureAwait(true) usage inside the
_pipeline.ExecuteAroundAsync lambdas is intentional to preserve UI-thread
affinity, so keep them and optionally refactor lambdas to named private methods
to reduce state-machine overhead. Leave the current ConfigureAwait(true) on
awaits of BeforeChangeAsync/AfterChangeAsync and the outer ExecuteAroundAsync
await as-is; if you want to refactor, extract each inline async () => { ... }
block into a private method (e.g., ExecutePushCoreAsync,
ExecuteReplaceCoreAsync, ExecutePopCoreAsync, ExecuteClearCoreAsync) that
performs the same sequence (BeforeChangeAsync, Do*Internal/DoPushPageInternal,
AfterChangeAsync) and pass that method to _pipeline.ExecuteAroundAsync to
improve readability and avoid nested lambda state machines.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d2fce200-1636-4233-be9b-4a9050ee5fac
📒 Files selected for processing (75)
GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.csGFramework.Core.Tests/Architectures/ArchitectureContextTests.csGFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.csGFramework.Core.Tests/Architectures/ArchitectureServicesTests.csGFramework.Core.Tests/Architectures/GameContextTests.csGFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.csGFramework.Core.Tests/Command/AbstractAsyncCommandTests.csGFramework.Core.Tests/Command/CommandExecutorTests.csGFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.csGFramework.Core.Tests/Coroutine/AsyncOperationTests.csGFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.csGFramework.Core.Tests/Coroutine/QueryCoroutineExtensionsTests.csGFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.csGFramework.Core.Tests/Coroutine/WaitForTaskTTests.csGFramework.Core.Tests/Coroutine/WaitForTaskTests.csGFramework.Core.Tests/Extensions/AsyncExtensionsTests.csGFramework.Core.Tests/Extensions/CollectionExtensionsTests.csGFramework.Core.Tests/Extensions/ResultExtensionsTests.csGFramework.Core.Tests/Functional/OptionTests.csGFramework.Core.Tests/Functional/Pipe/PipeExtensionsTests.csGFramework.Core.Tests/Functional/ResultTTests.csGFramework.Core.Tests/Functional/ResultTests.csGFramework.Core.Tests/Localization/LocalizationTableTests.csGFramework.Core.Tests/Logging/DefaultLogFormatterTests.csGFramework.Core.Tests/Logging/JsonLogFormatterTests.csGFramework.Core.Tests/Logging/LogContextTests.csGFramework.Core.Tests/Logging/LogEntryTests.csGFramework.Core.Tests/Logging/RollingFileAppenderTests.csGFramework.Core.Tests/Model/AsyncTestModel.csGFramework.Core.Tests/Pause/PauseStackManagerTests.csGFramework.Core.Tests/Query/AbstractAsyncQueryTests.csGFramework.Core.Tests/Query/AsyncQueryExecutorTests.csGFramework.Core.Tests/Resource/ResourceManagerTests.csGFramework.Core.Tests/Rule/ContextAwareEnvironmentExtensionsTests.csGFramework.Core.Tests/Rule/ContextAwareServiceExtensionsTests.csGFramework.Core.Tests/State/StateMachineSystemTests.csGFramework.Core.Tests/State/StateMachineTests.csGFramework.Core.Tests/State/StateTests.csGFramework.Core.Tests/Systems/AsyncTestSystem.csGFramework.Core.Tests/Tests/AsyncArchitectureTests.csGFramework.Core/Extensions/AsyncExtensions.csGFramework.Core/Extensions/CollectionExtensions.csGFramework.Core/Extensions/ContextAwareCommandExtensions.csGFramework.Core/Extensions/ContextAwareEnvironmentExtensions.csGFramework.Core/Extensions/ContextAwareEventExtensions.csGFramework.Core/Extensions/ContextAwareQueryExtensions.csGFramework.Core/Extensions/ContextAwareServiceExtensions.csGFramework.Core/Extensions/GuardExtensions.csGFramework.Core/Extensions/NumericExtensions.csGFramework.Core/Extensions/StoreEventBusExtensions.csGFramework.Core/Extensions/StringExtensions.csGFramework.Core/StateManagement/StoreBuilder.csGFramework.Core/StateManagement/StoreSelection.csGFramework.Game.Tests/Config/ArchitectureConfigIntegrationTests.csGFramework.Game.Tests/Config/GameConfigBootstrapTests.csGFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.csGFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.csGFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.csGFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.csGFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.csGFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.csGFramework.Game.Tests/Config/YamlConfigLoaderTests.csGFramework.Game.Tests/Config/YamlConfigTextValidatorTests.csGFramework.Game.Tests/Data/PersistenceTests.csGFramework.Game/Routing/RouterBase.csGFramework.Game/Scene/SceneRouterBase.csGFramework.Game/Scene/SceneTransitionPipeline.csGFramework.Game/Setting/SettingsSystem.csGFramework.Game/Storage/FileStorage.csGFramework.Game/Storage/ScopedStorage.csGFramework.Game/UI/UiRouterBase.csGFramework.Game/UI/UiTransitionPipeline.csGFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.csai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.mdai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
- 修复 AsyncExtensionsTests 中错误返回 ConfiguredTaskAwaitable 导致的测试编译失败 - 收敛多处测试中的冗余 async/await 与 ValueTask 断言包装,减少 PR review 指出的告警 - 更新 StoreSelection 的 net9+ 锁实现与 analyzer-warning-reduction 跟踪文档,记录 PR #288 与当前 MSB4018 环境阻塞
Summary
Test ResultsDetails
Insights
Fail Rate
build-and-test: Run #967
🎉 All tests passed!Slowest Tests
± Comparison with run #963 at eaf80f1 | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 30 runs. Github Test Reporter by CTRF 💚 |
- 将 ConfigureAwait 参数从 false 修改为 true - 确保异步操作在正确的上下文中继续执行 - 避免潜在的死锁或性能问题
Summary
Test ResultsDetails
Insights
Fail Rate
build-and-test: Run #968
🎉 All tests passed!Slowest Tests
± Comparison with run #967 at f33a4e8 | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 31 runs. Github Test Reporter by CTRF 💚 |
- 修复 PR #288 中经本地复核后仍成立的 Core、Game 与测试建议 - 更新 WSL 标准 dotnet build 验证路径并确认 Release 构建可通过 - 补充 analyzer-warning-reduction 跟踪文档记录本轮结论与恢复点
Summary
Test ResultsDetails
Insights
Fail Rate
build-and-test: Run #970
🎉 All tests passed!Slowest Tests
± Comparison with run #968 at c7e4b3c | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 32 runs. Github Test Reporter by CTRF 💚 |
Summary by CodeRabbit
改进
重构