diff --git a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs
index 0ca5ea00..ece23f07 100644
--- a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs
+++ b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs
@@ -203,8 +203,8 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
Path.GetFileName(file.Path)));
}
- if (idProperty.TypeSpec.SchemaType != "integer" &&
- idProperty.TypeSpec.SchemaType != "string")
+ if (!IsSchemaType(idProperty.TypeSpec.SchemaType, "integer") &&
+ !IsSchemaType(idProperty.TypeSpec.SchemaType, "string"))
{
return SchemaParseResult.FromDiagnostic(
Diagnostic.Create(
@@ -1854,6 +1854,27 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
};
}
+ ///
+ /// 使用 schema 关键字的大小写敏感语义比较类型名称。
+ ///
+ /// 从 JSON schema 读取或推导出的类型名称。
+ /// 期望匹配的 schema 类型关键字。
+ /// 当两个类型名称按 schema 关键字语义完全一致时返回 。
+ private static bool IsSchemaType(string schemaType, string expectedType)
+ {
+ return string.Equals(schemaType, expectedType, StringComparison.Ordinal);
+ }
+
+ ///
+ /// 判断类型是否支持 JSON schema 数值范围和倍数约束。
+ ///
+ /// 从 JSON schema 读取或推导出的类型名称。
+ /// 当类型为 integer 或 number 时返回 。
+ private static bool IsNumericSchemaType(string schemaType)
+ {
+ return IsSchemaType(schemaType, "integer") || IsSchemaType(schemaType, "number");
+ }
+
///
/// 解析数组属性,支持标量数组与对象数组。
///
@@ -3943,57 +3964,57 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
parts.Add($"const = {constDocumentation}");
}
- if ((schemaType == "integer" || schemaType == "number") &&
+ if (IsNumericSchemaType(schemaType) &&
TryGetFiniteNumber(element, "minimum", out var minimum))
{
parts.Add($"minimum = {minimum.ToString(CultureInfo.InvariantCulture)}");
}
- if ((schemaType == "integer" || schemaType == "number") &&
+ if (IsNumericSchemaType(schemaType) &&
TryGetFiniteNumber(element, "exclusiveMinimum", out var exclusiveMinimum))
{
parts.Add($"exclusiveMinimum = {exclusiveMinimum.ToString(CultureInfo.InvariantCulture)}");
}
- if ((schemaType == "integer" || schemaType == "number") &&
+ if (IsNumericSchemaType(schemaType) &&
TryGetFiniteNumber(element, "maximum", out var maximum))
{
parts.Add($"maximum = {maximum.ToString(CultureInfo.InvariantCulture)}");
}
- if ((schemaType == "integer" || schemaType == "number") &&
+ if (IsNumericSchemaType(schemaType) &&
TryGetFiniteNumber(element, "exclusiveMaximum", out var exclusiveMaximum))
{
parts.Add($"exclusiveMaximum = {exclusiveMaximum.ToString(CultureInfo.InvariantCulture)}");
}
- if ((schemaType == "integer" || schemaType == "number") &&
+ if (IsNumericSchemaType(schemaType) &&
TryGetFiniteNumber(element, "multipleOf", out var multipleOf) &&
multipleOf > 0d)
{
parts.Add($"multipleOf = {multipleOf.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "string" &&
+ if (IsSchemaType(schemaType, "string") &&
TryGetNonNegativeInt32(element, "minLength", out var minLength))
{
parts.Add($"minLength = {minLength.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "string" &&
+ if (IsSchemaType(schemaType, "string") &&
TryGetNonNegativeInt32(element, "maxLength", out var maxLength))
{
parts.Add($"maxLength = {maxLength.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "string" &&
+ if (IsSchemaType(schemaType, "string") &&
element.TryGetProperty("pattern", out var patternElement) &&
patternElement.ValueKind == JsonValueKind.String)
{
parts.Add($"pattern = '{patternElement.GetString() ?? string.Empty}'");
}
- if (schemaType == "string" &&
+ if (IsSchemaType(schemaType, "string") &&
element.TryGetProperty("format", out var formatElement) &&
formatElement.ValueKind == JsonValueKind.String)
{
@@ -4004,26 +4025,26 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
}
}
- if (schemaType == "array" &&
+ if (IsSchemaType(schemaType, "array") &&
TryGetNonNegativeInt32(element, "minItems", out var minItems))
{
parts.Add($"minItems = {minItems.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "array" &&
+ if (IsSchemaType(schemaType, "array") &&
TryGetNonNegativeInt32(element, "maxItems", out var maxItems))
{
parts.Add($"maxItems = {maxItems.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "array" &&
+ if (IsSchemaType(schemaType, "array") &&
element.TryGetProperty("uniqueItems", out var uniqueItemsElement) &&
uniqueItemsElement.ValueKind == JsonValueKind.True)
{
parts.Add("uniqueItems = true");
}
- if (schemaType == "array")
+ if (IsSchemaType(schemaType, "array"))
{
var containsDocumentation = TryBuildContainsDocumentation(element);
if (containsDocumentation is not null)
@@ -4038,31 +4059,31 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
parts.Add($"not = {notDocumentation}");
}
- if (schemaType == "array" &&
+ if (IsSchemaType(schemaType, "array") &&
TryGetNonNegativeInt32(element, "minContains", out var minContains))
{
parts.Add($"minContains = {minContains.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "array" &&
+ if (IsSchemaType(schemaType, "array") &&
TryGetNonNegativeInt32(element, "maxContains", out var maxContains))
{
parts.Add($"maxContains = {maxContains.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "object" &&
+ if (IsSchemaType(schemaType, "object") &&
TryGetNonNegativeInt32(element, "minProperties", out var minProperties))
{
parts.Add($"minProperties = {minProperties.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "object" &&
+ if (IsSchemaType(schemaType, "object") &&
TryGetNonNegativeInt32(element, "maxProperties", out var maxProperties))
{
parts.Add($"maxProperties = {maxProperties.ToString(CultureInfo.InvariantCulture)}");
}
- if (schemaType == "object")
+ if (IsSchemaType(schemaType, "object"))
{
var dependentRequiredDocumentation = TryBuildDependentRequiredDocumentation(element);
if (dependentRequiredDocumentation is not null)
@@ -4351,14 +4372,14 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
}
var schemaType = typeElement.GetString();
- if (string.IsNullOrWhiteSpace(schemaType))
+ if (schemaType is null || string.IsNullOrWhiteSpace(schemaType))
{
return null;
}
var details = new List();
if (includeRequiredProperties &&
- schemaType == "object")
+ IsSchemaType(schemaType, "object"))
{
var requiredDocumentation = TryBuildRequiredPropertiesDocumentation(schemaElement);
if (requiredDocumentation is not null)
@@ -4367,13 +4388,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
}
}
- var enumDocumentation = TryBuildEnumDocumentation(schemaElement, schemaType!);
+ var enumDocumentation = TryBuildEnumDocumentation(schemaElement, schemaType);
if (enumDocumentation is not null)
{
details.Add($"enum = {enumDocumentation}");
}
- var constraintDocumentation = TryBuildConstraintDocumentation(schemaElement, schemaType!);
+ var constraintDocumentation = TryBuildConstraintDocumentation(schemaElement, schemaType);
if (constraintDocumentation is not null)
{
details.Add(constraintDocumentation);
@@ -4492,7 +4513,9 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
/// 组合后的路径。
private static string CombinePath(string parentPath, string propertyName)
{
- return parentPath == "" ? propertyName : $"{parentPath}.{propertyName}";
+ return string.Equals(parentPath, "", StringComparison.Ordinal)
+ ? propertyName
+ : $"{parentPath}.{propertyName}";
}
///
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 b13e0e74..d27049dc 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,14 +7,16 @@
## 当前恢复点
-- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-018`
-- 当前阶段:`Phase 18`
+- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-019`
+- 当前阶段:`Phase 19`
- 当前焦点:
- 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次
- 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock
建议点,属于跨 target 兼容性风险,不在本轮直接批量替换
- 已完成 `GFramework.Core.SourceGenerators/Rule/ContextAwareGenerator.cs` 的剩余 `MA0051` 结构拆分,生成输出保持不变
- 已完成 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` 的 `MA0051` 结构拆分,生成输出保持不变
+ - 已完成 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0006` 低风险收口,schema 关键字比较显式使用
+ `StringComparison.Ordinal`
- `LoggingConfiguration`、`FilterConfiguration` 与 `CollectionExtensions` 已改用集合抽象接口,并保留内部具体集合默认值
- `CoroutineScheduler` 的 tag/group 字典已显式使用 `StringComparer.Ordinal`,保持既有区分大小写语义
- `EasyEvents.AddEvent()` 的重复注册路径已改为状态冲突异常,避免把泛型类型参数伪装成方法参数名
@@ -22,9 +24,11 @@
- 当前 `GFramework.Core` `net8.0` warnings-only 基线已降到 `0` 条
- 当前 `GFramework.Core.SourceGenerators` warnings-only 基线已降到 `0` 条
- 当前 `GFramework.Cqrs.SourceGenerators` warnings-only 基线已降到 `0` 条
+ - 当前 `GFramework.Game.SourceGenerators` warnings-only 基线已从 `46` 条降到 `19` 条,剩余均为
+ `SchemaConfigGenerator.cs` 的 `MA0051`
- `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑
- 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进
- - 下一轮默认转向 `GFramework.Game.SourceGenerators` 的 `MA0006` / `MA0051` 热点,或继续评估跨 target 的 `MA0158`
+ - 下一轮默认继续拆分 `GFramework.Game.SourceGenerators` 的 `MA0051` 热点,或评估跨 target 的 `MA0158`
锁替换风险
- 单次 `boot` 的工作树改动上限控制在约 `100` 个文件以内,避免 recovery context 与 review 面同时失控
- 若任务边界互不冲突,允许使用不同模型的 subagent 并行处理不同 warning 类型或不同目录,但必须遵守显式 ownership
@@ -45,6 +49,8 @@
- 已完成当前 `GFramework.Core` `net8.0` 剩余低风险 analyzer warning 批次;warnings-only 基线已降到 `0` 条
- 已完成 `GFramework.Core.SourceGenerators` 中 `ContextAwareGenerator` 的剩余 `MA0051` 收口;warnings-only 基线已降到 `0` 条
- 已完成 `GFramework.Cqrs.SourceGenerators` 中 `CqrsHandlerRegistryGenerator` 的剩余 `MA0051` 收口;warnings-only 基线已降到 `0` 条
+- 已完成 `GFramework.Game.SourceGenerators` 中 `SchemaConfigGenerator` 的 `MA0006` 收口;warnings-only 基线剩余 `19` 条
+ `MA0051`
## 当前活跃事实
@@ -81,6 +87,8 @@
并通过 source generator 项目 build 与 `ContextAwareGeneratorSnapshotTests` 验证生成输出未回归
- `RP-018` 暂缓跨 target `MA0158`,转入 `GFramework.Cqrs.SourceGenerators` 的单文件结构性 warning;
通过拆分 handler 分析、运行时类型引用构造、注册器源码发射与精确反射注册输出阶段,清空该项目当前 `MA0051`
+- `RP-019` 转入 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs`,先完成低风险 `MA0006` 批次;
+ 通过 schema 类型比较 helper 与显式 `StringComparison.Ordinal` 清空当前项目的 `MA0006`
- 当前工作树分支 `fix/analyzer-warning-reduction-batch` 已在 `ai-plan/public/README.md` 建立 topic 映射
## 当前风险
@@ -95,8 +103,7 @@
- 缓解措施:下一轮先按 target framework 与 API 可用性评估,不直接批量替换共享源码中的 `object` lock
- source generator warning 外溢风险:运行 `GFramework.SourceGenerators.Tests` 会构建相邻 generator/test 项目并显示既有
`GFramework.Game.SourceGenerators` 与测试项目 warning
- - 缓解措施:本轮以 `GFramework.Cqrs.SourceGenerators` 独立 warnings-only build 作为主验收,并用 focused generator test
- 验证行为;后续若处理相邻 generator warning,应另开明确切片
+ - 缓解措施:继续以被修改 generator 项目的独立 warnings-only build 作为主验收,并用 focused generator test 验证行为
- Godot 资产文件环境风险:当前 worktree 的 `GFramework.Godot` restore/build 仍会命中 Windows fallback package folder
- 缓解措施:后续若继续触达 Godot 模块,先用 Linux 侧 restore 资产或 Windows-hosted 构建链刷新该项目,再补跑定向 build
- 并行实现风险:批量收敛时若 subagent 写入边界不清晰,容易引入命名冲突或重复重构
@@ -198,13 +205,24 @@
- 结果:`14 Passed`,`0 Failed`
- 说明:该 test project 构建仍显示 `GFramework.Game.SourceGenerators` 与测试项目中的既有 analyzer warning;本轮关注的
`GFramework.Cqrs.SourceGenerators` 独立 build 已清零
+- `RP-019` 的验证结果:
+ - `dotnet restore GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -p:RestoreFallbackFolders= -nologo`
+ - 结果:通过;刷新 Linux 侧资产以清除 stale Windows fallback package folder
+ - `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release -t:Rebuild --no-restore -p:UseSharedCompilation=false -p:RestoreFallbackFolders= -nologo -clp:"Summary;WarningsOnly"`
+ - 结果:`19 Warning(s)`,`0 Error(s)`;当前项目输出已不再出现 `MA0006`,剩余均为 `SchemaConfigGenerator.cs` 的
+ `MA0051`
+ - `dotnet restore GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -p:RestoreFallbackFolders= -nologo`
+ - 结果:通过;刷新 test project 资产以清除 stale Windows fallback package folder
+ - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~SchemaConfigGenerator -m:1 -p:RestoreFallbackFolders= -nologo`
+ - 结果:`50 Passed`,`0 Failed`
+ - 说明:测试项目构建仍显示既有 source generator test analyzer warning;不属于本轮写集
- active 跟踪文件只保留当前恢复点、活跃事实、风险与下一步,不再重复保存已完成阶段的长篇历史
## 下一步
1. 若要继续该主题,先读 active tracking,再按需展开历史归档中的 warning 热点与验证记录
-2. 下一轮优先转入 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0006` 低风险批次,再评估是否继续拆分
- 该文件的 `MA0051`
+2. 下一轮优先继续拆分 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0051`;建议先从
+ `TryBuildConstraintDocumentation` 或 `GenerateConfigCatalogSource` 这类高收益方法切入
3. 若改回推进 `MA0158`,先设计 `net8.0` / `net9.0` / `net10.0` 多 target 条件编译方案,不直接批量替换共享源码中的
`object` lock
4. 若后续继续改动 `GFramework.Godot`,先修复该项目的 Linux 侧 restore 资产,再补跑独立 build
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 b567a7e5..58b721a6 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
@@ -1,5 +1,35 @@
# Analyzer Warning Reduction 追踪
+## 2026-04-22 — RP-019
+
+### 阶段:`SchemaConfigGenerator` 当前 `MA0006` 收口(RP-019)
+
+- 启动复核:
+ - 当前 worktree 仍映射到 `analyzer-warning-reduction` active topic
+ - Windows Git interop 在当前 shell 中返回 WSL socket 错误;本轮使用显式 `--git-dir` / `--work-tree` 读取状态
+ - `GFramework.Game.SourceGenerators` 首次 build 受 stale Windows fallback package folder 影响,刷新 restore 资产后复现
+ `46` 条 warning,其中 `MA0006=27`,其余为 `SchemaConfigGenerator.cs` 的 `MA0051`
+- 决策:
+ - 本轮先收口低风险 `MA0006`,不在同一 slice 中拆分 `SchemaConfigGenerator.cs` 的长方法
+ - 未使用 subagent;critical path 是本地复现 warning、替换 schema 字符串比较并用 focused schema generator tests 验证输出行为
+- 实施调整:
+ - 为 schema 类型关键字新增 `IsSchemaType` / `IsNumericSchemaType` helper,统一使用 `StringComparison.Ordinal`
+ - 将 id key 类型验证、约束文档生成、required property 文档和路径拼接中的直接字符串比较改为显式 ordinal 比较
+ - 修正 `JsonElement.GetString()` 后的 nullable flow,避免新增 `CS8604`
+- 验证结果:
+ - `dotnet restore GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -p:RestoreFallbackFolders= -nologo`
+ - 结果:通过
+ - `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release -t:Rebuild --no-restore -p:UseSharedCompilation=false -p:RestoreFallbackFolders= -nologo -clp:"Summary;WarningsOnly"`
+ - 结果:`19 Warning(s)`,`0 Error(s)`;当前项目输出已无 `MA0006`,剩余均为 `MA0051`
+ - `dotnet restore GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -p:RestoreFallbackFolders= -nologo`
+ - 结果:通过
+ - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~SchemaConfigGenerator -m:1 -p:RestoreFallbackFolders= -nologo`
+ - 结果:`50 Passed`,`0 Failed`
+ - 说明:测试项目构建仍显示既有 analyzer warning;不属于本轮写集
+- 下一步建议:
+ - 继续该主题时,优先拆分 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0051`
+ - 若回到 `MA0158`,先设计多 target 条件编译方案,再考虑替换共享源码中的 `object` lock
+
## 2026-04-22 — RP-018
### 阶段:`CqrsHandlerRegistryGenerator` 剩余 `MA0051` 收口(RP-018)