mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
fix(source-generators): 收口PR269剩余review问题
- 修复 Cqrs handler registry 对 Roslyn error type 的直接引用,改走安全的运行时类型查找 - 补充 SchemaConfigGenerator 根 type 非字符串诊断回归与 Cqrs 未解析类型回归测试 - 更新 analyzer-warning-reduction 的 RP-024 跟踪与验证记录
This commit is contained in:
parent
df68cdfd82
commit
4ef9406ee9
@ -253,6 +253,11 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
|
||||
private static bool CanReferenceFromGeneratedRegistry(Compilation compilation, ITypeSymbol type)
|
||||
{
|
||||
// Roslyn error symbols stringify to unresolved type names; emitting them via typeof(...) would turn
|
||||
// an existing user-code error into a second generator-produced compile error instead of falling back.
|
||||
if (type.TypeKind == TypeKind.Error)
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case IArrayTypeSymbol arrayType:
|
||||
@ -274,7 +279,7 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
case ITypeParameterSymbol:
|
||||
return false;
|
||||
default:
|
||||
// Treat other Roslyn type kinds, such as dynamic or unresolved error types, as referenceable for now.
|
||||
// Treat other Roslyn type kinds, such as dynamic, as referenceable for now.
|
||||
// If a real-world case proves unsafe, tighten this branch instead of broadening the named-type path above.
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -221,6 +221,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
private static bool TryValidateSchemaRoot(string filePath, JsonElement root, out Diagnostic? diagnostic)
|
||||
{
|
||||
if (!root.TryGetProperty("type", out var rootTypeElement) ||
|
||||
rootTypeElement.ValueKind != JsonValueKind.String ||
|
||||
!IsSchemaType(rootTypeElement.GetString() ?? string.Empty, "object"))
|
||||
{
|
||||
diagnostic = Diagnostic.Create(
|
||||
|
||||
@ -72,6 +72,45 @@ public class SchemaConfigGeneratorTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证根节点 <c>type</c> 元数据不是字符串时,会返回根对象约束诊断,而不是抛出 JSON 访问异常。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Run_Should_Report_Diagnostic_When_Root_Type_Metadata_Is_Not_A_String()
|
||||
{
|
||||
const string source = """
|
||||
namespace TestApp
|
||||
{
|
||||
public sealed class Dummy
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
const string schema = """
|
||||
{
|
||||
"type": 123,
|
||||
"required": ["id"],
|
||||
"properties": {
|
||||
"id": { "type": "integer" }
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
var result = SchemaGeneratorTestDriver.Run(
|
||||
source,
|
||||
("monster.schema.json", schema));
|
||||
|
||||
var diagnostic = result.Results.Single().Diagnostics.Single();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(diagnostic.Id, Is.EqualTo("GF_ConfigSchema_002"));
|
||||
Assert.That(diagnostic.Severity, Is.EqualTo(DiagnosticSeverity.Error));
|
||||
Assert.That(diagnostic.GetMessage(), Does.Contain("monster.schema.json"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 schema 文件名若生成无效根类型标识符时,会在生成前产生命名明确的诊断。
|
||||
/// </summary>
|
||||
|
||||
@ -1375,6 +1375,103 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 handler 合同里出现未解析错误类型时,生成器会改为运行时精确查找该类型,
|
||||
/// 而不会把无效类型名直接写进生成代码中的 <c>typeof(...)</c>。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void Emits_Runtime_Type_Lookup_When_Handler_Contract_Contains_Unresolved_Error_Types()
|
||||
{
|
||||
const string source = """
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
public interface IServiceCollection { }
|
||||
|
||||
public static class ServiceCollectionServiceExtensions
|
||||
{
|
||||
public static void AddTransient(IServiceCollection services, Type serviceType, Type implementationType) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Core.Abstractions.Logging
|
||||
{
|
||||
public interface ILogger
|
||||
{
|
||||
void Debug(string msg);
|
||||
}
|
||||
}
|
||||
|
||||
namespace GFramework.Cqrs.Abstractions.Cqrs
|
||||
{
|
||||
public interface IRequest<TResponse> { }
|
||||
public interface INotification { }
|
||||
public interface IStreamRequest<TResponse> { }
|
||||
|
||||
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
|
||||
public interface INotificationHandler<in TNotification> where TNotification : INotification { }
|
||||
public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
|
||||
}
|
||||
|
||||
namespace GFramework.Cqrs
|
||||
{
|
||||
public interface ICqrsHandlerRegistry
|
||||
{
|
||||
void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
|
||||
public sealed class CqrsHandlerRegistryAttribute : Attribute
|
||||
{
|
||||
public CqrsHandlerRegistryAttribute(Type registryType) { }
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public sealed class CqrsReflectionFallbackAttribute : Attribute
|
||||
{
|
||||
public CqrsReflectionFallbackAttribute(params string[] fallbackHandlerTypeNames) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace TestApp
|
||||
{
|
||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
|
||||
public sealed record BrokenRequest() : IRequest<MissingResponse>;
|
||||
|
||||
public sealed class BrokenHandler : IRequestHandler<BrokenRequest, MissingResponse>
|
||||
{
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
var execution = ExecuteGenerator(source);
|
||||
var inputCompilationErrors = execution.InputCompilationDiagnostics
|
||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
var generatedCompilationErrors = execution.GeneratedCompilationDiagnostics
|
||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
var generatorErrors = execution.GeneratorDiagnostics
|
||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0246"));
|
||||
Assert.That(generatedCompilationErrors, Is.Empty);
|
||||
Assert.That(generatorErrors, Is.Empty);
|
||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
Does.Contain("registryAssembly.GetType(\"MissingResponse\", throwOnError: false, ignoreCase: false);"));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
Does.Contain("internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当 fallback metadata 仍然必需且 runtime 提供了承载契约时,
|
||||
/// 生成器会继续产出注册器并发射程序集级 <c>CqrsReflectionFallbackAttribute</c>。
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-023`
|
||||
- 当前阶段:`Phase 23`
|
||||
- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-024`
|
||||
- 当前阶段:`Phase 24`
|
||||
- 当前焦点:
|
||||
- 已完成 `GFramework.Core` 当前 `MA0016` / `MA0002` / `MA0015` / `MA0077` 低风险收口批次
|
||||
- 已复核 `net10.0` 下的 `MA0158` 基线:`GFramework.Core` / `GFramework.Cqrs` 当前共有 `16` 个 object lock
|
||||
@ -25,6 +25,8 @@
|
||||
`FilterConfiguration` 的公共 API 兼容形状,并将 analyzer 兼容性处理收敛到局部 pragma
|
||||
- 已完成当前 PR #269 第三轮 follow-up:继续收口 `SchemaConfigGenerator` 的根类型标识符校验与 XML 文档转义,
|
||||
并补齐 `LoggingConfigurationTests`、`CollectionExtensionsTests`、`Cqrs` helper 抽取与 `ai-plan` 命令文本修正
|
||||
- 已完成当前 PR #269 第四轮 follow-up:将 `CqrsHandlerRegistryGenerator` 的 Roslyn error type 直接引用改为
|
||||
运行时精确查找路径,并为 `SchemaConfigGenerator` 补上根 `type` 非字符串时的防御与回归测试
|
||||
- `CoroutineScheduler` 的 tag/group 字典已显式使用 `StringComparer.Ordinal`,保持既有区分大小写语义
|
||||
- `EasyEvents.AddEvent<T>()` 的重复注册路径已恢复为 `ArgumentException`,以保持既有异常契约
|
||||
- `Option<T>` 已声明 `IEquatable<Option<T>>`,与已有强类型 `Equals(Option<T>)` 契约对齐
|
||||
@ -59,6 +61,8 @@
|
||||
- 已完成当前 PR #269 的 review follow-up:收口 `ContextAwareGenerator` 的字段命名冲突 / 锁内读取契约、
|
||||
`CqrsHandlerRegistryGenerator` 的运行时类型 null 防御与超大文件拆分、`SchemaConfigGenerator` 的取消语义,
|
||||
并恢复 `EasyEvents` / `CollectionExtensions` / logging 配置模型的公共 API 兼容形状
|
||||
- 已完成当前 PR #269 的第四轮 review follow-up:确认 5 个 latest-head 未解决线程中仅剩 2 个本地仍成立,
|
||||
已分别在 `CqrsHandlerRegistryGenerator` 与 `SchemaConfigGenerator` 中收口,并补齐定向 generator regression tests
|
||||
- 已完成 `GFramework.Game.SourceGenerators` 中 `SchemaConfigGenerator` 的第一批 `MA0051` 收口;warnings-only 基线剩余 `9` 条
|
||||
`MA0051`
|
||||
|
||||
@ -110,6 +114,9 @@
|
||||
- `RP-023` 继续复核 PR #269 剩余 nitpick/outside-diff 项,确认仍成立的项集中在 `SchemaConfigGenerator` 根类型名校验、
|
||||
aggregate registration comparer XML 文档转义、logging / collection 反射测试补强,以及跟踪文档中的
|
||||
`RestoreFallbackFolders=""` 可复制性问题
|
||||
- `RP-024` 使用 `$gframework-pr-review` 继续复核 PR #269 latest-head unresolved threads,确认 `EasyEvents` 异常契约、
|
||||
`SchemaConfigGenerator` 取消传播与 `ContextAwareGenerator` 快照冲突线程均已在本地收口,仅剩 `Cqrs` error type
|
||||
直接引用与根 schema `type` 非字符串防御仍成立;现已补齐实现与回归测试
|
||||
- 当前工作树分支 `fix/analyzer-warning-reduction-batch` 已在 `ai-plan/public/README.md` 建立 topic 映射
|
||||
|
||||
## 当前风险
|
||||
|
||||
@ -1,5 +1,37 @@
|
||||
# Analyzer Warning Reduction 追踪
|
||||
|
||||
## 2026-04-22 — RP-024
|
||||
|
||||
### 阶段:PR #269 第四轮 review follow-up 收口(RP-024)
|
||||
|
||||
- 启动复核:
|
||||
- 延续 `$gframework-pr-review` 对 PR #269 latest-head unresolved threads 的复核,重点核对最新 5 个未解决线程是否仍与当前
|
||||
worktree 一致
|
||||
- 本地确认 `EasyEvents` 异常契约、`SchemaConfigGenerator` 取消传播与 `ContextAwareGenerator` 字段冲突线程已是陈旧信号,
|
||||
真正仍成立的仅剩 `CqrsHandlerRegistryGenerator` 的 Roslyn error type 直接引用,以及根 schema `type` 非字符串时的
|
||||
`GetString()` 防御
|
||||
- 决策:
|
||||
- `CqrsHandlerRegistryGenerator` 保持现有“优先精确重建、必要时退回运行时查找”的设计,不引入新的程序集级 fallback 契约分支;
|
||||
只在 `CanReferenceFromGeneratedRegistry(...)` 中显式拒绝 `TypeKind.Error`,让未解析类型走已有运行时查找路径
|
||||
- `SchemaConfigGenerator` 继续沿用现有 `GF_ConfigSchema_002` 诊断,不新增诊断 ID;仅在根对象校验入口补上
|
||||
`JsonValueKind.String` 前置判断
|
||||
- 实施调整:
|
||||
- 为 `CqrsHandlerRegistryGenerator.RuntimeTypeReferences` 增加 `TypeKind.Error` 防御,避免把未解析类型写成生成代码里的
|
||||
`typeof(...)`
|
||||
- 为 `SchemaConfigGeneratorTests` 补上根 `type` 为数字时返回 `GF_ConfigSchema_002` 的回归测试
|
||||
- 为 `CqrsHandlerRegistryGeneratorTests` 补上未解析 error type 会改走运行时 `GetType(...)` 精确查找的回归测试
|
||||
- 验证结果:
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release --no-restore -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:`0 Warning(s)`,`0 Error(s)`
|
||||
- `dotnet build GFramework.Game.SourceGenerators/GFramework.Game.SourceGenerators.csproj -c Release --no-restore -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:通过;仍保留既有 `9` 条 `SchemaConfigGenerator.cs` `MA0051`
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~SchemaConfigGeneratorTests.Run_Should_Report_Diagnostic_When_Root_Type_Metadata_Is_Not_A_String|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Runtime_Type_Lookup_When_Handler_Contract_Contains_Unresolved_Error_Types" -m:1 -p:RestoreFallbackFolders="" -v minimal`
|
||||
- 结果:`2 Passed`,`0 Failed`
|
||||
- 说明:测试命令需在无沙箱环境下运行,因为当前 test host 在沙箱内创建本地 socket 会收到 `Permission denied`
|
||||
- 下一步建议:
|
||||
- 若继续压缩 PR #269 的 review backlog,可再次抓取最新 unresolved threads,确认 GitHub 上仅剩陈旧线程后再决定是否继续代码改动
|
||||
- 若回到 analyzer 主线,继续推进 `SchemaConfigGenerator.cs` 剩余 `MA0051`
|
||||
|
||||
## 2026-04-22 — RP-023
|
||||
|
||||
### 阶段:PR #269 第三轮 review follow-up 收口(RP-023)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user