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;
}
///