From 414e49c4139220cb85b1de5914e4c13e6e1342a1 Mon Sep 17 00:00:00 2001 From: GwWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 28 Dec 2025 16:08:24 +0800 Subject: [PATCH] =?UTF-8?q?refactor(generators):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=BA=90=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=E5=9F=BA?= =?UTF-8?q?=E7=B1=BB=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 AttributeClassGeneratorBase 抽象基类拆分为 MetadataAttributeClassGeneratorBase 和 TypeAttributeClassGeneratorBase - 为 GodotLoggerGenerator 实现 TypeAttributeClassGeneratorBase 基类 - 为 EnumExtensionsGenerator 实现 MetadataAttributeClassGeneratorBase 基类 - 为 LoggerGenerator 实现 TypeAttributeClassGeneratorBase 基类 - 为 ContextAwareGenerator 实现 MetadataAttributeClassGeneratorBase 基类 - 添加 ContextAwareGenerator 中对 IContextAware 接口实现的验证逻辑 - 简化 AttributeClassGeneratorBase 中的语法提供程序实现 - 移除 AttributeClassGeneratorBase 中的异常处理和错误输出逻辑 - 优化属性解析机制,使用元数据名称或类型进行特性查找 --- .../logging/GodotLoggerGenerator.cs | 104 ++++------------- .../generator/AttributeClassGeneratorBase.cs | 110 ++++++------------ .../MetadataAttributeClassGeneratorBase.cs | 41 +++++++ .../TypeAttributeClassGeneratorBase.cs | 35 ++++++ .../enums/EnumExtensionsGenerator.cs | 14 +-- .../logging/LoggerGenerator.cs | 5 +- .../rule/ContextAwareGenerator.cs | 23 +++- 7 files changed, 155 insertions(+), 177 deletions(-) create mode 100644 GFramework.SourceGenerators.Common/generator/MetadataAttributeClassGeneratorBase.cs create mode 100644 GFramework.SourceGenerators.Common/generator/TypeAttributeClassGeneratorBase.cs diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs index 7125a6f..78480da 100644 --- a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs +++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs @@ -1,12 +1,10 @@ using System; using System.Linq; using System.Text; +using GFramework.Godot.SourceGenerators.Abstractions.logging; using GFramework.SourceGenerators.Common.constants; -using GFramework.SourceGenerators.Common.diagnostics; +using GFramework.SourceGenerators.Common.generator; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace GFramework.Godot.SourceGenerators.logging; @@ -14,95 +12,25 @@ namespace GFramework.Godot.SourceGenerators.logging; /// 日志生成器,用于为标记了GodotLogAttribute的类自动生成日志字段 /// [Generator] -public sealed class GodotLoggerGenerator : IIncrementalGenerator +public sealed class GodotLoggerGenerator : TypeAttributeClassGeneratorBase { - private const string AttributeMetadataName = - $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.logging.GodotLogAttribute"; + protected override Type AttributeType => typeof(GodotLogAttribute); - private const string AttributeShortNameWithoutSuffix = "GodotLog"; - /// - /// 初始化源生成器,设置语法提供程序和源输出注册 - /// - /// 增量生成器初始化上下文 - public void Initialize(IncrementalGeneratorInitializationContext context) - { - // 创建语法提供程序,用于查找标记了GodotLogAttribute的类声明 - var targets = context.SyntaxProvider - .CreateSyntaxProvider( - static (node, _) => - { - if (node is not ClassDeclarationSyntax cls) return false; - return cls.AttributeLists - .SelectMany(a => a.Attributes) - .Any(a => a.Name.ToString().Contains(AttributeShortNameWithoutSuffix)); - }, - static (ctx, _) => - { - var cls = (ClassDeclarationSyntax)ctx.Node; - var sym = ctx.SemanticModel.GetDeclaredSymbol(cls); - return (ClassDecl: cls, Symbol: sym); - }) - .Where(x => x.Symbol is not null); - - // 注册源输出,为符合条件的类生成日志字段代码 - context.RegisterSourceOutput(targets, (spc, pair) => - { - try - { - var classDecl = pair.ClassDecl; - var classSymbol = pair.Symbol!; - - var attr = GetAttribute(classSymbol); - if (attr is null) return; - - if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) - { - spc.ReportDiagnostic(Diagnostic.Create( - CommonDiagnostics.ClassMustBePartial, - classDecl.Identifier.GetLocation(), - classSymbol.Name)); - return; - } - - var source = Generate(classSymbol, attr); - spc.AddSource($"{classSymbol.Name}.Logger.g.cs", SourceText.From(source, Encoding.UTF8)); - } - catch (Exception ex) - { - var safeName = pair.Symbol?.Name ?? "Unknown"; - var errorSource = $"// Source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}"; - spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8)); - } - }); - } - - /// - /// 获取类符号上的GodotLogAttribute属性数据 - /// - /// 类符号 - /// GodotLogAttribute属性数据,如果未找到则返回null - private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol) - => classSymbol.GetAttributes().FirstOrDefault(a => - { - var cls = a.AttributeClass; - return cls != null && - (cls.ToDisplayString() == AttributeMetadataName || - cls.Name.StartsWith(AttributeShortNameWithoutSuffix)); - }); + protected override string AttributeShortNameWithoutSuffix => "GodotLog"; /// /// 生成日志字段的源代码 /// - /// 类符号 + /// 类符号 /// GodotLogAttribute属性数据 /// 生成的源代码字符串 - private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr) + protected override string Generate(INamedTypeSymbol symbol, AttributeData attr) { - var ns = classSymbol.ContainingNamespace.IsGlobalNamespace + var ns = symbol.ContainingNamespace.IsGlobalNamespace ? null - : classSymbol.ContainingNamespace.ToDisplayString(); - var className = classSymbol.Name; + : symbol.ContainingNamespace.ToDisplayString(); + var className = symbol.Name; // 解析构造函数参数 var name = className; @@ -144,11 +72,19 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator } /// - /// 从属性数据中获取指定名称的命名参数值 + /// 获取生成文件的提示名称 + /// + /// 类型符号 + /// 生成文件的提示名称 + protected override string GetHintName(INamedTypeSymbol symbol) + => $"{symbol.Name}.Logger.g.cs"; + + /// + /// 获取属性的命名参数值 /// /// 属性数据 /// 参数名称 - /// 参数值,如果未找到则返回null + /// 参数值 private static object? GetNamedArg(AttributeData attr, string name) => attr.NamedArguments.FirstOrDefault(kv => kv.Key == name).Value.Value; } \ No newline at end of file diff --git a/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs index 6170e45..28d972f 100644 --- a/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs +++ b/GFramework.SourceGenerators.Common/generator/AttributeClassGeneratorBase.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using GFramework.SourceGenerators.Common.diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -13,13 +12,7 @@ namespace GFramework.SourceGenerators.Common.generator; public abstract class AttributeClassGeneratorBase : IIncrementalGenerator { /// - /// 获取属性的元数据名称 - /// - protected abstract Type AttributeType { get; } - - /// - /// Attribute 的短名称(不含 Attribute 后缀) - /// 仅用于 Syntax 层宽松匹配 + /// 获取属性的短名称(不包含后缀) /// protected abstract string AttributeShortNameWithoutSuffix { get; } @@ -29,66 +22,70 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator /// 增量生成器初始化上下文 public void Initialize(IncrementalGeneratorInitializationContext context) { - var targets = context.SyntaxProvider.CreateSyntaxProvider( + // 创建语法提供程序,查找带有指定属性的类声明 + var candidates = context.SyntaxProvider.CreateSyntaxProvider( (node, _) => node is ClassDeclarationSyntax cls && cls.AttributeLists .SelectMany(a => a.Attributes) .Any(a => a.Name.ToString() .Contains(AttributeShortNameWithoutSuffix)), - static (ctx, t) => + (ctx, _) => { var cls = (ClassDeclarationSyntax)ctx.Node; - var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls, t); - return (ClassDecl: cls, Symbol: symbol); - } - ) - .Where(x => x.Symbol is not null); + var symbol = ctx.SemanticModel.GetDeclaredSymbol(cls); + return (cls, symbol); + }) + .Where(x => x.symbol is not null); - context.RegisterSourceOutput(targets, (spc, pair) => + var combined = candidates.Combine(context.CompilationProvider); + + context.RegisterSourceOutput(combined, (spc, pair) => { - try - { - Execute(spc, pair.ClassDecl, pair.Symbol!); - } - catch (Exception ex) - { - EmitError(spc, pair.Symbol, ex); - } + var ((cls, symbol), compilation) = pair; + Execute(spc, compilation, cls, symbol!); }); } /// - /// 执行源代码生成的主要逻辑 + /// 解析指定符号上的属性数据 + /// + /// 编译对象 + /// 命名类型符号 + /// 属性数据,如果未找到则返回null + protected abstract AttributeData? ResolveAttribute( + Compilation compilation, + INamedTypeSymbol symbol); + + /// + /// 执行源代码生成 /// /// 源生产上下文 + /// 编译对象 /// 类声明语法节点 /// 命名类型符号 private void Execute( SourceProductionContext context, + Compilation compilation, ClassDeclarationSyntax classDecl, INamedTypeSymbol symbol) { - var attr = GetAttribute(symbol); - if (attr == null) return; + var attr = ResolveAttribute(compilation, symbol); + if (attr is null) + return; - // partial 校验 if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword)) { ReportClassMustBePartial(context, classDecl, symbol); return; } - // 子类校验 if (!ValidateSymbol(context, classDecl, symbol, attr)) return; - var source = Generate(symbol, attr); - context.AddSource(GetHintName(symbol), source); + context.AddSource(GetHintName(symbol), Generate(symbol, attr)); } - #region 可覆写点 - /// /// 验证符号的有效性 /// @@ -101,10 +98,7 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator SourceProductionContext context, ClassDeclarationSyntax syntax, INamedTypeSymbol symbol, - AttributeData attr) - { - return true; - } + AttributeData attr) => true; /// /// 生成源代码 @@ -122,27 +116,10 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator /// 命名类型符号 /// 生成文件的提示名称 protected virtual string GetHintName(INamedTypeSymbol symbol) - { - return $"{symbol.Name}.g.cs"; - } - - #endregion - - #region Attribute / Diagnostic + => $"{symbol.Name}.g.cs"; /// - /// 获取指定符号的属性数据 - /// - /// 命名类型符号 - /// 属性数据,如果未找到则返回null - protected virtual AttributeData? GetAttribute(INamedTypeSymbol symbol) - { - return symbol.GetAttributes().FirstOrDefault(a => - string.Equals(a.AttributeClass?.ToDisplayString(), AttributeType.FullName, StringComparison.Ordinal)); - } - - /// - /// 报告类必须是partial的诊断信息 + /// 报告类必须是部分类的错误 /// /// 源生产上下文 /// 类声明语法节点 @@ -157,23 +134,4 @@ public abstract class AttributeClassGeneratorBase : IIncrementalGenerator syntax.Identifier.GetLocation(), symbol.Name)); } - - /// - /// 发出错误信息 - /// - /// 源生产上下文 - /// 命名类型符号 - /// 异常对象 - protected virtual void EmitError( - SourceProductionContext context, - INamedTypeSymbol? symbol, - Exception ex) - { - var name = symbol?.Name ?? "Unknown"; - var text = - $"// source generator error: {ex.Message}\n// {ex.StackTrace}"; - context.AddSource($"{name}.Error.g.cs", text); - } - - #endregion } \ No newline at end of file diff --git a/GFramework.SourceGenerators.Common/generator/MetadataAttributeClassGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/MetadataAttributeClassGeneratorBase.cs new file mode 100644 index 0000000..bd5e886 --- /dev/null +++ b/GFramework.SourceGenerators.Common/generator/MetadataAttributeClassGeneratorBase.cs @@ -0,0 +1,41 @@ +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace GFramework.SourceGenerators.Common.generator; + +/// +/// 元数据属性类生成器基类,用于基于元数据名称解析特性的抽象基类 +/// +public abstract class MetadataAttributeClassGeneratorBase + : AttributeClassGeneratorBase +{ + /// + /// 获取特性元数据名称的抽象属性 + /// + protected abstract string AttributeMetadataName { get; } + + /// + /// 根据元数据名称解析指定符号上的特性 + /// + /// 编译对象,用于获取类型信息 + /// 命名类型符号,用于查找其上的特性 + /// 如果找到匹配的特性则返回AttributeData对象,否则返回null + protected override AttributeData? ResolveAttribute( + Compilation compilation, + INamedTypeSymbol symbol) + { + // 通过元数据名称获取特性符号 + var attrSymbol = + compilation.GetTypeByMetadataName(AttributeMetadataName); + + if (attrSymbol is null) + return null; + + // 在符号的所有特性中查找与目标特性符号匹配的第一个特性 + return symbol.GetAttributes() + .FirstOrDefault(a => + SymbolEqualityComparer.Default.Equals( + a.AttributeClass, + attrSymbol)); + } +} \ No newline at end of file diff --git a/GFramework.SourceGenerators.Common/generator/TypeAttributeClassGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/TypeAttributeClassGeneratorBase.cs new file mode 100644 index 0000000..7f6ccd7 --- /dev/null +++ b/GFramework.SourceGenerators.Common/generator/TypeAttributeClassGeneratorBase.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace GFramework.SourceGenerators.Common.generator; + +/// +/// 基于类型特性的类生成器基类 +/// +public abstract class TypeAttributeClassGeneratorBase + : AttributeClassGeneratorBase +{ + /// + /// 获取要处理的特性类型 + /// + protected abstract Type AttributeType { get; } + + /// + /// 解析指定符号上的特性 + /// + /// 编译对象(未使用) + /// 要检查的命名类型符号 + /// 匹配的特性数据,如果未找到则返回null + protected override AttributeData? ResolveAttribute( + Compilation compilation, + INamedTypeSymbol symbol) + { + var fullName = AttributeType.FullName; + + // 查找符号上匹配指定特性的第一个实例 + return symbol.GetAttributes() + .FirstOrDefault(a => + string.Equals(a.AttributeClass?.ToDisplayString(), fullName, StringComparison.Ordinal)); + } +} \ No newline at end of file diff --git a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs index a90a29d..dea3a3c 100644 --- a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs +++ b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs @@ -1,7 +1,6 @@ -using System; -using System.Linq; +using System.Linq; using System.Text; -using GFramework.SourceGenerators.Abstractions.enums; +using GFramework.SourceGenerators.Common.constants; using GFramework.SourceGenerators.Common.diagnostics; using GFramework.SourceGenerators.Common.generator; using Microsoft.CodeAnalysis; @@ -13,13 +12,10 @@ namespace GFramework.SourceGenerators.enums; /// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法 /// [Generator] -public sealed class EnumExtensionsGenerator : AttributeClassGeneratorBase +public sealed class EnumExtensionsGenerator : MetadataAttributeClassGeneratorBase { - /// - /// 使用强类型 Attribute,替代字符串 - /// - protected override Type AttributeType => - typeof(GenerateEnumExtensionsAttribute); + protected override string AttributeMetadataName => + $"{PathContests.SourceGeneratorsAbstractionsPath}.enums.GenerateEnumExtensionsAttribute"; /// /// 仅用于 Syntax 粗筛选 diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs index b4d7951..90064fe 100644 --- a/GFramework.SourceGenerators/logging/LoggerGenerator.cs +++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs @@ -13,10 +13,10 @@ namespace GFramework.SourceGenerators.logging; /// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段 /// [Generator] -public sealed class LoggerGenerator : AttributeClassGeneratorBase +public sealed class LoggerGenerator : TypeAttributeClassGeneratorBase { /// - /// 强类型 Attribute + /// 获取属性元数据的完整名称,用于标识日志属性的完全限定名 /// protected override Type AttributeType => typeof(LogAttribute); @@ -25,6 +25,7 @@ public sealed class LoggerGenerator : AttributeClassGeneratorBase /// protected override string AttributeShortNameWithoutSuffix => "Log"; + /// /// 对类进行额外语义校验(可选) /// diff --git a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs index ac816ca..09efa4b 100644 --- a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs +++ b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs @@ -1,26 +1,31 @@ -using System; +using System.Linq; using System.Text; -using GFramework.SourceGenerators.Abstractions.rule; +using GFramework.Core.Abstractions.rule; using GFramework.SourceGenerators.Common.constants; using GFramework.SourceGenerators.Common.generator; +using GFramework.SourceGenerators.diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace GFramework.SourceGenerators.rule; [Generator] -public sealed class ContextAwareGenerator : AttributeClassGeneratorBase +public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase { /// - /// 使用强类型 Attribute,替代字符串 + /// 获取属性元数据的完整名称,用于标识ContextAwareAttribute的完全限定名 /// - protected override Type AttributeType => typeof(ContextAwareAttribute); + /// 返回ContextAwareAttribute的完全限定名字符串 + protected override string AttributeMetadataName => + $"{PathContests.SourceGeneratorsAbstractionsPath}.rule.ContextAwareAttribute"; /// /// 仅用于 Syntax 粗筛选 /// + /// 返回属性的简短名称,不包含后缀 protected override string AttributeShortNameWithoutSuffix => "ContextAware"; + /// /// 额外语义校验:必须实现 IContextAware /// @@ -30,7 +35,13 @@ public sealed class ContextAwareGenerator : AttributeClassGeneratorBase INamedTypeSymbol symbol, AttributeData attr) { - return true; + if (symbol.AllInterfaces.Any(i => + i.ToDisplayString() == typeof(IContextAware).FullName)) return true; + context.ReportDiagnostic(Diagnostic.Create( + ContextAwareDiagnostic.ClassMustImplementIContextAware, + syntax.Identifier.GetLocation(), + symbol.Name)); + return false; } ///