diff --git a/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs b/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs index f74ec308..487061ac 100644 --- a/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs +++ b/GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs @@ -89,17 +89,42 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator if (!IsConcreteHandlerType(type)) return null; - var handlerInterfaces = type.AllInterfaces - .Where(IsSupportedHandlerInterface) - .OrderBy(GetTypeSortKey, StringComparer.Ordinal) - .ToImmutableArray(); + var handlerInterfaces = GetSupportedHandlerInterfaces(type); if (handlerInterfaces.IsDefaultOrEmpty) return null; + return CreateHandlerCandidateAnalysis(context.SemanticModel.Compilation, type, handlerInterfaces); + } + + /// + /// 收集当前实现类型已经关闭的 CQRS handler 接口,并按稳定显示名排序以保证生成输出可重复。 + /// + /// 当前语义模型发现的具体 handler 实现类型。 + /// 可由 CQRS 注册器生成器处理的 handler 接口集合。 + private static ImmutableArray GetSupportedHandlerInterfaces(INamedTypeSymbol type) + { + return type.AllInterfaces + .Where(IsSupportedHandlerInterface) + .OrderBy(GetTypeSortKey, StringComparer.Ordinal) + .ToImmutableArray(); + } + + /// + /// 将单个实现类型的 handler 接口拆分为直接注册、实现类型反射注册、精确反射注册和兜底 fallback 四类结果。 + /// + /// 当前生成轮次的编译上下文,用于判断类型可访问性。 + /// 需要分析的 handler 实现类型。 + /// 该实现类型声明的受支持 CQRS handler 接口。 + /// 供最终生成阶段消费的 handler 候选分析结果。 + private static HandlerCandidateAnalysis CreateHandlerCandidateAnalysis( + Compilation compilation, + INamedTypeSymbol type, + ImmutableArray handlerInterfaces) + { var implementationTypeDisplayName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); var implementationLogName = GetLogDisplayName(type); - var canReferenceImplementation = CanReferenceFromGeneratedRegistry(context.SemanticModel.Compilation, type); + var canReferenceImplementation = CanReferenceFromGeneratedRegistry(compilation, type); var registrations = ImmutableArray.CreateBuilder(handlerInterfaces.Length); var reflectedImplementationRegistrations = ImmutableArray.CreateBuilder(handlerInterfaces.Length); @@ -108,32 +133,16 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator string? reflectionFallbackHandlerTypeMetadataName = null; foreach (var handlerInterface in handlerInterfaces) { - var canReferenceHandlerInterface = - CanReferenceFromGeneratedRegistry(context.SemanticModel.Compilation, handlerInterface); - if (canReferenceImplementation && canReferenceHandlerInterface) - { - registrations.Add(new HandlerRegistrationSpec( - handlerInterface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - implementationTypeDisplayName, - GetLogDisplayName(handlerInterface), - implementationLogName)); - continue; - } - - if (!canReferenceImplementation && canReferenceHandlerInterface) - { - reflectedImplementationRegistrations.Add(new ReflectedImplementationRegistrationSpec( - handlerInterface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - GetLogDisplayName(handlerInterface))); - continue; - } - - if (TryCreatePreciseReflectedRegistration( - context.SemanticModel.Compilation, + if (TryAddStaticHandlerRegistration( + compilation, handlerInterface, - out var preciseReflectedRegistration)) + canReferenceImplementation, + implementationTypeDisplayName, + implementationLogName, + registrations, + reflectedImplementationRegistrations, + preciseReflectedRegistrations)) { - preciseReflectedRegistrations.Add(preciseReflectedRegistration); continue; } @@ -155,6 +164,56 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator reflectionFallbackHandlerTypeMetadataName); } + /// + /// 尝试为单个 handler 接口选择无需程序集级 fallback 的注册表示。 + /// + /// 当前生成轮次的编译上下文。 + /// 正在分类的关闭 handler 接口。 + /// 生成代码是否可直接引用实现类型。 + /// 实现类型在生成源码中的全限定显示名。 + /// 实现类型用于日志输出的稳定显示名。 + /// 直接类型注册集合。 + /// 实现类型需要反射解析、接口可直接引用的注册集合。 + /// 接口类型需要运行时精确构造的注册集合。 + /// + /// 当当前接口已经被添加到某个静态注册集合时返回 ;否则调用方应记录 reflection fallback 元数据。 + /// + private static bool TryAddStaticHandlerRegistration( + Compilation compilation, + INamedTypeSymbol handlerInterface, + bool canReferenceImplementation, + string implementationTypeDisplayName, + string implementationLogName, + ImmutableArray.Builder registrations, + ImmutableArray.Builder reflectedImplementationRegistrations, + ImmutableArray.Builder preciseReflectedRegistrations) + { + var canReferenceHandlerInterface = CanReferenceFromGeneratedRegistry(compilation, handlerInterface); + if (canReferenceImplementation && canReferenceHandlerInterface) + { + registrations.Add(new HandlerRegistrationSpec( + handlerInterface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + implementationTypeDisplayName, + GetLogDisplayName(handlerInterface), + implementationLogName)); + return true; + } + + if (!canReferenceImplementation && canReferenceHandlerInterface) + { + reflectedImplementationRegistrations.Add(new ReflectedImplementationRegistrationSpec( + handlerInterface.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + GetLogDisplayName(handlerInterface))); + return true; + } + + if (!TryCreatePreciseReflectedRegistration(compilation, handlerInterface, out var preciseReflectedRegistration)) + return false; + + preciseReflectedRegistrations.Add(preciseReflectedRegistration); + return true; + } + /// /// 执行 CQRS handler registry 生成管线的最终发射阶段,负责将候选 handler 分析结果汇总为单个 /// CqrsHandlerRegistry.g.cs,并在需要时附带程序集级 reflection fallback 元数据。 @@ -423,41 +482,17 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator if (type is INamedTypeSymbol genericNamedType && genericNamedType.IsGenericType && - !genericNamedType.IsUnboundGenericType && - TryCreateGenericTypeDefinitionReference(compilation, genericNamedType, - out var genericTypeDefinitionReference)) + !genericNamedType.IsUnboundGenericType) { - var genericTypeArguments = - ImmutableArray.CreateBuilder(genericNamedType.TypeArguments.Length); - foreach (var typeArgument in genericNamedType.TypeArguments) - { - if (!TryCreateRuntimeTypeReference(compilation, typeArgument, out var genericTypeArgumentReference)) - { - runtimeTypeReference = null; - return false; - } - - genericTypeArguments.Add(genericTypeArgumentReference!); - } - - runtimeTypeReference = RuntimeTypeReferenceSpec.FromConstructedGeneric( - genericTypeDefinitionReference!, - genericTypeArguments.ToImmutable()); - return true; + return TryCreateConstructedGenericRuntimeTypeReference( + compilation, + genericNamedType, + out runtimeTypeReference); } if (type is INamedTypeSymbol namedType) { - if (SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly)) - { - runtimeTypeReference = RuntimeTypeReferenceSpec.FromReflectionLookup( - GetReflectionTypeMetadataName(namedType)); - return true; - } - - runtimeTypeReference = RuntimeTypeReferenceSpec.FromExternalReflectionLookup( - namedType.ContainingAssembly.Identity.ToString(), - GetReflectionTypeMetadataName(namedType)); + runtimeTypeReference = CreateNamedRuntimeTypeReference(compilation, namedType); return true; } @@ -465,6 +500,66 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator return false; } + /// + /// 为已构造泛型类型构造运行时类型引用,并递归验证每个泛型实参都可以稳定编码到生成输出中。 + /// + /// 当前生成轮次的编译上下文。 + /// 需要表示的已构造泛型类型。 + /// + /// 当方法返回 时,包含泛型定义和泛型实参的运行时重建描述。 + /// + /// 当泛型定义和全部泛型实参都能表达时返回 + private static bool TryCreateConstructedGenericRuntimeTypeReference( + Compilation compilation, + INamedTypeSymbol genericNamedType, + out RuntimeTypeReferenceSpec? runtimeTypeReference) + { + if (!TryCreateGenericTypeDefinitionReference( + compilation, + genericNamedType, + out var genericTypeDefinitionReference)) + { + runtimeTypeReference = null; + return false; + } + + var genericTypeArguments = + ImmutableArray.CreateBuilder(genericNamedType.TypeArguments.Length); + foreach (var typeArgument in genericNamedType.TypeArguments) + { + if (!TryCreateRuntimeTypeReference(compilation, typeArgument, out var genericTypeArgumentReference)) + { + runtimeTypeReference = null; + return false; + } + + genericTypeArguments.Add(genericTypeArgumentReference!); + } + + runtimeTypeReference = RuntimeTypeReferenceSpec.FromConstructedGeneric( + genericTypeDefinitionReference!, + genericTypeArguments.ToImmutable()); + return true; + } + + /// + /// 为无法直接书写的命名类型选择当前程序集反射查找或外部程序集反射查找表示。 + /// + /// 当前生成轮次的编译上下文。 + /// 需要在运行时解析的命名类型。 + /// 适合写入生成注册器的命名类型运行时引用。 + private static RuntimeTypeReferenceSpec CreateNamedRuntimeTypeReference( + Compilation compilation, + INamedTypeSymbol namedType) + { + if (SymbolEqualityComparer.Default.Equals(namedType.ContainingAssembly, compilation.Assembly)) + return RuntimeTypeReferenceSpec.FromReflectionLookup(GetReflectionTypeMetadataName(namedType)); + + return RuntimeTypeReferenceSpec.FromExternalReflectionLookup( + namedType.ContainingAssembly.Identity.ToString(), + GetReflectionTypeMetadataName(namedType)); + } + /// /// 为已构造泛型类型解析其泛型定义的运行时引用描述。 /// @@ -626,34 +721,56 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator GenerationEnvironment generationEnvironment, IReadOnlyList registrations, IReadOnlyList fallbackHandlerTypeMetadataNames) + { + var sourceShape = CreateGeneratedRegistrySourceShape(registrations); + var builder = new StringBuilder(); + AppendGeneratedSourcePreamble(builder, generationEnvironment, fallbackHandlerTypeMetadataNames); + AppendGeneratedRegistryType(builder, registrations, sourceShape); + return builder.ToString(); + } + + /// + /// 预先计算生成注册器需要的辅助分支,让主源码发射流程保持线性且避免重复扫描注册集合。 + /// + /// 已整理并排序的 handler 注册描述。 + /// 当前生成输出需要启用的结构分支。 + private static GeneratedRegistrySourceShape CreateGeneratedRegistrySourceShape( + IReadOnlyList registrations) { var hasReflectedImplementationRegistrations = registrations.Any(static registration => !registration.ReflectedImplementationRegistrations.IsDefaultOrEmpty); var hasPreciseReflectedRegistrations = registrations.Any(static registration => !registration.PreciseReflectedRegistrations.IsDefaultOrEmpty); + var hasReflectionTypeLookups = registrations.Any(static registration => + !string.IsNullOrWhiteSpace(registration.ReflectionTypeMetadataName)); var hasExternalAssemblyTypeLookups = registrations.Any(static registration => registration.PreciseReflectedRegistrations.Any(static preciseRegistration => preciseRegistration.ServiceTypeArguments.Any(ContainsExternalAssemblyTypeLookup))); - var builder = new StringBuilder(); + + return new GeneratedRegistrySourceShape( + hasReflectedImplementationRegistrations, + hasPreciseReflectedRegistrations, + hasReflectionTypeLookups, + hasExternalAssemblyTypeLookups); + } + + /// + /// 发射生成文件头、nullable 指令以及注册器所需的程序集级元数据特性。 + /// + /// 生成源码构造器。 + /// 当前轮次的生成环境。 + /// 需要程序集级 reflection fallback 的 handler 元数据名称。 + private static void AppendGeneratedSourcePreamble( + StringBuilder builder, + GenerationEnvironment generationEnvironment, + IReadOnlyList fallbackHandlerTypeMetadataNames) + { builder.AppendLine("// "); builder.AppendLine("#nullable enable"); builder.AppendLine(); if (generationEnvironment.SupportsReflectionFallbackAttribute && fallbackHandlerTypeMetadataNames.Count > 0) { - builder.Append("[assembly: global::"); - builder.Append(CqrsRuntimeNamespace); - builder.Append(".CqrsReflectionFallbackAttribute("); - for (var index = 0; index < fallbackHandlerTypeMetadataNames.Count; index++) - { - if (index > 0) - builder.Append(", "); - - builder.Append('"'); - builder.Append(EscapeStringLiteral(fallbackHandlerTypeMetadataNames[index])); - builder.Append('"'); - } - - builder.AppendLine(")]"); + AppendReflectionFallbackAttribute(builder, fallbackHandlerTypeMetadataNames); builder.AppendLine(); } @@ -664,7 +781,44 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator builder.Append('.'); builder.Append(GeneratedTypeName); builder.AppendLine("))]"); + } + /// + /// 发射程序集级 reflection fallback 元数据特性,供运行时补齐生成阶段无法精确表达的 handler。 + /// + /// 生成源码构造器。 + /// 需要写入特性的 handler 元数据名称。 + private static void AppendReflectionFallbackAttribute( + StringBuilder builder, + IReadOnlyList fallbackHandlerTypeMetadataNames) + { + builder.Append("[assembly: global::"); + builder.Append(CqrsRuntimeNamespace); + builder.Append(".CqrsReflectionFallbackAttribute("); + for (var index = 0; index < fallbackHandlerTypeMetadataNames.Count; index++) + { + if (index > 0) + builder.Append(", "); + + builder.Append('"'); + builder.Append(EscapeStringLiteral(fallbackHandlerTypeMetadataNames[index])); + builder.Append('"'); + } + + builder.AppendLine(")]"); + } + + /// + /// 发射生成注册器类型本体,包括 Register 方法和运行时反射辅助方法。 + /// + /// 生成源码构造器。 + /// 已排序的 handler 注册描述。 + /// 当前输出需要启用的结构分支。 + private static void AppendGeneratedRegistryType( + StringBuilder builder, + IReadOnlyList registrations, + GeneratedRegistrySourceShape sourceShape) + { builder.AppendLine(); builder.Append("namespace "); builder.Append(GeneratedNamespace); @@ -676,6 +830,28 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator builder.Append(CqrsRuntimeNamespace); builder.AppendLine(".ICqrsHandlerRegistry"); builder.AppendLine("{"); + AppendRegisterMethod(builder, registrations, sourceShape); + + if (sourceShape.HasExternalAssemblyTypeLookups) + { + builder.AppendLine(); + AppendReflectionHelpers(builder, sourceShape.HasExternalAssemblyTypeLookups); + } + + builder.AppendLine("}"); + } + + /// + /// 发射注册器的 Register 方法,保持直接注册和反射注册之间的原始稳定排序。 + /// + /// 生成源码构造器。 + /// 已排序的 handler 注册描述。 + /// 当前输出需要启用的结构分支。 + private static void AppendRegisterMethod( + StringBuilder builder, + IReadOnlyList registrations, + GeneratedRegistrySourceShape sourceShape) + { builder.Append( " public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::"); builder.Append(LoggingNamespace); @@ -685,9 +861,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(services));"); builder.AppendLine(" if (logger is null)"); builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(logger));"); - if (hasReflectedImplementationRegistrations || hasPreciseReflectedRegistrations || - registrations.Any(static registration => - !string.IsNullOrWhiteSpace(registration.ReflectionTypeMetadataName))) + if (sourceShape.RequiresRegistryAssemblyVariable) { builder.AppendLine(); builder.Append(" var registryAssembly = typeof(global::"); @@ -715,15 +889,6 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator } builder.AppendLine(" }"); - - if (hasExternalAssemblyTypeLookups) - { - builder.AppendLine(); - AppendReflectionHelpers(builder, hasExternalAssemblyTypeLookups); - } - - builder.AppendLine("}"); - return builder.ToString(); } private static void AppendDirectRegistrations( @@ -732,37 +897,108 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator { foreach (var directRegistration in registration.DirectRegistrations) { - builder.AppendLine( - " global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient("); - builder.AppendLine(" services,"); - builder.Append(" typeof("); - builder.Append(directRegistration.HandlerInterfaceDisplayName); - builder.AppendLine("),"); - builder.Append(" typeof("); - builder.Append(directRegistration.ImplementationTypeDisplayName); - builder.AppendLine("));"); - builder.Append(" logger.Debug(\"Registered CQRS handler "); - builder.Append(EscapeStringLiteral(directRegistration.ImplementationLogName)); - builder.Append(" as "); - builder.Append(EscapeStringLiteral(directRegistration.HandlerInterfaceLogName)); - builder.AppendLine(".\");"); + AppendServiceRegistration( + builder, + $"typeof({directRegistration.HandlerInterfaceDisplayName})", + $"typeof({directRegistration.ImplementationTypeDisplayName})", + " "); + AppendRegistrationLog( + builder, + directRegistration.ImplementationLogName, + directRegistration.HandlerInterfaceLogName, + " "); } } + /// + /// 发射 AddTransient 调用,调用方负责传入已经按当前分支解析好的 service 和 implementation 表达式。 + /// + /// 生成源码构造器。 + /// 生成代码中的服务类型表达式。 + /// 生成代码中的实现类型表达式。 + /// 当前生成语句的缩进。 + private static void AppendServiceRegistration( + StringBuilder builder, + string serviceTypeExpression, + string implementationTypeExpression, + string indent) + { + builder.Append(indent); + builder.AppendLine("global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient("); + builder.Append(indent); + builder.AppendLine(" services,"); + builder.Append(indent); + builder.Append(" "); + builder.Append(serviceTypeExpression); + builder.AppendLine(","); + builder.Append(indent); + builder.Append(" "); + builder.Append(implementationTypeExpression); + builder.AppendLine(");"); + } + + /// + /// 发射与注册语句配套的调试日志,保持所有生成注册路径的日志文本完全一致。 + /// + /// 生成源码构造器。 + /// 实现类型日志名。 + /// handler 接口日志名。 + /// 当前生成语句的缩进。 + private static void AppendRegistrationLog( + StringBuilder builder, + string implementationLogName, + string handlerInterfaceLogName, + string indent) + { + builder.Append(indent); + builder.Append("logger.Debug(\"Registered CQRS handler "); + builder.Append(EscapeStringLiteral(implementationLogName)); + builder.Append(" as "); + builder.Append(EscapeStringLiteral(handlerInterfaceLogName)); + builder.AppendLine(".\");"); + } + private static void AppendOrderedImplementationRegistrations( StringBuilder builder, ImplementationRegistrationSpec registration, int registrationIndex) { - var orderedRegistrations = - new List<(string HandlerInterfaceLogName, OrderedRegistrationKind Kind, int Index)>( - registration.DirectRegistrations.Length + - registration.ReflectedImplementationRegistrations.Length + - registration.PreciseReflectedRegistrations.Length); + var orderedRegistrations = CreateOrderedRegistrations(registration); + var implementationVariableName = $"implementationType{registrationIndex}"; + AppendImplementationTypeVariable(builder, registration, implementationVariableName); + builder.Append(" if ("); + builder.Append(implementationVariableName); + builder.AppendLine(" is not null)"); + builder.AppendLine(" {"); + + foreach (var orderedRegistration in orderedRegistrations) + { + AppendOrderedRegistration( + builder, + registration, + orderedRegistration, + registrationIndex, + implementationVariableName); + } + + builder.AppendLine(" }"); + } + + /// + /// 合并直接注册、实现类型反射注册和精确反射注册,并按 handler 接口日志名排序以保持生成输出稳定。 + /// + /// 单个实现类型聚合后的注册描述。 + /// 带有来源类型和原始索引的有序注册列表。 + private static List CreateOrderedRegistrations(ImplementationRegistrationSpec registration) + { + var orderedRegistrations = new List( + registration.DirectRegistrations.Length + + registration.ReflectedImplementationRegistrations.Length + + registration.PreciseReflectedRegistrations.Length); for (var directIndex = 0; directIndex < registration.DirectRegistrations.Length; directIndex++) { - orderedRegistrations.Add(( + orderedRegistrations.Add(new OrderedRegistrationSpec( registration.DirectRegistrations[directIndex].HandlerInterfaceLogName, OrderedRegistrationKind.Direct, directIndex)); @@ -772,7 +1008,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator reflectedIndex < registration.ReflectedImplementationRegistrations.Length; reflectedIndex++) { - orderedRegistrations.Add(( + orderedRegistrations.Add(new OrderedRegistrationSpec( registration.ReflectedImplementationRegistrations[reflectedIndex].HandlerInterfaceLogName, OrderedRegistrationKind.ReflectedImplementation, reflectedIndex)); @@ -782,7 +1018,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator preciseIndex < registration.PreciseReflectedRegistrations.Length; preciseIndex++) { - orderedRegistrations.Add(( + orderedRegistrations.Add(new OrderedRegistrationSpec( registration.PreciseReflectedRegistrations[preciseIndex].HandlerInterfaceLogName, OrderedRegistrationKind.PreciseReflected, preciseIndex)); @@ -790,8 +1026,20 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator orderedRegistrations.Sort(static (left, right) => StringComparer.Ordinal.Compare(left.HandlerInterfaceLogName, right.HandlerInterfaceLogName)); + return orderedRegistrations; + } - var implementationVariableName = $"implementationType{registrationIndex}"; + /// + /// 发射实现类型变量。公开类型直接使用 typeof,不可直接引用的实现类型则从当前程序集反射解析。 + /// + /// 生成源码构造器。 + /// 单个实现类型聚合后的注册描述。 + /// 生成代码中的实现类型变量名。 + private static void AppendImplementationTypeVariable( + StringBuilder builder, + ImplementationRegistrationSpec registration, + string implementationVariableName) + { if (string.IsNullOrWhiteSpace(registration.ReflectionTypeMetadataName)) { builder.Append(" var "); @@ -808,71 +1056,131 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator builder.Append(EscapeStringLiteral(registration.ReflectionTypeMetadataName!)); builder.AppendLine("\", throwOnError: false, ignoreCase: false);"); } + } - builder.Append(" if ("); - builder.Append(implementationVariableName); - builder.AppendLine(" is not null)"); - builder.AppendLine(" {"); - - foreach (var orderedRegistration in orderedRegistrations) + /// + /// 根据注册来源发射单条有序注册,确保混合直接和反射路径时仍按 handler 接口名稳定输出。 + /// + /// 生成源码构造器。 + /// 单个实现类型聚合后的注册描述。 + /// 带来源类型和原始索引的排序项。 + /// 实现类型在整体注册列表中的索引,用于生成稳定变量名。 + /// 生成代码中的实现类型变量名。 + private static void AppendOrderedRegistration( + StringBuilder builder, + ImplementationRegistrationSpec registration, + OrderedRegistrationSpec orderedRegistration, + int registrationIndex, + string implementationVariableName) + { + switch (orderedRegistration.Kind) { - switch (orderedRegistration.Kind) - { - case OrderedRegistrationKind.Direct: - var directRegistration = registration.DirectRegistrations[orderedRegistration.Index]; - builder.AppendLine( - " global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient("); - builder.AppendLine(" services,"); - builder.Append(" typeof("); - builder.Append(directRegistration.HandlerInterfaceDisplayName); - builder.AppendLine("),"); - builder.Append(" "); - builder.Append(implementationVariableName); - builder.AppendLine(");"); - builder.Append(" logger.Debug(\"Registered CQRS handler "); - builder.Append(EscapeStringLiteral(registration.ImplementationLogName)); - builder.Append(" as "); - builder.Append(EscapeStringLiteral(directRegistration.HandlerInterfaceLogName)); - builder.AppendLine(".\");"); - break; - case OrderedRegistrationKind.ReflectedImplementation: - var reflectedRegistration = - registration.ReflectedImplementationRegistrations[orderedRegistration.Index]; - builder.AppendLine( - " global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient("); - builder.AppendLine(" services,"); - builder.Append(" typeof("); - builder.Append(reflectedRegistration.HandlerInterfaceDisplayName); - builder.AppendLine("),"); - builder.Append(" "); - builder.Append(implementationVariableName); - builder.AppendLine(");"); - builder.Append(" logger.Debug(\"Registered CQRS handler "); - builder.Append(EscapeStringLiteral(registration.ImplementationLogName)); - builder.Append(" as "); - builder.Append(EscapeStringLiteral(reflectedRegistration.HandlerInterfaceLogName)); - builder.AppendLine(".\");"); - break; - case OrderedRegistrationKind.PreciseReflected: - var preciseRegistration = registration.PreciseReflectedRegistrations[orderedRegistration.Index]; - var registrationVariablePrefix = $"serviceType{registrationIndex}_{orderedRegistration.Index}"; - AppendPreciseReflectedTypeResolution( - builder, - preciseRegistration.ServiceTypeArguments, - registrationVariablePrefix, - implementationVariableName, - preciseRegistration.OpenHandlerTypeDisplayName, - registration.ImplementationLogName, - preciseRegistration.HandlerInterfaceLogName, - 3); - break; - default: - throw new InvalidOperationException( - $"Unsupported ordered CQRS registration kind {orderedRegistration.Kind}."); - } + case OrderedRegistrationKind.Direct: + AppendOrderedDirectRegistration( + builder, + registration, + registration.DirectRegistrations[orderedRegistration.Index], + implementationVariableName); + break; + case OrderedRegistrationKind.ReflectedImplementation: + AppendOrderedReflectedImplementationRegistration( + builder, + registration, + registration.ReflectedImplementationRegistrations[orderedRegistration.Index], + implementationVariableName); + break; + case OrderedRegistrationKind.PreciseReflected: + AppendOrderedPreciseReflectedRegistration( + builder, + registration, + registration.PreciseReflectedRegistrations[orderedRegistration.Index], + registrationIndex, + orderedRegistration.Index, + implementationVariableName); + break; + default: + throw new InvalidOperationException( + $"Unsupported ordered CQRS registration kind {orderedRegistration.Kind}."); } + } - builder.AppendLine(" }"); + /// + /// 发射实现类型已通过变量解析、handler 接口可直接引用的直接注册语句。 + /// + /// 生成源码构造器。 + /// 单个实现类型聚合后的注册描述。 + /// 当前直接注册项。 + /// 生成代码中的实现类型变量名。 + private static void AppendOrderedDirectRegistration( + StringBuilder builder, + ImplementationRegistrationSpec registration, + HandlerRegistrationSpec directRegistration, + string implementationVariableName) + { + AppendServiceRegistration( + builder, + $"typeof({directRegistration.HandlerInterfaceDisplayName})", + implementationVariableName, + " "); + AppendRegistrationLog( + builder, + registration.ImplementationLogName, + directRegistration.HandlerInterfaceLogName, + " "); + } + + /// + /// 发射实现类型需要反射解析、handler 接口可直接引用的注册语句。 + /// + /// 生成源码构造器。 + /// 单个实现类型聚合后的注册描述。 + /// 当前实现类型反射注册项。 + /// 生成代码中的实现类型变量名。 + private static void AppendOrderedReflectedImplementationRegistration( + StringBuilder builder, + ImplementationRegistrationSpec registration, + ReflectedImplementationRegistrationSpec reflectedRegistration, + string implementationVariableName) + { + AppendServiceRegistration( + builder, + $"typeof({reflectedRegistration.HandlerInterfaceDisplayName})", + implementationVariableName, + " "); + AppendRegistrationLog( + builder, + registration.ImplementationLogName, + reflectedRegistration.HandlerInterfaceLogName, + " "); + } + + /// + /// 发射 handler 接口需要运行时精确构造的注册语句。 + /// + /// 生成源码构造器。 + /// 单个实现类型聚合后的注册描述。 + /// 当前精确反射注册项。 + /// 实现类型在整体注册列表中的索引。 + /// 当前注册项在原始精确反射注册集合中的索引。 + /// 生成代码中的实现类型变量名。 + private static void AppendOrderedPreciseReflectedRegistration( + StringBuilder builder, + ImplementationRegistrationSpec registration, + PreciseReflectedRegistrationSpec preciseRegistration, + int registrationIndex, + int orderedRegistrationIndex, + string implementationVariableName) + { + var registrationVariablePrefix = $"serviceType{registrationIndex}_{orderedRegistrationIndex}"; + AppendPreciseReflectedTypeResolution( + builder, + preciseRegistration.ServiceTypeArguments, + registrationVariablePrefix, + implementationVariableName, + preciseRegistration.OpenHandlerTypeDisplayName, + registration.ImplementationLogName, + preciseRegistration.HandlerInterfaceLogName, + 3); } private static void AppendPreciseReflectedTypeResolution( @@ -886,10 +1194,50 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator int indentLevel) { var indent = new string(' ', indentLevel * 4); - var nestedIndent = new string(' ', (indentLevel + 1) * 4); - var resolvedArgumentNames = new string[serviceTypeArguments.Length]; var reflectedArgumentNames = new List(); + var resolvedArgumentNames = AppendServiceTypeArgumentResolutions( + builder, + serviceTypeArguments, + registrationVariablePrefix, + reflectedArgumentNames, + indent); + if (reflectedArgumentNames.Count > 0) + indent = AppendReflectedArgumentGuardStart(builder, reflectedArgumentNames, indent); + + AppendClosedGenericServiceTypeCreation( + builder, + registrationVariablePrefix, + openHandlerTypeDisplayName, + resolvedArgumentNames, + indent); + AppendServiceRegistration(builder, registrationVariablePrefix, implementationVariableName, indent); + AppendRegistrationLog(builder, implementationLogName, handlerInterfaceLogName, indent); + + if (reflectedArgumentNames.Count > 0) + { + builder.Append(new string(' ', indentLevel * 4)); + builder.AppendLine("}"); + } + } + + /// + /// 递归发射每个 handler 泛型实参的运行时类型解析表达式。 + /// + /// 生成源码构造器。 + /// handler 服务类型的运行时泛型实参描述。 + /// 当前注册项的稳定变量名前缀。 + /// 需要空值检查的反射解析变量集合。 + /// 当前生成语句的缩进。 + /// 可传给 MakeGenericType 的实参表达式。 + private static string[] AppendServiceTypeArgumentResolutions( + StringBuilder builder, + ImmutableArray serviceTypeArguments, + string registrationVariablePrefix, + ICollection reflectedArgumentNames, + string indent) + { + var resolvedArgumentNames = new string[serviceTypeArguments.Length]; for (var argumentIndex = 0; argumentIndex < serviceTypeArguments.Length; argumentIndex++) { resolvedArgumentNames[argumentIndex] = AppendRuntimeTypeReferenceResolution( @@ -900,32 +1248,60 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator indent); } - if (reflectedArgumentNames.Count > 0) + return resolvedArgumentNames; + } + + /// + /// 为运行时反射解析出的泛型实参发射空值保护块,避免生成注册器注册无法完整构造的服务类型。 + /// + /// 生成源码构造器。 + /// 需要参与空值检查的变量名。 + /// 保护块开始前的缩进。 + /// 保护块内部应使用的下一层缩进。 + private static string AppendReflectedArgumentGuardStart( + StringBuilder builder, + IReadOnlyList reflectedArgumentNames, + string indent) + { + builder.Append(indent); + builder.Append("if ("); + for (var index = 0; index < reflectedArgumentNames.Count; index++) { - builder.Append(indent); - builder.Append("if ("); - for (var index = 0; index < reflectedArgumentNames.Count; index++) - { - if (index > 0) - builder.Append(" && "); + if (index > 0) + builder.Append(" && "); - builder.Append(reflectedArgumentNames[index]); - builder.Append(" is not null"); - } - - builder.AppendLine(")"); - builder.Append(indent); - builder.AppendLine("{"); - indent = nestedIndent; + builder.Append(reflectedArgumentNames[index]); + builder.Append(" is not null"); } + builder.AppendLine(")"); + builder.Append(indent); + builder.AppendLine("{"); + return $"{indent} "; + } + + /// + /// 发射关闭 handler 服务类型的 MakeGenericType 构造语句。 + /// + /// 生成源码构造器。 + /// 生成代码中的服务类型变量名。 + /// 开放 handler 接口类型显示名。 + /// 已解析的泛型实参表达式。 + /// 当前生成语句的缩进。 + private static void AppendClosedGenericServiceTypeCreation( + StringBuilder builder, + string registrationVariablePrefix, + string openHandlerTypeDisplayName, + IReadOnlyList resolvedArgumentNames, + string indent) + { builder.Append(indent); builder.Append("var "); builder.Append(registrationVariablePrefix); builder.Append(" = typeof("); builder.Append(openHandlerTypeDisplayName); builder.Append(").MakeGenericType("); - for (var index = 0; index < resolvedArgumentNames.Length; index++) + for (var index = 0; index < resolvedArgumentNames.Count; index++) { if (index > 0) builder.Append(", "); @@ -934,31 +1310,6 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator } builder.AppendLine(");"); - builder.Append(indent); - builder.AppendLine( - "global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient("); - builder.Append(indent); - builder.AppendLine(" services,"); - builder.Append(indent); - builder.Append(" "); - builder.Append(registrationVariablePrefix); - builder.AppendLine(","); - builder.Append(indent); - builder.Append(" "); - builder.Append(implementationVariableName); - builder.AppendLine(");"); - builder.Append(indent); - builder.Append("logger.Debug(\"Registered CQRS handler "); - builder.Append(EscapeStringLiteral(implementationLogName)); - builder.Append(" as "); - builder.Append(EscapeStringLiteral(handlerInterfaceLogName)); - builder.AppendLine(".\");"); - - if (reflectedArgumentNames.Count > 0) - { - builder.Append(new string(' ', indentLevel * 4)); - builder.AppendLine("}"); - } } private static string AppendRuntimeTypeReferenceResolution( @@ -972,57 +1323,145 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator return $"typeof({runtimeTypeReference.TypeDisplayName})"; if (runtimeTypeReference.ArrayElementTypeReference is not null) - { - var elementExpression = AppendRuntimeTypeReferenceResolution( + return AppendArrayRuntimeTypeReferenceResolution( builder, - runtimeTypeReference.ArrayElementTypeReference, - $"{variableBaseName}Element", + runtimeTypeReference, + variableBaseName, reflectedArgumentNames, indent); - return runtimeTypeReference.ArrayRank == 1 - ? $"{elementExpression}.MakeArrayType()" - : $"{elementExpression}.MakeArrayType({runtimeTypeReference.ArrayRank})"; - } - if (runtimeTypeReference.PointerElementTypeReference is not null) - { - var pointedAtExpression = AppendRuntimeTypeReferenceResolution( + return AppendPointerRuntimeTypeReferenceResolution( builder, - runtimeTypeReference.PointerElementTypeReference, - $"{variableBaseName}PointedAt", + runtimeTypeReference, + variableBaseName, reflectedArgumentNames, indent); - return $"{pointedAtExpression}.MakePointerType()"; - } - if (runtimeTypeReference.GenericTypeDefinitionReference is not null) - { - var genericTypeDefinitionExpression = AppendRuntimeTypeReferenceResolution( + return AppendConstructedGenericRuntimeTypeReferenceResolution( builder, - runtimeTypeReference.GenericTypeDefinitionReference, - $"{variableBaseName}GenericDefinition", + runtimeTypeReference, + variableBaseName, reflectedArgumentNames, indent); - var genericArgumentExpressions = new string[runtimeTypeReference.GenericTypeArguments.Length]; - for (var argumentIndex = 0; - argumentIndex < runtimeTypeReference.GenericTypeArguments.Length; - argumentIndex++) - { - genericArgumentExpressions[argumentIndex] = AppendRuntimeTypeReferenceResolution( - builder, - runtimeTypeReference.GenericTypeArguments[argumentIndex], - $"{variableBaseName}GenericArgument{argumentIndex}", - reflectedArgumentNames, - indent); - } - return - $"{genericTypeDefinitionExpression}.MakeGenericType({string.Join(", ", genericArgumentExpressions)})"; + return AppendReflectionRuntimeTypeReferenceResolution( + builder, + runtimeTypeReference, + variableBaseName, + reflectedArgumentNames, + indent); + } + + /// + /// 发射数组类型引用的运行时重建表达式。 + /// + /// 生成源码构造器。 + /// 数组类型引用描述。 + /// 用于递归生成变量名的稳定前缀。 + /// 需要空值检查的反射解析变量集合。 + /// 当前生成语句的缩进。 + /// 数组类型表达式。 + private static string AppendArrayRuntimeTypeReferenceResolution( + StringBuilder builder, + RuntimeTypeReferenceSpec runtimeTypeReference, + string variableBaseName, + ICollection reflectedArgumentNames, + string indent) + { + var elementExpression = AppendRuntimeTypeReferenceResolution( + builder, + runtimeTypeReference.ArrayElementTypeReference!, + $"{variableBaseName}Element", + reflectedArgumentNames, + indent); + + return runtimeTypeReference.ArrayRank == 1 + ? $"{elementExpression}.MakeArrayType()" + : $"{elementExpression}.MakeArrayType({runtimeTypeReference.ArrayRank})"; + } + + /// + /// 发射指针类型引用的运行时重建表达式。 + /// + /// 生成源码构造器。 + /// 指针类型引用描述。 + /// 用于递归生成变量名的稳定前缀。 + /// 需要空值检查的反射解析变量集合。 + /// 当前生成语句的缩进。 + /// 指针类型表达式。 + private static string AppendPointerRuntimeTypeReferenceResolution( + StringBuilder builder, + RuntimeTypeReferenceSpec runtimeTypeReference, + string variableBaseName, + ICollection reflectedArgumentNames, + string indent) + { + var pointedAtExpression = AppendRuntimeTypeReferenceResolution( + builder, + runtimeTypeReference.PointerElementTypeReference!, + $"{variableBaseName}PointedAt", + reflectedArgumentNames, + indent); + + return $"{pointedAtExpression}.MakePointerType()"; + } + + /// + /// 发射已构造泛型类型引用的运行时重建表达式。 + /// + /// 生成源码构造器。 + /// 已构造泛型类型引用描述。 + /// 用于递归生成变量名的稳定前缀。 + /// 需要空值检查的反射解析变量集合。 + /// 当前生成语句的缩进。 + /// 已构造泛型类型表达式。 + private static string AppendConstructedGenericRuntimeTypeReferenceResolution( + StringBuilder builder, + RuntimeTypeReferenceSpec runtimeTypeReference, + string variableBaseName, + ICollection reflectedArgumentNames, + string indent) + { + var genericTypeDefinitionExpression = AppendRuntimeTypeReferenceResolution( + builder, + runtimeTypeReference.GenericTypeDefinitionReference!, + $"{variableBaseName}GenericDefinition", + reflectedArgumentNames, + indent); + var genericArgumentExpressions = new string[runtimeTypeReference.GenericTypeArguments.Length]; + for (var argumentIndex = 0; + argumentIndex < runtimeTypeReference.GenericTypeArguments.Length; + argumentIndex++) + { + genericArgumentExpressions[argumentIndex] = AppendRuntimeTypeReferenceResolution( + builder, + runtimeTypeReference.GenericTypeArguments[argumentIndex], + $"{variableBaseName}GenericArgument{argumentIndex}", + reflectedArgumentNames, + indent); } - var reflectionTypeMetadataName = runtimeTypeReference.ReflectionTypeMetadataName!; + return $"{genericTypeDefinitionExpression}.MakeGenericType({string.Join(", ", genericArgumentExpressions)})"; + } + + /// + /// 发射命名类型的运行时反射查找语句,并返回后续服务类型构造应引用的变量名。 + /// + /// 生成源码构造器。 + /// 反射查找类型引用描述。 + /// 生成代码中的反射变量名。 + /// 需要空值检查的反射解析变量集合。 + /// 当前生成语句的缩进。 + /// 生成代码中的反射变量名。 + private static string AppendReflectionRuntimeTypeReferenceResolution( + StringBuilder builder, + RuntimeTypeReferenceSpec runtimeTypeReference, + string variableBaseName, + ICollection reflectedArgumentNames, + string indent) + { reflectedArgumentNames.Add(variableBaseName); builder.Append(indent); builder.Append("var "); @@ -1030,7 +1469,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator if (string.IsNullOrWhiteSpace(runtimeTypeReference.ReflectionAssemblyName)) { builder.Append(" = registryAssembly.GetType(\""); - builder.Append(EscapeStringLiteral(reflectionTypeMetadataName)); + builder.Append(EscapeStringLiteral(runtimeTypeReference.ReflectionTypeMetadataName!)); builder.AppendLine("\", throwOnError: false, ignoreCase: false);"); } else @@ -1038,7 +1477,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator builder.Append(" = ResolveReferencedAssemblyType(\""); builder.Append(EscapeStringLiteral(runtimeTypeReference.ReflectionAssemblyName!)); builder.Append("\", \""); - builder.Append(EscapeStringLiteral(reflectionTypeMetadataName)); + builder.Append(EscapeStringLiteral(runtimeTypeReference.ReflectionTypeMetadataName!)); builder.AppendLine("\");"); } @@ -1144,6 +1583,23 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator string HandlerInterfaceDisplayName, string HandlerInterfaceLogName); + private readonly record struct OrderedRegistrationSpec( + string HandlerInterfaceLogName, + OrderedRegistrationKind Kind, + int Index); + + private readonly record struct GeneratedRegistrySourceShape( + bool HasReflectedImplementationRegistrations, + bool HasPreciseReflectedRegistrations, + bool HasReflectionTypeLookups, + bool HasExternalAssemblyTypeLookups) + { + public bool RequiresRegistryAssemblyVariable => + HasReflectedImplementationRegistrations || + HasPreciseReflectedRegistrations || + HasReflectionTypeLookups; + } + /// /// 标记某条 handler 注册语句在生成阶段采用的表达策略。 /// 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 ffe8d6ec..b13e0e74 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,22 +7,25 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-017` -- 当前阶段:`Phase 17` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-018` +- 当前阶段:`Phase 18` - 当前焦点: - 已完成 `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` 结构拆分,生成输出保持不变 - `LoggingConfiguration`、`FilterConfiguration` 与 `CollectionExtensions` 已改用集合抽象接口,并保留内部具体集合默认值 - `CoroutineScheduler` 的 tag/group 字典已显式使用 `StringComparer.Ordinal`,保持既有区分大小写语义 - `EasyEvents.AddEvent()` 的重复注册路径已改为状态冲突异常,避免把泛型类型参数伪装成方法参数名 - `Option` 已声明 `IEquatable>`,与已有强类型 `Equals(Option)` 契约对齐 - 当前 `GFramework.Core` `net8.0` warnings-only 基线已降到 `0` 条 - 当前 `GFramework.Core.SourceGenerators` warnings-only 基线已降到 `0` 条 + - 当前 `GFramework.Cqrs.SourceGenerators` warnings-only 基线已降到 `0` 条 - `GFramework.Godot` 的 `Timing.cs` 已同步适配新事件签名,但当前 worktree 的 Godot restore 资产仍受 Windows fallback package folder 干扰,独立 build 需在修复资产后补跑 - 后续继续按 warning 类型和数量批处理,而不是回退到按单文件切片推进 - - 下一轮默认继续评估跨 target 的 `MA0158` 锁替换风险,或转向其他 source generator / test warning 热点 + - 下一轮默认转向 `GFramework.Game.SourceGenerators` 的 `MA0006` / `MA0051` 热点,或继续评估跨 target 的 `MA0158` + 锁替换风险 - 单次 `boot` 的工作树改动上限控制在约 `100` 个文件以内,避免 recovery context 与 review 面同时失控 - 若任务边界互不冲突,允许使用不同模型的 subagent 并行处理不同 warning 类型或不同目录,但必须遵守显式 ownership @@ -41,6 +44,7 @@ 等待满默认超时并返回 `false` 的竞态,并通过整包 `GFramework.Core.Tests` 重新验证 - 已完成当前 `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` 条 ## 当前活跃事实 @@ -75,6 +79,8 @@ warnings-only build 与 focused tests 验证配置反序列化、集合扩展、事件重复注册、`Option` 相等性和协程 tag/group 语义 - `RP-017` 复核 `MA0158` 当前仍是跨 target 锁类型迁移问题,因此先收口单点 `ContextAwareGenerator` `MA0051`, 并通过 source generator 项目 build 与 `ContextAwareGeneratorSnapshotTests` 验证生成输出未回归 +- `RP-018` 暂缓跨 target `MA0158`,转入 `GFramework.Cqrs.SourceGenerators` 的单文件结构性 warning; + 通过拆分 handler 分析、运行时类型引用构造、注册器源码发射与精确反射注册输出阶段,清空该项目当前 `MA0051` - 当前工作树分支 `fix/analyzer-warning-reduction-batch` 已在 `ai-plan/public/README.md` 建立 topic 映射 ## 当前风险 @@ -88,8 +94,8 @@ - net10 专属 warning 风险:`MA0158` 建议使用 `System.Threading.Lock`,但项目多 target 时需要确认兼容边界 - 缓解措施:下一轮先按 target framework 与 API 可用性评估,不直接批量替换共享源码中的 `object` lock - source generator warning 外溢风险:运行 `GFramework.SourceGenerators.Tests` 会构建相邻 generator/test 项目并显示既有 - `GFramework.Cqrs.SourceGenerators`、`GFramework.Game.SourceGenerators` 与测试项目 warning - - 缓解措施:本轮以 `GFramework.Core.SourceGenerators` 独立 warnings-only build 作为主验收,并用 focused snapshot test + `GFramework.Game.SourceGenerators` 与测试项目 warning + - 缓解措施:本轮以 `GFramework.Cqrs.SourceGenerators` 独立 warnings-only build 作为主验收,并用 focused generator test 验证行为;后续若处理相邻 generator warning,应另开明确切片 - Godot 资产文件环境风险:当前 worktree 的 `GFramework.Godot` restore/build 仍会命中 Windows fallback package folder - 缓解措施:后续若继续触达 Godot 模块,先用 Linux 侧 restore 资产或 Windows-hosted 构建链刷新该项目,再补跑定向 build @@ -185,13 +191,21 @@ - 结果:`0 Warning(s)`,`0 Error(s)`;`ContextAwareGenerator.cs` 已不再出现 `MA0051` - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter "FullyQualifiedName~ContextAwareGeneratorSnapshotTests" -m:1 -p:RestoreFallbackFolders="" -nologo` - 结果:`1 Passed`,`0 Failed`;测试构建仍显示相邻 source generator 和测试项目的既有 analyzer warning +- `RP-018` 的验证结果: + - `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release -t:Rebuild --no-restore -p:UseSharedCompilation=false -p:RestoreFallbackFolders= -nologo -clp:"Summary;WarningsOnly"` + - 结果:`0 Warning(s)`,`0 Error(s)`;`CqrsHandlerRegistryGenerator.cs` 当前 `MA0051` 已清零 + - `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore --filter FullyQualifiedName~CqrsHandlerRegistryGeneratorTests -m:1 -p:RestoreFallbackFolders= -nologo` + - 结果:`14 Passed`,`0 Failed` + - 说明:该 test project 构建仍显示 `GFramework.Game.SourceGenerators` 与测试项目中的既有 analyzer warning;本轮关注的 + `GFramework.Cqrs.SourceGenerators` 独立 build 已清零 - active 跟踪文件只保留当前恢复点、活跃事实、风险与下一步,不再重复保存已完成阶段的长篇历史 ## 下一步 1. 若要继续该主题,先读 active tracking,再按需展开历史归档中的 warning 热点与验证记录 -2. 下一轮优先评估 `net10.0` 下的 `MA0158` 是否能通过条件编译或目标框架特定源码安全推进 -3. 若暂不推进 `MA0158`,可转入 `GFramework.Cqrs.SourceGenerators` 或 `GFramework.Game.SourceGenerators` 的剩余 - `MA0051` / `MA0006` 热点,但应单独建立文件 ownership 和验证范围 +2. 下一轮优先转入 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0006` 低风险批次,再评估是否继续拆分 + 该文件的 `MA0051` +3. 若改回推进 `MA0158`,先设计 `net8.0` / `net9.0` / `net10.0` 多 target 条件编译方案,不直接批量替换共享源码中的 + `object` lock 4. 若后续继续改动 `GFramework.Godot`,先修复该项目的 Linux 侧 restore 资产,再补跑独立 build 5. 若本主题确认暂缓,可保持当前归档状态,不需要再恢复 `local-plan/` 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 ae5fa38d..b567a7e5 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,31 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-22 — RP-018 + +### 阶段:`CqrsHandlerRegistryGenerator` 剩余 `MA0051` 收口(RP-018) + +- 启动复核: + - 当前 worktree 仍映射到 `analyzer-warning-reduction` active topic + - `MA0158` 锁迁移仍然跨 `GFramework.Core` / `GFramework.Cqrs` 多 target 共享源码,继续视为需要单独设计的兼容性问题 + - `GFramework.Cqrs.SourceGenerators` warnings-only build 复现 `CqrsHandlerRegistryGenerator.cs` 的 `6` 个 `MA0051` +- 决策: + - 本轮暂缓 `MA0158`,转入单文件、可由生成器测试覆盖的 `GFramework.Cqrs.SourceGenerators` 结构拆分 + - 未使用 subagent;critical path 是本地复现 warning、拆分源码发射流程并用 focused generator tests 验证输出未变 +- 实施调整: + - 将 handler candidate 分析拆为接口收集、候选构造和单接口注册分类阶段 + - 将运行时类型引用构造拆为已构造泛型、命名类型反射查找等独立 helper + - 将注册器源码生成拆为文件头、程序集特性、注册器类型、`Register` 方法和服务注册日志发射 helper + - 将有序注册与精确反射注册输出拆为独立阶段,保留原有排序和生成文本形状 +- 验证结果: + - `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.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~CqrsHandlerRegistryGeneratorTests -m:1 -p:RestoreFallbackFolders= -nologo` + - 结果:`14 Passed`,`0 Failed` + - 说明:测试项目构建仍显示 `GFramework.Game.SourceGenerators` 与测试项目中的既有 analyzer warning;不属于本轮写集 +- 下一步建议: + - 继续该主题时,优先处理 `GFramework.Game.SourceGenerators/Config/SchemaConfigGenerator.cs` 的 `MA0006` 低风险批次 + - 若回到 `MA0158`,先设计多 target 条件编译方案,再考虑替换共享源码中的 `object` lock + ## 2026-04-22 — RP-017 ### 阶段:`ContextAwareGenerator` 剩余 `MA0051` 收口(RP-017)