From dc8c5766dc5586948d1d735d7cba1077625f25fa Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 23 Apr 2026 10:37:34 +0800 Subject: [PATCH] =?UTF-8?q?refactor(analyzer-warning):=20=E6=94=B6?= =?UTF-8?q?=E5=8F=A3=20SchemaConfigGenerator=20=E5=89=A9=E4=BD=99=E5=91=8A?= =?UTF-8?q?=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 拆分 SchemaConfigGenerator 的 schema 校验与代码发射 helper,清零 GFramework.Game.SourceGenerators 的剩余 MA0051 - 更新 analyzer-warning-reduction 的 tracking 与 trace,记录 RP-029 的验证结果与后续恢复点 --- .../Config/SchemaConfigGenerator.cs | 1004 ++++++++++++----- .../analyzer-warning-reduction-tracking.md | 30 +- .../analyzer-warning-reduction-trace.md | 40 + 3 files changed, 757 insertions(+), 317 deletions(-) diff --git a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs index 60664a38..daabab58 100644 --- a/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs +++ b/GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs @@ -1242,90 +1242,128 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return false; } - if (!element.TryGetProperty("properties", out var propertiesElement) || - propertiesElement.ValueKind != JsonValueKind.Object) + if (!TryGetDeclaredProperties( + filePath, + displayPath, + element, + ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, + "dependentRequired", + out var declaredProperties, + out diagnostic)) + { + return false; + } + + foreach (var dependency in dependentRequiredElement.EnumerateObject()) + { + if (!TryValidateDependentRequiredEntry( + filePath, + displayPath, + dependency, + declaredProperties, + out diagnostic)) + { + return false; + } + } + + return true; + } + + private static bool TryValidateDependentRequiredEntry( + string filePath, + string displayPath, + JsonProperty dependency, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (!declaredProperties.Contains(dependency.Name)) { diagnostic = Diagnostic.Create( ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, CreateFileLocation(filePath), Path.GetFileName(filePath), displayPath, - "Object schemas using 'dependentRequired' must also declare an object-valued 'properties' map."); + $"Trigger property '{dependency.Name}' is not declared in the same object schema."); return false; } - var declaredProperties = new HashSet( - propertiesElement - .EnumerateObject() - .Select(static property => property.Name), - StringComparer.Ordinal); - - foreach (var dependency in dependentRequiredElement.EnumerateObject()) + if (dependency.Value.ValueKind != JsonValueKind.Array) { - if (!declaredProperties.Contains(dependency.Name)) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Property '{dependency.Name}' must declare 'dependentRequired' as an array of sibling property names."); + return false; + } + + foreach (var dependencyTarget in dependency.Value.EnumerateArray()) + { + if (!TryValidateDependentRequiredTarget( + filePath, displayPath, - $"Trigger property '{dependency.Name}' is not declared in the same object schema."); - return false; - } - - if (dependency.Value.ValueKind != JsonValueKind.Array) + dependency.Name, + dependencyTarget, + declaredProperties, + out diagnostic)) { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Property '{dependency.Name}' must declare 'dependentRequired' as an array of sibling property names."); return false; } - - foreach (var dependencyTarget in dependency.Value.EnumerateArray()) - { - if (dependencyTarget.ValueKind != JsonValueKind.String) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Property '{dependency.Name}' must declare 'dependentRequired' entries as strings."); - return false; - } - - var dependencyTargetName = dependencyTarget.GetString(); - if (string.IsNullOrWhiteSpace(dependencyTargetName)) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Property '{dependency.Name}' cannot declare blank 'dependentRequired' entries."); - return false; - } - - var normalizedDependencyTargetName = dependencyTargetName!; - if (!declaredProperties.Contains(normalizedDependencyTargetName)) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Dependent target '{normalizedDependencyTargetName}' is not declared in the same object schema."); - return false; - } - } } return true; } + private static bool TryValidateDependentRequiredTarget( + string filePath, + string displayPath, + string dependencyName, + JsonElement dependencyTarget, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (dependencyTarget.ValueKind != JsonValueKind.String) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Property '{dependencyName}' must declare 'dependentRequired' entries as strings."); + return false; + } + + var dependencyTargetName = dependencyTarget.GetString(); + if (string.IsNullOrWhiteSpace(dependencyTargetName)) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Property '{dependencyName}' cannot declare blank 'dependentRequired' entries."); + return false; + } + + var normalizedDependencyTargetName = dependencyTargetName!; + if (declaredProperties.Contains(normalizedDependencyTargetName)) + { + return true; + } + + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentRequiredMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Dependent target '{normalizedDependencyTargetName}' is not declared in the same object schema."); + return false; + } + /// /// 验证当前 schema 节点是否以运行时支持的方式声明了 dependentSchemas。 /// 只有 object 节点允许挂载该关键字;一旦关键字出现,就继续复用对象节点的形状校验, @@ -1431,60 +1469,75 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return false; } - if (!element.TryGetProperty("properties", out var propertiesElement) || - propertiesElement.ValueKind != JsonValueKind.Object) + if (!TryGetDeclaredProperties( + filePath, + displayPath, + element, + ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, + "dependentSchemas", + out var declaredProperties, + out diagnostic)) + { + return false; + } + + foreach (var dependency in dependentSchemasElement.EnumerateObject()) + { + if (!TryValidateDependentSchemaEntry( + filePath, + displayPath, + dependency, + declaredProperties, + out diagnostic)) + { + return false; + } + } + + return true; + } + + private static bool TryValidateDependentSchemaEntry( + string filePath, + string displayPath, + JsonProperty dependency, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (!declaredProperties.Contains(dependency.Name)) { diagnostic = Diagnostic.Create( ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, CreateFileLocation(filePath), Path.GetFileName(filePath), displayPath, - "Object schemas using 'dependentSchemas' must also declare an object-valued 'properties' map."); + $"Trigger property '{dependency.Name}' is not declared in the same object schema."); return false; } - var declaredProperties = new HashSet( - propertiesElement - .EnumerateObject() - .Select(static property => property.Name), - StringComparer.Ordinal); - - foreach (var dependency in dependentSchemasElement.EnumerateObject()) + if (dependency.Value.ValueKind != JsonValueKind.Object) { - if (!declaredProperties.Contains(dependency.Name)) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Trigger property '{dependency.Name}' is not declared in the same object schema."); - return false; - } + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Property '{dependency.Name}' must declare 'dependentSchemas' as an object-valued schema."); + return false; + } - if (dependency.Value.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Property '{dependency.Name}' must declare 'dependentSchemas' as an object-valued schema."); - return false; - } - - if (!dependency.Value.TryGetProperty("type", out var dependentSchemaTypeElement) || - dependentSchemaTypeElement.ValueKind != JsonValueKind.String || - !string.Equals(dependentSchemaTypeElement.GetString(), "object", StringComparison.Ordinal)) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Property '{dependency.Name}' must declare an object-typed 'dependentSchemas' schema."); - return false; - } + if (!dependency.Value.TryGetProperty("type", out var dependentSchemaTypeElement) || + dependentSchemaTypeElement.ValueKind != JsonValueKind.String || + !string.Equals(dependentSchemaTypeElement.GetString(), "object", StringComparison.Ordinal)) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidDependentSchemasMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Property '{dependency.Name}' must declare an object-typed 'dependentSchemas' schema."); + return false; } return true; @@ -1595,48 +1648,28 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return false; } - if (!element.TryGetProperty("properties", out var propertiesElement) || - propertiesElement.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidAllOfMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), + if (!TryGetDeclaredProperties( + filePath, displayPath, - "Object schemas using 'allOf' must also declare an object-valued 'properties' map."); + element, + ConfigSchemaDiagnostics.InvalidAllOfMetadata, + "allOf", + out var declaredProperties, + out diagnostic)) + { return false; } - var declaredProperties = new HashSet( - propertiesElement - .EnumerateObject() - .Select(static property => property.Name), - StringComparer.Ordinal); - var allOfIndex = 0; foreach (var allOfSchema in allOfElement.EnumerateArray()) { - if (allOfSchema.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidAllOfMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), + if (!TryValidateAllOfEntryShape( + filePath, displayPath, - $"Entry #{allOfIndex + 1} in 'allOf' must be an object-valued schema."); - return false; - } - - if (!allOfSchema.TryGetProperty("type", out var allOfTypeElement) || - allOfTypeElement.ValueKind != JsonValueKind.String || - !string.Equals(allOfTypeElement.GetString(), "object", StringComparison.Ordinal)) + allOfSchema, + allOfIndex, + out diagnostic)) { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidAllOfMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"Entry #{allOfIndex + 1} in 'allOf' must declare an object-typed schema."); return false; } @@ -1657,6 +1690,41 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator return true; } + private static bool TryValidateAllOfEntryShape( + string filePath, + string displayPath, + JsonElement allOfSchema, + int allOfIndex, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (allOfSchema.ValueKind != JsonValueKind.Object) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidAllOfMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Entry #{allOfIndex + 1} in 'allOf' must be an object-valued schema."); + return false; + } + + if (!allOfSchema.TryGetProperty("type", out var allOfTypeElement) || + allOfTypeElement.ValueKind != JsonValueKind.String || + !string.Equals(allOfTypeElement.GetString(), "object", StringComparison.Ordinal)) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidAllOfMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Entry #{allOfIndex + 1} in 'allOf' must declare an object-typed schema."); + return false; + } + + return true; + } + /// /// 验证当前 schema 节点是否以运行时支持的方式声明了 object-focused if / then / else。 /// @@ -1746,51 +1814,57 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator var hasIf = element.TryGetProperty("if", out var ifElement); var hasThen = element.TryGetProperty("then", out var thenElement); var hasElse = element.TryGetProperty("else", out var elseElement); - if (!hasIf && !hasThen && !hasElse) + if (!TryValidateConditionalSchemaPresence( + filePath, + displayPath, + hasIf, + hasThen, + hasElse, + out diagnostic)) { - return true; + return false; } if (!hasIf) { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - "Object schemas using 'then' or 'else' must also declare 'if'."); - return false; + return true; } - if (!hasThen && !hasElse) + if (!TryGetDeclaredProperties( + filePath, + displayPath, + element, + ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, + "if/then/else", + out var declaredProperties, + out diagnostic)) { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - "Object schemas using 'if' must also declare at least one of 'then' or 'else'."); return false; } - if (!element.TryGetProperty("properties", out var propertiesElement) || - propertiesElement.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - "Object schemas using 'if/then/else' must also declare an object-valued 'properties' map."); - return false; - } - - var declaredProperties = new HashSet( - propertiesElement - .EnumerateObject() - .Select(static property => property.Name), - StringComparer.Ordinal); + return TryValidateConditionalSchemaBranches( + filePath, + displayPath, + ifElement, + hasThen, + thenElement, + hasElse, + elseElement, + declaredProperties, + out diagnostic); + } + private static bool TryValidateConditionalSchemaBranches( + string filePath, + string displayPath, + JsonElement ifElement, + bool hasThen, + JsonElement thenElement, + bool hasElse, + JsonElement elseElement, + ISet declaredProperties, + out Diagnostic? diagnostic) + { if (!TryValidateConditionalSchemaBranch( filePath, displayPath, @@ -1824,6 +1898,76 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out diagnostic); } + private static bool TryValidateConditionalSchemaPresence( + string filePath, + string displayPath, + bool hasIf, + bool hasThen, + bool hasElse, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (!hasIf && !hasThen && !hasElse) + { + return true; + } + + if (!hasIf) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + "Object schemas using 'then' or 'else' must also declare 'if'."); + return false; + } + + if (hasThen || hasElse) + { + return true; + } + + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + "Object schemas using 'if' must also declare at least one of 'then' or 'else'."); + return false; + } + + private static bool TryGetDeclaredProperties( + string filePath, + string displayPath, + JsonElement element, + DiagnosticDescriptor descriptor, + string keywordName, + out HashSet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + declaredProperties = new HashSet(StringComparer.Ordinal); + if (!element.TryGetProperty("properties", out var propertiesElement) || + propertiesElement.ValueKind != JsonValueKind.Object) + { + diagnostic = Diagnostic.Create( + descriptor, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"Object schemas using '{keywordName}' must also declare an object-valued 'properties' map."); + return false; + } + + declaredProperties = new HashSet( + propertiesElement + .EnumerateObject() + .Select(static property => property.Name), + StringComparer.Ordinal); + return true; + } + /// /// 验证单个 object-focused 条件分支的类型与父对象字段引用范围。 /// @@ -1896,36 +2040,79 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator out Diagnostic? diagnostic) { diagnostic = null; - if (schemaElement.TryGetProperty("properties", out var propertiesElement)) + if (!TryValidateObjectFocusedSchemaProperties( + filePath, + displayPath, + entryLabel, + schemaElement, + declaredProperties, + out diagnostic)) { - if (propertiesElement.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"The '{entryLabel}' schema must declare 'properties' as an object-valued map."); - return false; - } - - foreach (var property in propertiesElement.EnumerateObject()) - { - if (declaredProperties.Contains(property.Name)) - { - continue; - } - - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - displayPath, - $"The '{entryLabel}' schema declares property '{property.Name}', but that property is not declared in the parent object schema."); - return false; - } + return false; } + return TryValidateObjectFocusedSchemaRequiredProperties( + filePath, + displayPath, + entryLabel, + schemaElement, + declaredProperties, + out diagnostic); + } + + private static bool TryValidateObjectFocusedSchemaProperties( + string filePath, + string displayPath, + string entryLabel, + JsonElement schemaElement, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (!schemaElement.TryGetProperty("properties", out var propertiesElement)) + { + return true; + } + + if (propertiesElement.ValueKind != JsonValueKind.Object) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"The '{entryLabel}' schema must declare 'properties' as an object-valued map."); + return false; + } + + foreach (var property in propertiesElement.EnumerateObject()) + { + if (declaredProperties.Contains(property.Name)) + { + continue; + } + + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidConditionalSchemaMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + displayPath, + $"The '{entryLabel}' schema declares property '{property.Name}', but that property is not declared in the parent object schema."); + return false; + } + + return true; + } + + private static bool TryValidateObjectFocusedSchemaRequiredProperties( + string filePath, + string displayPath, + string entryLabel, + JsonElement schemaElement, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; if (!schemaElement.TryGetProperty("required", out var requiredElement)) { return true; @@ -2004,37 +2191,79 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator { diagnostic = null; var allOfEntryPath = BuildAllOfEntryPath(displayPath, allOfIndex); - - if (allOfSchema.TryGetProperty("properties", out var allOfPropertiesElement)) + if (!TryValidateAllOfEntryProperties( + filePath, + allOfEntryPath, + allOfSchema, + allOfIndex, + declaredProperties, + out diagnostic)) { - if (allOfPropertiesElement.ValueKind != JsonValueKind.Object) - { - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidAllOfMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - allOfEntryPath, - $"Entry #{allOfIndex + 1} in 'allOf' must declare 'properties' as an object-valued map."); - return false; - } - - foreach (var property in allOfPropertiesElement.EnumerateObject()) - { - if (declaredProperties.Contains(property.Name)) - { - continue; - } - - diagnostic = Diagnostic.Create( - ConfigSchemaDiagnostics.InvalidAllOfMetadata, - CreateFileLocation(filePath), - Path.GetFileName(filePath), - allOfEntryPath, - $"Entry #{allOfIndex + 1} in 'allOf' declares property '{property.Name}', but that property is not declared in the parent object schema."); - return false; - } + return false; } + return TryValidateAllOfEntryRequiredProperties( + filePath, + allOfEntryPath, + allOfSchema, + allOfIndex, + declaredProperties, + out diagnostic); + } + + private static bool TryValidateAllOfEntryProperties( + string filePath, + string allOfEntryPath, + JsonElement allOfSchema, + int allOfIndex, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; + if (!allOfSchema.TryGetProperty("properties", out var allOfPropertiesElement)) + { + return true; + } + + if (allOfPropertiesElement.ValueKind != JsonValueKind.Object) + { + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidAllOfMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + allOfEntryPath, + $"Entry #{allOfIndex + 1} in 'allOf' must declare 'properties' as an object-valued map."); + return false; + } + + foreach (var property in allOfPropertiesElement.EnumerateObject()) + { + if (declaredProperties.Contains(property.Name)) + { + continue; + } + + diagnostic = Diagnostic.Create( + ConfigSchemaDiagnostics.InvalidAllOfMetadata, + CreateFileLocation(filePath), + Path.GetFileName(filePath), + allOfEntryPath, + $"Entry #{allOfIndex + 1} in 'allOf' declares property '{property.Name}', but that property is not declared in the parent object schema."); + return false; + } + + return true; + } + + private static bool TryValidateAllOfEntryRequiredProperties( + string filePath, + string allOfEntryPath, + JsonElement allOfSchema, + int allOfIndex, + ISet declaredProperties, + out Diagnostic? diagnostic) + { + diagnostic = null; if (!allOfSchema.TryGetProperty("required", out var requiredElement)) { return true; @@ -2379,11 +2608,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator private static string GenerateConfigClass(SchemaFileSpec schema) { var builder = new StringBuilder(); - builder.AppendLine("// "); - builder.AppendLine("#nullable enable"); - builder.AppendLine(); - builder.AppendLine($"namespace {schema.Namespace};"); - builder.AppendLine(); + AppendGeneratedSourceHeader(builder, schema.Namespace); AppendObjectType(builder, schema.RootObject, schema.FileName, schema.Title, schema.Description, isRoot: true, indentationLevel: 0); @@ -2402,11 +2627,98 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator var indexedQueryableProperties = queryableProperties .Where(static property => property.IsIndexedLookup) .ToArray(); + AppendGeneratedSourceHeader(builder, schema.Namespace); + AppendGeneratedTableTypeHeader(builder, schema, indexedQueryableProperties); + AppendGeneratedTableConstructor(builder, schema, indexedQueryableProperties); + AppendGeneratedTableCoreMembers(builder, schema); + AppendGeneratedTableLookupMembers(builder, schema, queryableProperties, indexedQueryableProperties); + builder.AppendLine("}"); + return builder.ToString().TrimEnd(); + } + + /// + /// 生成运行时注册与访问辅助源码。 + /// 该辅助类型把 schema 命名约定、配置目录和 schema 相对路径固化为生成代码, + /// 让消费端无需重复手写字符串常量和主键提取逻辑。 + /// + /// 已解析的 schema 模型。 + /// 辅助类型源码。 + private static string GenerateBindingsClass(SchemaFileSpec schema) + { + var registerMethodName = $"Register{schema.EntityName}Table"; + var getMethodName = $"Get{schema.EntityName}Table"; + var tryGetMethodName = $"TryGet{schema.EntityName}Table"; + var bindingsClassName = $"{schema.EntityName}ConfigBindings"; + var referenceSpecs = CollectReferenceSpecs(schema.RootObject).ToArray(); + + var builder = new StringBuilder(); + AppendGeneratedSourceHeader(builder, schema.Namespace); + AppendBindingsTypeHeader(builder, bindingsClassName, schema.FileName); + AppendBindingsReferenceMetadataType(builder); + AppendBindingsMetadataType(builder, schema); + AppendBindingsMetadataAliases(builder); + AppendYamlSerializationHelpers(builder, schema); + AppendBindingsReferencesType(builder, referenceSpecs); + AppendBindingsRegisterMethod(builder, schema, registerMethodName); + AppendBindingsGetMethod(builder, schema, getMethodName); + AppendBindingsTryGetMethod(builder, schema, tryGetMethodName); + builder.AppendLine("}"); + return builder.ToString().TrimEnd(); + } + + /// + /// 生成项目级聚合辅助源码。 + /// 该辅助把当前消费者项目内所有有效 schema 汇总为一个统一入口, + /// 以便运行时快速完成批量注册并在需要时枚举已生成的配置域元数据。 + /// + /// 当前编译中成功解析的 schema 集合。 + /// 聚合辅助源码。 + private static string GenerateCatalogClass(IReadOnlyList schemas) + { + var builder = new StringBuilder(); + AppendGeneratedSourceHeader(builder, GeneratedNamespace); + AppendGeneratedConfigCatalogType(builder, schemas); + builder.AppendLine(); + AppendGeneratedConfigRegistrationOptionsType(builder, schemas); + builder.AppendLine(); + AppendGeneratedConfigRegistrationExtensionsType(builder, schemas); + + return builder.ToString().TrimEnd(); + } + + /// + /// Emits the generated catalog type that exposes schema metadata and filtering helpers. + /// + /// Output buffer. + /// Successfully parsed schemas for the current compilation. + private static void AppendGeneratedConfigCatalogType(StringBuilder builder, IReadOnlyList schemas) + { + AppendGeneratedConfigCatalogHeader(builder); + AppendGeneratedConfigTableMetadataType(builder); + AppendGeneratedConfigTablesProperty(builder, schemas); + AppendGeneratedConfigResolveAbsolutePathMethod(builder); + AppendGeneratedConfigTryGetByTableNameMethod(builder, schemas); + AppendGeneratedConfigGetTablesInConfigDomainMethod(builder); + AppendGeneratedConfigGetTablesForRegistrationMethod(builder); + AppendGeneratedConfigMatchesRegistrationOptionsMethod(builder); + AppendGeneratedConfigAllowListMethod(builder); + builder.AppendLine("}"); + } + + private static void AppendGeneratedSourceHeader(StringBuilder builder, string namespaceName) + { builder.AppendLine("// "); builder.AppendLine("#nullable enable"); builder.AppendLine(); - builder.AppendLine($"namespace {schema.Namespace};"); + builder.AppendLine($"namespace {namespaceName};"); builder.AppendLine(); + } + + private static void AppendGeneratedTableTypeHeader( + StringBuilder builder, + SchemaFileSpec schema, + IReadOnlyList indexedQueryableProperties) + { builder.AppendLine("/// "); builder.AppendLine( $"/// Auto-generated table wrapper for schema file '{schema.FileName}'."); @@ -2423,7 +2735,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( $" private readonly global::System.Lazy>> _{ToCamelCase(property.PropertyName)}Index;"); } + } + private static void AppendGeneratedTableConstructor( + StringBuilder builder, + SchemaFileSpec schema, + IReadOnlyList indexedQueryableProperties) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Creates a generated table wrapper around the runtime config table instance."); @@ -2442,6 +2760,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator } builder.AppendLine(" }"); + } + + private static void AppendGeneratedTableCoreMembers(StringBuilder builder, SchemaFileSpec schema) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" public global::System.Type KeyType => _inner.KeyType;"); @@ -2476,8 +2798,15 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" {"); builder.AppendLine(" return _inner.All();"); builder.AppendLine(" }"); + } - if (indexedQueryableProperties.Length > 0) + private static void AppendGeneratedTableLookupMembers( + StringBuilder builder, + SchemaFileSpec schema, + IReadOnlyList queryableProperties, + IReadOnlyList indexedQueryableProperties) + { + if (indexedQueryableProperties.Count > 0) { foreach (var property in indexedQueryableProperties) { @@ -2496,40 +2825,22 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(); AppendTryFindFirstByPropertyMethod(builder, schema, property); } - - builder.AppendLine("}"); - return builder.ToString().TrimEnd(); } - /// - /// 生成运行时注册与访问辅助源码。 - /// 该辅助类型把 schema 命名约定、配置目录和 schema 相对路径固化为生成代码, - /// 让消费端无需重复手写字符串常量和主键提取逻辑。 - /// - /// 已解析的 schema 模型。 - /// 辅助类型源码。 - private static string GenerateBindingsClass(SchemaFileSpec schema) + private static void AppendBindingsTypeHeader(StringBuilder builder, string bindingsClassName, string fileName) { - var registerMethodName = $"Register{schema.EntityName}Table"; - var getMethodName = $"Get{schema.EntityName}Table"; - var tryGetMethodName = $"TryGet{schema.EntityName}Table"; - var bindingsClassName = $"{schema.EntityName}ConfigBindings"; - var referenceSpecs = CollectReferenceSpecs(schema.RootObject).ToArray(); - - var builder = new StringBuilder(); - builder.AppendLine("// "); - builder.AppendLine("#nullable enable"); - builder.AppendLine(); - builder.AppendLine($"namespace {schema.Namespace};"); - builder.AppendLine(); builder.AppendLine("/// "); builder.AppendLine( - $"/// Auto-generated registration and lookup helpers for schema file '{schema.FileName}'."); + $"/// Auto-generated registration and lookup helpers for schema file '{fileName}'."); builder.AppendLine( "/// The helper centralizes table naming, config directory, schema path, and strongly-typed registry access so consumer projects do not need to duplicate the same conventions."); builder.AppendLine("/// "); builder.AppendLine($"public static class {bindingsClassName}"); builder.AppendLine("{"); + } + + private static void AppendBindingsReferenceMetadataType(StringBuilder builder) + { builder.AppendLine(" /// "); builder.AppendLine( " /// Describes one schema property that declares x-gframework-ref-table metadata."); @@ -2550,6 +2861,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" string referencedTableName,"); builder.AppendLine(" string valueSchemaType,"); builder.AppendLine(" bool isCollection)"); + AppendBindingsReferenceMetadataConstructorBody(builder); + AppendBindingsReferenceMetadataProperties(builder); + builder.AppendLine(" }"); + } + + private static void AppendBindingsReferenceMetadataConstructorBody(StringBuilder builder) + { builder.AppendLine(" {"); builder.AppendLine( " DisplayPath = displayPath ?? throw new global::System.ArgumentNullException(nameof(displayPath));"); @@ -2559,6 +2877,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator " ValueSchemaType = valueSchemaType ?? throw new global::System.ArgumentNullException(nameof(valueSchemaType));"); builder.AppendLine(" IsCollection = isCollection;"); builder.AppendLine(" }"); + } + + private static void AppendBindingsReferenceMetadataProperties(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2581,7 +2903,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator " /// Gets a value indicating whether the property stores multiple reference keys."); builder.AppendLine(" /// "); builder.AppendLine(" public bool IsCollection { get; }"); - builder.AppendLine(" }"); + } + + private static void AppendBindingsMetadataType(StringBuilder builder, SchemaFileSpec schema) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2615,6 +2940,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( $" public const string SchemaRelativePath = {SymbolDisplay.FormatLiteral(schema.SchemaRelativePath, true)};"); builder.AppendLine(" }"); + } + + private static void AppendBindingsMetadataAliases(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2637,7 +2966,12 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" /// "); builder.AppendLine(" public const string SchemaRelativePath = Metadata.SchemaRelativePath;"); builder.AppendLine(); - AppendYamlSerializationHelpers(builder, schema); + } + + private static void AppendBindingsReferencesType( + StringBuilder builder, + IReadOnlyList referenceSpecs) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2645,7 +2979,16 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" /// "); builder.AppendLine(" public static class References"); builder.AppendLine(" {"); + AppendBindingsReferenceMembers(builder, referenceSpecs); + AppendBindingsReferenceCollectionProperty(builder, referenceSpecs); + AppendBindingsTryGetByDisplayPathMethod(builder, referenceSpecs); + builder.AppendLine(" }"); + } + private static void AppendBindingsReferenceMembers( + StringBuilder builder, + IReadOnlyList referenceSpecs) + { foreach (var referenceSpec in referenceSpecs) { builder.AppendLine(" /// "); @@ -2664,29 +3007,38 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator $" {(referenceSpec.IsCollection ? "true" : "false")});"); builder.AppendLine(); } + } + private static void AppendBindingsReferenceCollectionProperty( + StringBuilder builder, + IReadOnlyList referenceSpecs) + { builder.AppendLine(" /// "); builder.AppendLine( " /// Gets all generated cross-table reference descriptors for the current schema."); builder.AppendLine(" /// "); - if (referenceSpecs.Length == 0) + if (referenceSpecs.Count == 0) { builder.AppendLine( " public static global::System.Collections.Generic.IReadOnlyList All { get; } = global::System.Array.Empty();"); + return; } - else + + builder.AppendLine( + " public static global::System.Collections.Generic.IReadOnlyList All { get; } = global::System.Array.AsReadOnly(new ReferenceMetadata[]"); + builder.AppendLine(" {"); + foreach (var referenceSpec in referenceSpecs) { - builder.AppendLine( - " public static global::System.Collections.Generic.IReadOnlyList All { get; } = global::System.Array.AsReadOnly(new ReferenceMetadata[]"); - builder.AppendLine(" {"); - foreach (var referenceSpec in referenceSpecs) - { - builder.AppendLine($" {referenceSpec.MemberName},"); - } - - builder.AppendLine(" });"); + builder.AppendLine($" {referenceSpec.MemberName},"); } + builder.AppendLine(" });"); + } + + private static void AppendBindingsTryGetByDisplayPathMethod( + StringBuilder builder, + IReadOnlyList referenceSpecs) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Tries to resolve generated reference metadata by schema property path."); @@ -2705,7 +3057,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" }"); builder.AppendLine(); - if (referenceSpecs.Length == 0) + if (referenceSpecs.Count == 0) { builder.AppendLine(" metadata = default;"); builder.AppendLine(" return false;"); @@ -2728,7 +3080,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator } builder.AppendLine(" }"); - builder.AppendLine(" }"); + } + + private static void AppendBindingsRegisterMethod( + StringBuilder builder, + SchemaFileSpec schema, + string registerMethodName) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2757,6 +3115,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine($" static config => config.{schema.KeyPropertyName},"); builder.AppendLine(" comparer);"); builder.AppendLine(" }"); + } + + private static void AppendBindingsGetMethod( + StringBuilder builder, + SchemaFileSpec schema, + string getMethodName) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Gets the generated config table wrapper from the registry."); @@ -2776,6 +3141,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( $" return new {schema.TableName}(registry.GetTable<{schema.KeyClrType}, {schema.ClassName}>(Metadata.TableName));"); builder.AppendLine(" }"); + } + + private static void AppendBindingsTryGetMethod( + StringBuilder builder, + SchemaFileSpec schema, + string tryGetMethodName) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Tries to get the generated config table wrapper from the registry."); @@ -2805,40 +3177,9 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" table = null;"); builder.AppendLine(" return false;"); builder.AppendLine(" }"); - builder.AppendLine("}"); - return builder.ToString().TrimEnd(); } - /// - /// 生成项目级聚合辅助源码。 - /// 该辅助把当前消费者项目内所有有效 schema 汇总为一个统一入口, - /// 以便运行时快速完成批量注册并在需要时枚举已生成的配置域元数据。 - /// - /// 当前编译中成功解析的 schema 集合。 - /// 聚合辅助源码。 - private static string GenerateCatalogClass(IReadOnlyList schemas) - { - var builder = new StringBuilder(); - builder.AppendLine("// "); - builder.AppendLine("#nullable enable"); - builder.AppendLine(); - builder.AppendLine($"namespace {GeneratedNamespace};"); - builder.AppendLine(); - AppendGeneratedConfigCatalogType(builder, schemas); - builder.AppendLine(); - AppendGeneratedConfigRegistrationOptionsType(builder, schemas); - builder.AppendLine(); - AppendGeneratedConfigRegistrationExtensionsType(builder, schemas); - - return builder.ToString().TrimEnd(); - } - - /// - /// Emits the generated catalog type that exposes schema metadata and filtering helpers. - /// - /// Output buffer. - /// Successfully parsed schemas for the current compilation. - private static void AppendGeneratedConfigCatalogType(StringBuilder builder, IReadOnlyList schemas) + private static void AppendGeneratedConfigCatalogHeader(StringBuilder builder) { builder.AppendLine("/// "); builder.AppendLine( @@ -2848,6 +3189,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine("/// "); builder.AppendLine("public static class GeneratedConfigCatalog"); builder.AppendLine("{"); + } + + private static void AppendGeneratedConfigTableMetadataType(StringBuilder builder) + { builder.AppendLine(" /// "); builder.AppendLine( " /// Describes one generated config table so bootstrap code can enumerate generated domains without re-parsing schema files at runtime."); @@ -2867,6 +3212,13 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" string tableName,"); builder.AppendLine(" string configRelativePath,"); builder.AppendLine(" string schemaRelativePath)"); + AppendGeneratedConfigTableMetadataConstructorBody(builder); + AppendGeneratedConfigTableMetadataProperties(builder); + builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigTableMetadataConstructorBody(StringBuilder builder) + { builder.AppendLine(" {"); builder.AppendLine( " ConfigDomain = configDomain ?? throw new global::System.ArgumentNullException(nameof(configDomain));"); @@ -2877,6 +3229,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( " SchemaRelativePath = schemaRelativePath ?? throw new global::System.ArgumentNullException(nameof(schemaRelativePath));"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigTableMetadataProperties(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Gets the logical config domain derived from the schema base name."); @@ -2899,7 +3255,12 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" /// Gets the relative schema file path collected by the source generator."); builder.AppendLine(" /// "); builder.AppendLine(" public string SchemaRelativePath { get; }"); - builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigTablesProperty( + StringBuilder builder, + IReadOnlyList schemas) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2919,6 +3280,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator } builder.AppendLine(" });"); + } + + private static void AppendGeneratedConfigResolveAbsolutePathMethod(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -2953,6 +3318,12 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( " return global::System.IO.Path.Combine(configRootPath, normalizedRelativePath);"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigTryGetByTableNameMethod( + StringBuilder builder, + IReadOnlyList schemas) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine(" /// Tries to resolve generated table metadata by runtime registration name."); @@ -2986,6 +3357,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(" metadata = default;"); builder.AppendLine(" return false;"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigGetTablesInConfigDomainMethod(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -3018,6 +3393,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( " return matchedTables.Count == 0 ? global::System.Array.Empty() : matchedTables.ToArray();"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigGetTablesForRegistrationMethod(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -3042,6 +3421,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine( " return matchedTables.Count == 0 ? global::System.Array.Empty() : matchedTables.ToArray();"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigMatchesRegistrationOptionsMethod(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -3076,6 +3459,10 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(); builder.AppendLine(" return options.TableFilter?.Invoke(metadata) ?? true;"); builder.AppendLine(" }"); + } + + private static void AppendGeneratedConfigAllowListMethod(StringBuilder builder) + { builder.AppendLine(); builder.AppendLine(" /// "); builder.AppendLine( @@ -3106,7 +3493,6 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator builder.AppendLine(); builder.AppendLine(" return false;"); builder.AppendLine(" }"); - builder.AppendLine("}"); } /// 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 bc42ecc2..a4dfd97f 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-028` -- 当前阶段:`Phase 28` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-029` +- 当前阶段:`Phase 29` - 当前焦点: - 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次 - 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock @@ -38,6 +38,9 @@ - 已完成当前分支与 `main` 的 `CqrsHandlerRegistryGenerator.cs` 文件级冲突收口:确认 `main` 侧新增的是 `OrderedRegistrationKind` / `RuntimeTypeReferenceSpec` 的 XML 文档,现已按当前 partial 拆分结构迁移到 `CqrsHandlerRegistryGenerator.Models.cs`,不回退已完成的生成器拆分 + - 已完成 `SchemaConfigGenerator.cs` 剩余 `MA0051` 收口:将 `dependentRequired` / `allOf` / conditional schema 校验 + 拆成更小的验证阶段,并将 `GenerateTableClass`、`GenerateBindingsClass`、`AppendGeneratedConfigCatalogType` + 拆成稳定的代码发射 helper,保持生成输出与快照一致 - 已更新 `AGENTS.md`:变更模块必须运行对应 `dotnet build -c Release`,并处理或显式报告模块构建 warning, 不再默认留给长期 warning 清理分支 - `CoroutineScheduler` 的 tag/group 字典已显式使用 `StringComparer.Ordinal`,保持既有区分大小写语义 @@ -46,8 +49,7 @@ - 当前 `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` 条降到 `9` 条,剩余均为 - `SchemaConfigGenerator.cs` 的 `MA0051` + - 当前 `GFramework.Game.SourceGenerators` warnings-only 基线已从 `46` 条降到 `0` 条 - `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑 - 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进 - 下一轮默认继续拆分 `GFramework.Game.SourceGenerators` 的 `MA0051` 热点,或评估跨 target 的 `MA0158` @@ -84,8 +86,7 @@ inherited-collision 快照测试 - 已完成当前分支与 `main` 的 `CqrsHandlerRegistryGenerator.cs` 冲突化解:保留当前 partial 结构,并把 `main` 侧新增的模型文档合并到 `CqrsHandlerRegistryGenerator.Models.cs` -- 已完成 `GFramework.Game.SourceGenerators` 中 `SchemaConfigGenerator` 的第一批 `MA0051` 收口;warnings-only 基线剩余 `9` 条 - `MA0051` +- 已完成 `GFramework.Game.SourceGenerators` 中 `SchemaConfigGenerator` 的剩余 `MA0051` 收口;warnings-only 基线已降到 `0` 条 ## 当前活跃事实 @@ -141,6 +142,8 @@ - `RP-025` 继续复核 PR #269 剩余 outside-diff / nitpick 信号后,确认本地仍成立的是 `SchemaConfigGenerator` 的归一化字段名冲突与 `Cqrs` 对 `dynamic` 的直接类型引用;已分别补上诊断、运行时类型归一化与回归测试, 并把“变更模块必须运行对应 build 且处理 warning”的治理规则写回 `AGENTS.md` +- `RP-029` 已完成 `SchemaConfigGenerator` 剩余 `MA0051` 收口:`GFramework.Game.SourceGenerators` 独立 Release + warnings-only build 已清零,并通过 `SchemaConfigGenerator` focused generator tests 锁定生成输出未回退 - 当前工作树分支 `fix/analyzer-warning-reduction-batch` 已在 `ai-plan/public/README.md` 建立 topic 映射 ## 当前风险 @@ -311,13 +314,24 @@ - 说明:测试项目构建仍会显示既有 `GFramework.SourceGenerators.Tests` analyzer warning;不属于本轮写集 - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~CollectionExtensionsTests|FullyQualifiedName~LoggingConfigurationTests" -m:1 -p:RestoreFallbackFolders="" -v minimal` - 结果:`27 Passed`,`0 Failed` +- `RP-029` 的验证结果: + - `dotnet restore GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -p:RestoreFallbackFolders="" -nologo` + - 结果:通过;刷新 Linux 侧 restore 资产以移除 Windows fallback package folder 干扰 + - `dotnet restore GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -p:RestoreFallbackFolders="" -nologo` + - 结果:通过;focused test 所属测试项目已同步刷新 Linux 侧 restore 资产 + - `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release -t:Rebuild --no-restore -p:UseSharedCompilation=false -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"` + - 结果:`0 Warning(s)`,`0 Error(s)`;`SchemaConfigGenerator.cs` 剩余 `MA0051` 已清零 + - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~SchemaConfigGenerator -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`54 Passed`,`0 Failed` + - 说明:测试项目构建仍显示既有 `GFramework.SourceGenerators.Tests` `MA0048` / `MA0051` / `MA0004` warning;不属于本轮 + `GFramework.Game.SourceGenerators` 写集 - active 跟踪文件只保留当前恢复点、活跃事实、风险与下一步,不再重复保存已完成阶段的长篇历史 ## 下一步 1. 若要继续该主题,先读 active tracking,再按需展开历史归档中的 warning 热点与验证记录 -2. 下一轮优先继续拆分 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的剩余 `MA0051`;建议先从 - `GenerateBindingsClass`、`AppendGeneratedConfigCatalogType` 或对象/条件 schema target 验证方法切入 +2. 下一轮优先评估是否将 `GFramework.SourceGenerators.Tests` 的既有 `MA0051` / `MA0004` / `MA0048` 作为独立 warning + 清理切片;若进入该写集,需要先明确测试项目 warning 也属于本轮必须处理的模块范围 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 ca74deca..c789f477 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,45 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-23 — RP-029 + +### 阶段:`SchemaConfigGenerator.cs` 剩余 `MA0051` 收口(RP-029) + +- 启动复核: + - 按 `gframework-boot` 流程恢复当前 worktree 后,先读取 `AGENTS.md`、`.ai/environment/tools.ai.yaml`、`ai-plan/public/README.md` + 与 active topic 跟踪文件,确认当前分支 `fix/analyzer-warning-reduction-batch` 仍映射到 + `analyzer-warning-reduction` + - 用历史基线命令重新执行 `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release -t:Rebuild --no-restore -p:UseSharedCompilation=false -p:RestoreFallbackFolders="" -nologo -clp:"Summary;WarningsOnly"`, + 复现 `SchemaConfigGenerator.cs` 剩余 `9` 条 `MA0051` +- 决策: + - 继续沿用“低风险结构拆分、不改诊断 ID、不改生成顺序、不改快照输出”的收口策略 + - 先把 schema 元数据校验方法拆成更小验证阶段,再把 `GenerateTableClass`、`GenerateBindingsClass` 与 + `AppendGeneratedConfigCatalogType` 的代码发射流程分段,避免直接改动生成文本内容 + - focused test 仍以 `SchemaConfigGenerator` 相关用例为主;`GFramework.SourceGenerators.Tests` 里既有测试项目 warning + 不纳入本轮写集 +- 实施调整: + - 为 `dependentRequired`、`dependentSchemas`、`allOf`、conditional schema 等对象级校验补上细粒度 helper, + 把 declared-properties 获取、分支校验、target 校验拆成独立阶段 + - 为生成代码头部、表包装、bindings metadata/references、catalog metadata 发射补充结构化 helper, + 将长方法按“头部 / 元数据 / 行为方法”拆分 + - 修正 `References` 代码发射 helper 的闭合范围,确保重构后的 `MonsterConfigBindings.g.cs` 与现有快照保持一致 + - 在构建阶段遇到 Linux `dotnet` 命中 Windows fallback package folder 时,先对 + `GFramework.Game.SourceGenerators` 与 `GFramework.SourceGenerators.Tests` 执行 + `dotnet restore -p:RestoreFallbackFolders=""`,再继续 `--no-restore` 验证 +- 验证结果: + - `dotnet restore GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -p:RestoreFallbackFolders="" -nologo` + - 结果:通过 + - `dotnet restore GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.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"` + - 结果:`0 Warning(s)`,`0 Error(s)` + - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~SchemaConfigGenerator -m:1 -p:RestoreFallbackFolders="" -nologo` + - 结果:`54 Passed`,`0 Failed` + - 说明:测试项目构建仍打印既有 `MA0048` / `MA0051` / `MA0004` warning;这些 warning 属于 `GFramework.SourceGenerators.Tests` + 基线,不属于本轮 `GFramework.Game.SourceGenerators` 写集 +- 下一步建议: + - 若继续 analyzer warning reduction,可评估是否为 `GFramework.SourceGenerators.Tests` 单独开新的 warning 清理切片 + - 若改回推进运行时主线,则按 `RP-017` 记录的策略先设计 `MA0158` 的多 target 兼容方案,再决定是否动共享 `object` lock + ## 2026-04-23 — RP-028 ### 阶段:`CqrsHandlerRegistryGenerator.cs` 文件级冲突化解(RP-028)