mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-08 17:44:29 +08:00
fix(game-source-generators): 清理 SchemaConfigGenerator 字符串比较
- 修复 SchemaConfigGenerator 中 schema 关键字比较缺少 StringComparison 的 analyzer warning - 新增 schema 类型比较 helper 以统一 ordinal 比较语义 - 更新 analyzer warning reduction 的 RP-019 恢复记录与验证结果
This commit is contained in:
parent
de782ae179
commit
78a23bf53a
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 schema 关键字的大小写敏感语义比较类型名称。
|
||||
/// </summary>
|
||||
/// <param name="schemaType">从 JSON schema 读取或推导出的类型名称。</param>
|
||||
/// <param name="expectedType">期望匹配的 schema 类型关键字。</param>
|
||||
/// <returns>当两个类型名称按 schema 关键字语义完全一致时返回 <see langword="true" />。</returns>
|
||||
private static bool IsSchemaType(string schemaType, string expectedType)
|
||||
{
|
||||
return string.Equals(schemaType, expectedType, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断类型是否支持 JSON schema 数值范围和倍数约束。
|
||||
/// </summary>
|
||||
/// <param name="schemaType">从 JSON schema 读取或推导出的类型名称。</param>
|
||||
/// <returns>当类型为 <c>integer</c> 或 <c>number</c> 时返回 <see langword="true" />。</returns>
|
||||
private static bool IsNumericSchemaType(string schemaType)
|
||||
{
|
||||
return IsSchemaType(schemaType, "integer") || IsSchemaType(schemaType, "number");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析数组属性,支持标量数组与对象数组。
|
||||
/// </summary>
|
||||
@ -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<string>();
|
||||
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
|
||||
/// <returns>组合后的路径。</returns>
|
||||
private static string CombinePath(string parentPath, string propertyName)
|
||||
{
|
||||
return parentPath == "<root>" ? propertyName : $"{parentPath}.{propertyName}";
|
||||
return string.Equals(parentPath, "<root>", StringComparison.Ordinal)
|
||||
? propertyName
|
||||
: $"{parentPath}.{propertyName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -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<T>()` 的重复注册路径已改为状态冲突异常,避免把泛型类型参数伪装成方法参数名
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user