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 是否随新提交收敛。