From fdf7382717f2cfb5f947029684cdf5613aed1e4d Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:38:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(sourcegenerators):=20=E6=94=B6=E5=8F=A3PR?= =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E9=81=97=E7=95=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 PR review 指出的 XML 文档位置、快照路径防御与换行归一化细节 - 更新 monster schema snapshot 场景,覆盖 dependentRequired、dependentSchemas、allOf 与 if/then/else 约束文档 - 补充 SchemaConfigGenerator 条件与组合校验 helper 的 XML 文档,并同步 ai-plan 恢复点与验证记录 --- .../Config/SchemaConfigGenerator.cs | 104 ++++++++++++++++++ .../AutoRegisterModuleGeneratorTests.cs | 2 +- .../SchemaConfigGeneratorSnapshotTests.cs | 56 +++++++++- .../SchemaConfigGenerator/MonsterConfig.g.txt | 2 +- .../Core/GeneratorSnapshotTest.cs | 8 +- .../analyzer-warning-reduction-tracking.md | 21 +++- .../analyzer-warning-reduction-trace.md | 17 +++ 7 files changed, 200 insertions(+), 10 deletions(-) diff --git a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs index daabab58..6d92383c 100644 --- a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs +++ b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs @@ -1270,6 +1270,17 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证单个 dependentRequired 触发项的声明形状。 + /// 该 helper 先锁定 trigger 字段本身是否属于当前对象,再把每个 target 交给更细粒度的 sibling 校验, + /// 让诊断能够明确区分“触发字段不存在”和“依赖目标非法”两类失败语义。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// 当前 dependentRequired 触发项。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前 dependentRequired 触发项是否有效。 private static bool TryValidateDependentRequiredEntry( string filePath, string displayPath, @@ -1317,6 +1328,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证单个 dependentRequired target 是否为已声明的 sibling 字段名。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// 触发依赖的字段名。 + /// 当前 target 元素。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前 dependentRequired target 是否有效。 private static bool TryValidateDependentRequiredTarget( string filePath, string displayPath, @@ -1497,6 +1518,15 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证单个 dependentSchemas 触发项是否保持为当前运行时支持的 object 子 schema 形状。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// 当前 dependentSchemas 触发项。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前 dependentSchemas 触发项是否有效。 private static bool TryValidateDependentSchemaEntry( string filePath, string displayPath, @@ -1690,6 +1720,15 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证单个 allOf 条目是否维持 object-valued、object-typed 的 focused constraint 形状。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// 当前 allOf 条目。 + /// 从 0 开始的条目索引。 + /// 失败时返回的诊断。 + /// 当前 allOf 条目形状是否有效。 private static bool TryValidateAllOfEntryShape( string filePath, string displayPath, @@ -1854,6 +1893,21 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out diagnostic); } + /// + /// 验证 object-focused 条件分支集合。 + /// if 分支始终必检,then / else 仅在声明时校验, + /// 以保持生成器对分支缺失与分支内容错误的诊断顺序稳定。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// if 分支 schema。 + /// 是否声明 then。 + /// then 分支 schema。 + /// 是否声明 else。 + /// else 分支 schema。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前条件分支集合是否有效。 private static bool TryValidateConditionalSchemaBranches( string filePath, string displayPath, @@ -1898,6 +1952,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out diagnostic); } + /// + /// 验证 object-focused if / then / else 的存在性组合是否合法。 + /// + /// Schema 文件路径。 + /// 父对象逻辑路径。 + /// 是否声明 if。 + /// 是否声明 then。 + /// 是否声明 else。 + /// 失败时返回的诊断。 + /// 当前条件关键字组合是否有效。 private static bool TryValidateConditionalSchemaPresence( string filePath, string displayPath, @@ -2060,6 +2124,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out diagnostic); } + /// + /// 验证 object-focused 条件 schema 的 properties 只引用父对象已声明字段。 + /// + /// Schema 文件路径。 + /// 当前分支逻辑路径。 + /// 分支标签。 + /// 当前分支 schema。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前分支 properties 是否有效。 private static bool TryValidateObjectFocusedSchemaProperties( string filePath, string displayPath, @@ -2104,6 +2178,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证 object-focused 条件 schema 的 required 约束只引用父对象已声明字段。 + /// + /// Schema 文件路径。 + /// 当前分支逻辑路径。 + /// 分支标签。 + /// 当前分支 schema。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前分支 required 是否有效。 private static bool TryValidateObjectFocusedSchemaRequiredProperties( string filePath, string displayPath, @@ -2211,6 +2295,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out diagnostic); } + /// + /// 验证单个 allOf 条目的 properties 映射不会引入父对象未声明字段。 + /// + /// Schema 文件路径。 + /// 当前 allOf 条目逻辑路径。 + /// 当前 allOf 条目。 + /// 从 0 开始的条目索引。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前 allOf 条目的 properties 映射是否有效。 private static bool TryValidateAllOfEntryProperties( string filePath, string allOfEntryPath, @@ -2255,6 +2349,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + /// + /// 验证单个 allOf 条目的 required 约束不会引用父对象未声明字段。 + /// + /// Schema 文件路径。 + /// 当前 allOf 条目逻辑路径。 + /// 当前 allOf 条目。 + /// 从 0 开始的条目索引。 + /// 父对象已声明属性集合。 + /// 失败时返回的诊断。 + /// 当前 allOf 条目的 required 约束是否有效。 private static bool TryValidateAllOfEntryRequiredProperties( string filePath, string allOfEntryPath, diff --git a/GFramework.SourceGenerators.Tests/Architectures/AutoRegisterModuleGeneratorTests.cs b/GFramework.SourceGenerators.Tests/Architectures/AutoRegisterModuleGeneratorTests.cs index 1ad31c14..513f3693 100644 --- a/GFramework.SourceGenerators.Tests/Architectures/AutoRegisterModuleGeneratorTests.cs +++ b/GFramework.SourceGenerators.Tests/Architectures/AutoRegisterModuleGeneratorTests.cs @@ -3,10 +3,10 @@ using GFramework.SourceGenerators.Tests.Core; namespace GFramework.SourceGenerators.Tests.Architectures; -[TestFixture] /// /// 验证 在模块自动注册场景下的生成契约与输出顺序。 /// +[TestFixture] public class AutoRegisterModuleGeneratorTests { private const string AttributeOrderSource = """ diff --git a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs index 0d9414f6..78741010 100644 --- a/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs +++ b/GFramework.SourceGenerators.Tests/Config/SchemaConfigGeneratorSnapshotTests.cs @@ -129,6 +129,58 @@ public class SchemaConfigGeneratorSnapshotTests "type": "string", "enum": ["coin", "gem"] } + }, + "dependentRequired": { + "currency": ["gold"] + }, + "dependentSchemas": { + "currency": { + "type": "object", + "required": ["gold"], + "properties": { + "gold": { + "type": "integer" + } + } + } + }, + "allOf": [ + { + "type": "object", + "required": ["gold"], + "properties": { + "gold": { + "type": "integer" + } + } + } + ], + "if": { + "type": "object", + "properties": { + "currency": { + "type": "string", + "const": "gem" + } + } + }, + "then": { + "type": "object", + "required": ["gold"], + "properties": { + "gold": { + "type": "integer" + } + } + }, + "else": { + "type": "object", + "required": ["currency"], + "properties": { + "currency": { + "type": "string" + } + } } }, "phases": { @@ -156,8 +208,8 @@ public class SchemaConfigGeneratorSnapshotTests """; /// - /// 验证一个最小 monster schema 能生成配置类型、表包装和注册辅助。 - /// + /// 验证一个最小 monster schema 能生成配置类型、表包装和注册辅助。 + /// [Test] public Task Snapshot_SchemaConfigGenerator() { diff --git a/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfig.g.txt b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfig.g.txt index 5f3aa997..ea36995f 100644 --- a/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfig.g.txt +++ b/GFramework.SourceGenerators.Tests/Config/snapshots/SchemaConfigGenerator/MonsterConfig.g.txt @@ -78,7 +78,7 @@ public sealed partial class MonsterConfig /// Reward payload. /// /// - /// Constraints: minProperties = 2, maxProperties = 2. + /// Constraints: minProperties = 2, maxProperties = 2, dependentRequired = { currency => [gold] }, dependentSchemas = { currency => object (required = [gold]) }, allOf = [ object (required = [gold]) ], if/then/else = if object; properties = { currency: string (const = "gem") }; then object (required = [gold]); properties = { gold: integer }; else object (required = [currency]); properties = { currency: string }. /// public sealed partial class RewardConfig { diff --git a/GFramework.SourceGenerators.Tests/Core/GeneratorSnapshotTest.cs b/GFramework.SourceGenerators.Tests/Core/GeneratorSnapshotTest.cs index a9744708..725eb107 100644 --- a/GFramework.SourceGenerators.Tests/Core/GeneratorSnapshotTest.cs +++ b/GFramework.SourceGenerators.Tests/Core/GeneratorSnapshotTest.cs @@ -44,7 +44,7 @@ public static class GeneratorSnapshotTest /// 标准化后的文本 private static string Normalize(string text) { - return text.Replace("\r\n", "\n").Trim(); + return text.Replace("\r\n", "\n", StringComparison.Ordinal).Trim(); } /// @@ -194,7 +194,11 @@ public static class GeneratorSnapshotTest /// 要写入的生成输出。 private static async Task WriteMissingSnapshotAndFailAsync(string path, string generatedContent) { - Directory.CreateDirectory(Path.GetDirectoryName(path)!); + // ResolveSnapshotPath 保证快照不会越界,但根目录路径仍会让 GetDirectoryName 返回 null。 + var snapshotDirectory = Path.GetDirectoryName(path) + ?? throw new InvalidOperationException( + $"Snapshot path '{path}' must include a parent directory."); + Directory.CreateDirectory(snapshotDirectory); await File.WriteAllTextAsync(path, generatedContent).ConfigureAwait(false); Assert.Fail($"未找到快照文件,已在以下路径生成新快照:\n{path}"); } diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index e4e37da8..496f4465 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -7,8 +7,8 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-033` -- 当前阶段:`Phase 33` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-034` +- 当前阶段:`Phase 34` - 当前焦点: - 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次 - 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock @@ -66,10 +66,23 @@ 生成文件名、快照目录和断言语义不变 - 当前 `GFramework.SourceGenerators.Tests` Release build 基线已从 `40` 条降到 `39` 条; `SchemaConfigGeneratorSnapshotTests.cs` 已不再出现在 `MA0051` 列表中 + - 已完成当前 PR #273 review follow-up 首轮核对:确认本地仍成立的问题集中在 + `SchemaConfigGenerator` helper XML 文档、`GeneratorSnapshotTest` 的 `StringComparison.Ordinal` / + snapshot 路径空值防御、`AutoRegisterModuleGeneratorTests` 的 XML 文档位置,以及 + `SchemaConfigGeneratorSnapshotTests` 的 monster 快照覆盖缺口 + - 已将 monster 快照场景扩展到 `dependentRequired`、`dependentSchemas`、`allOf` 与 object-focused + `if/then/else`,以便把新增 schema 约束文档纳入 snapshot 验证 + - 已完成本轮定向验证: + `GFramework.Game.SourceGenerators` Release build 通过; + `GFramework.SourceGenerators.Tests` 在 `-m:1 --no-restore` 下 Release build 通过; + `SchemaConfigGeneratorSnapshotTests` 与 `AutoRegisterModuleGeneratorTests` 定向测试共 `4` 项全部通过 + - 当前验证仍受环境/基线约束: + `GFramework.SourceGenerators.Tests` Release build 保留既有 `MA0051` warning 基线; + NuGet vulnerability audit 在离线环境下产生 `NU1900` - `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑 - 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进 - - 下一轮默认继续拆分 `GFramework.SourceGenerators.Tests` 的 `MA0051` 热点,优先处理 - `GeneratorSnapshotTest` 或 `ContextRegistrationAnalyzerTests` + - 下一轮默认重新抓取 PR #273 最新 review 线程,并确认本轮 snapshot 更新后是否还存在剩余 open thread 或 + `dotnet-format` 细项 - 单次 `boot` 的工作树改动上限控制在约 `100` 个文件以内,避免 recovery context 与 review 面同时失控 - 若任务边界互不冲突,允许使用不同模型的 subagent 并行处理不同 warning 类型或不同目录,但必须遵守显式 ownership diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index 8deaf556..620d2928 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -964,3 +964,20 @@ 1. 若继续 analyzer warning reduction,优先回到 `GFramework.Core` 剩余 `MA0051` 热点,并继续保持“单 warning family、单切入点”的节奏 2. 后续所有 WSL 下的 .NET 定向验证命令继续显式附带 `-p:RestoreFallbackFolders=`,避免把环境问题误判成代码回归 +# 2026-04-23 + +- RP-034 / PR #273 review follow-up: + - 使用 `gframework-pr-review` 抓取当前分支 PR #273 的 latest-head review threads、MegaLinter 和测试摘要。 + - 本地复核后确认仍成立的项集中在 `SchemaConfigGenerator` helper XML 文档、 + `GeneratorSnapshotTest` 的 `StringComparison.Ordinal` 与 snapshot 路径空值防御、 + `AutoRegisterModuleGeneratorTests` 的 XML 文档位置,以及 + `SchemaConfigGeneratorSnapshotTests` 的 monster snapshot 覆盖缺口。 + - 已扩展 monster schema 场景以覆盖 `dependentRequired`、`dependentSchemas`、`allOf` 与 object-focused + `if/then/else`,并同步更新 `MonsterConfig.g.txt` 的约束快照。 + - `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release -p:RestoreFallbackFolders=` + 通过;离线 NuGet vulnerability audit 产生 `NU1900`。 + - `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1` + 通过;测试项目保留既有 `MA0051` warning 基线。 + - `DOTNET_CLI_HOME=/tmp/dotnet-home dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~SchemaConfigGeneratorSnapshotTests|FullyQualifiedName~AutoRegisterModuleGeneratorTests" -m:1` + 通过,`4` 个用例全部通过;需要在沙箱外执行以绕过 `vstest` 本地 socket 权限限制。 + - 下一步:提交本轮修复并在需要时重新抓取 PR review,确认 open threads 是否随新提交收敛。