diff --git a/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs
new file mode 100644
index 0000000..ec36fed
--- /dev/null
+++ b/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace GFramework.SourceGenerators.Common.generator;
+
+///
+/// 基于特性的枚举生成器基类
+///
+/// 特性类型,必须继承自Attribute
+public abstract class AttributeEnumGeneratorBase : IIncrementalGenerator
+ where TAttribute : Attribute
+{
+ ///
+ /// 获取特性的短名称(不包含后缀)
+ ///
+ protected abstract string AttributeShortNameWithoutSuffix { get; }
+
+ ///
+ /// 初始化增量生成器
+ ///
+ /// 增量生成器初始化上下文
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // 查找带有指定特性的枚举声明
+ var enums = context.SyntaxProvider.CreateSyntaxProvider(
+ (node, _) => node is EnumDeclarationSyntax decl &&
+ decl.AttributeLists.SelectMany(a => a.Attributes)
+ .Any(a => a.Name.ToString().Contains(AttributeShortNameWithoutSuffix)),
+ (ctx, _) =>
+ {
+ var decl = (EnumDeclarationSyntax)ctx.Node;
+ var symbol = ctx.SemanticModel.GetDeclaredSymbol(decl) as INamedTypeSymbol;
+ return (decl, symbol);
+ }).Where(x => x.symbol != null);
+
+ // 注册源代码输出
+ context.RegisterSourceOutput(enums, (spc, pair) =>
+ {
+ var attr = pair.symbol!.GetAttributes()
+ .FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == typeof(TAttribute).FullName);
+ if (attr == null) return;
+
+ var src = Generate(pair.symbol!, attr);
+ spc.AddSource($"{pair.symbol!.Name}.EnumExtensions.g.cs", SourceText.From(src, Encoding.UTF8));
+ });
+ }
+
+ ///
+ /// 生成源代码
+ ///
+ /// 枚举符号
+ /// 特性数据
+ /// 生成的源代码字符串
+ protected abstract string Generate(INamedTypeSymbol enumSymbol, AttributeData attr);
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj b/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj
index 911926f..71449aa 100644
--- a/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj
+++ b/GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj
@@ -3,7 +3,7 @@
enable
enable
- net8.0
+ net8.0;net9.0;net10.0
diff --git a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs
index 1f23cab..a90a29d 100644
--- a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs
+++ b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs
@@ -1,80 +1,82 @@
using System;
using System.Linq;
using System.Text;
-using GFramework.SourceGenerators.constants;
+using GFramework.SourceGenerators.Abstractions.enums;
+using GFramework.SourceGenerators.Common.diagnostics;
+using GFramework.SourceGenerators.Common.generator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Text;
namespace GFramework.SourceGenerators.enums;
+///
+/// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法
+///
[Generator]
-public class EnumExtensionsGenerator : IIncrementalGenerator
+public sealed class EnumExtensionsGenerator : AttributeClassGeneratorBase
{
- private const string AttributeFullName =
- $"{PathContests.RootAbstractionsPath}.enums.GenerateEnumExtensionsAttribute";
+ ///
+ /// 使用强类型 Attribute,替代字符串
+ ///
+ protected override Type AttributeType =>
+ typeof(GenerateEnumExtensionsAttribute);
- public void Initialize(IncrementalGeneratorInitializationContext context)
+ ///
+ /// 仅用于 Syntax 粗筛选
+ ///
+ protected override string AttributeShortNameWithoutSuffix => "GenerateEnumExtensions";
+
+ ///
+ /// 验证符号是否为有效的枚举类型
+ ///
+ /// 源生产上下文
+ /// 类声明语法节点
+ /// 命名类型符号
+ /// 属性数据
+ /// 验证是否通过
+ protected override bool ValidateSymbol(
+ SourceProductionContext context,
+ ClassDeclarationSyntax syntax,
+ INamedTypeSymbol symbol,
+ AttributeData attr)
{
- // 1. 找到所有 EnumDeclarationSyntax 节点
- var enumDecls = context.SyntaxProvider
- .CreateSyntaxProvider(
- (s, _) => s is EnumDeclarationSyntax,
- (ctx, _) =>
- (EnumDecl: (EnumDeclarationSyntax)ctx.Node, ctx.SemanticModel))
- .Where(t => t.EnumDecl != null);
-
- // 2. 解析为 symbol 并过滤带 Attribute 的 enum
- var enumSymbols = enumDecls
- .Select((t, _) =>
- {
- var model = t.SemanticModel;
- var enumDecl = t.EnumDecl;
- var symbol = model.GetDeclaredSymbol(enumDecl) as INamedTypeSymbol;
- return symbol;
- })
- .Where(symbol => symbol != null)
- .Select((symbol, _) =>
- {
- var hasAttr = symbol!.GetAttributes().Any(ad =>
- ad.AttributeClass?.ToDisplayString() == AttributeFullName);
- return (Symbol: symbol, HasAttr: hasAttr);
- })
- .Where(x => x.HasAttr)
- .Collect();
-
- // 3. 为每个 enum 生成代码
- context.RegisterSourceOutput(enumSymbols, (spc, list) =>
+ if (symbol.TypeKind != TypeKind.Enum)
{
- foreach (var enumSymbol in list.Select(item => item.Symbol))
- try
- {
- var src = GenerateForEnum(enumSymbol);
- var hintName = $"{enumSymbol.Name}.EnumExtensions.g.cs";
- spc.AddSource(hintName, SourceText.From(src, Encoding.UTF8));
- }
- catch (Exception ex)
- {
- // 发生异常时生成一个注释文件(避免完全静默失败)
- var err = $"// EnumExtensionsGenerator failed for {enumSymbol?.Name}: {ex.Message}";
- spc.AddSource($"{enumSymbol?.Name}.EnumExtensions.Error.g.cs",
- SourceText.From(err, Encoding.UTF8));
- }
- });
+ var loc = syntax.Identifier.GetLocation();
+ context.ReportDiagnostic(Diagnostic.Create(
+ CommonDiagnostics.ClassMustBePartial, // 可以定义一个新的 Enum 专用 Diagnostic
+ loc,
+ symbol.Name
+ ));
+ return false;
+ }
+
+ return true;
}
- private static string GenerateForEnum(INamedTypeSymbol enumSymbol)
+ ///
+ /// 生成枚举扩展方法的源代码
+ ///
+ /// 枚举类型符号
+ /// 属性数据
+ /// 生成的源代码字符串
+ protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
{
- var ns = enumSymbol.ContainingNamespace.IsGlobalNamespace
+ var ns = symbol.ContainingNamespace.IsGlobalNamespace
? null
- : enumSymbol.ContainingNamespace.ToDisplayString();
- var enumName = enumSymbol.Name;
- var fullEnumName = enumSymbol.ToDisplayString(); // 包含命名空间
- var members = enumSymbol.GetMembers().OfType().Where(f => f.ConstantValue != null).ToArray();
+ : symbol.ContainingNamespace.ToDisplayString();
+
+ var enumName = symbol.Name;
+ var fullEnumName = symbol.ToDisplayString();
+ var members = symbol.GetMembers()
+ .OfType()
+ .Where(f => f.ConstantValue != null)
+ .ToArray();
var sb = new StringBuilder();
sb.AppendLine("// ");
sb.AppendLine("using System;");
+
if (!string.IsNullOrEmpty(ns))
{
sb.AppendLine($"namespace {ns}");
@@ -89,25 +91,18 @@ public class EnumExtensionsGenerator : IIncrementalGenerator
sb.AppendLine($" public static partial class {enumName}Extensions");
sb.AppendLine(" {");
- // 1. 单项 IsX 方法
-// 替换原第93行开始的 foreach 块
- var memberChecks = members.Select(m =>
+ // 生成 IsX 方法
+ foreach (var memberName in members.Select(m => m.Name))
{
- var memberName = m.Name;
- var safeMethodName = $"Is{memberName}";
- return $@" /// Auto-generated: 是否为 {memberName}
- public static bool {safeMethodName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};
+ sb.AppendLine($" /// Auto-generated: 是否为 {memberName}");
+ sb.AppendLine(
+ $" public static bool Is{memberName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName};");
+ sb.AppendLine();
+ }
-";
- }).ToArray();
-
- sb.Append(string.Join("", memberChecks));
-
-
- // 2. IsIn(params ...) 方法
+ // 生成 IsIn 方法
sb.AppendLine(" /// Auto-generated: 判断是否属于指定集合");
- sb.AppendLine(
- $" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
+ sb.AppendLine($" public static bool IsIn(this {fullEnumName} value, params {fullEnumName}[] values)");
sb.AppendLine(" {");
sb.AppendLine(" if (values == null) return false;");
sb.AppendLine(" foreach (var v in values) if (value == v) return true;");
@@ -119,4 +114,12 @@ public class EnumExtensionsGenerator : IIncrementalGenerator
return sb.ToString();
}
+
+ ///
+ /// 获取生成文件的提示名称
+ ///
+ /// 命名类型符号
+ /// 生成文件的提示名称
+ protected override string GetHintName(INamedTypeSymbol symbol)
+ => $"{symbol.Name}.EnumExtensions.g.cs";
}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/logging/LoggerGenerator.cs b/GFramework.SourceGenerators/logging/LoggerGenerator.cs
index 554ab2a..7d50aff 100644
--- a/GFramework.SourceGenerators/logging/LoggerGenerator.cs
+++ b/GFramework.SourceGenerators/logging/LoggerGenerator.cs
@@ -1,12 +1,10 @@
using System;
using System.Linq;
using System.Text;
-using GFramework.SourceGenerators.Common.diagnostics;
-using GFramework.SourceGenerators.constants;
+using GFramework.SourceGenerators.Abstractions.logging;
+using GFramework.SourceGenerators.Common.generator;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.Text;
namespace GFramework.SourceGenerators.logging;
@@ -14,171 +12,85 @@ namespace GFramework.SourceGenerators.logging;
/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段
///
[Generator]
-public sealed class LoggerGenerator : IIncrementalGenerator
+public sealed class LoggerGenerator : AttributeClassGeneratorBase
{
- private const string AttributeMetadataName = $"{PathContests.RootAbstractionsPath}.logging.LogAttribute";
- private const string AttributeShortName = "LogAttribute";
- private const string AttributeShortNameWithoutSuffix = "Log";
+ ///
+ /// 强类型 Attribute
+ ///
+ protected override Type AttributeType => typeof(LogAttribute);
///
- /// 初始化生成器,设置语法过滤和代码生成逻辑
+ /// 用于语法快速筛选
///
- /// 增量生成器初始化上下文
- public void Initialize(IncrementalGeneratorInitializationContext context)
+ protected override string AttributeShortNameWithoutSuffix => "Log";
+
+ ///
+ /// 对类进行额外语义校验(可选)
+ ///
+ protected override bool ValidateSymbol(
+ SourceProductionContext context,
+ ClassDeclarationSyntax syntax,
+ INamedTypeSymbol symbol,
+ AttributeData attr)
{
- // 1. 语法过滤:快速筛选候选类
- var targets = context.SyntaxProvider.CreateSyntaxProvider(
- static (node, _) =>
- {
- if (node is not ClassDeclarationSyntax cls) return false;
- // 只要包含 Log 字眼的 Attribute 就先放行
- return cls.AttributeLists.SelectMany(a => a.Attributes).Any(a =>
- {
- var name = a.Name.ToString();
- // 简单的字符串匹配,防止错过别名情况
- return name.Contains(AttributeShortNameWithoutSuffix);
- });
- },
- static (ctx, _) =>
- {
- var classDecl = (ClassDeclarationSyntax)ctx.Node;
- var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
- return (ClassDecl: classDecl, Symbol: symbol);
- })
- .Where(x => x.Symbol is not null);
-
- // 2. 生成代码
- context.RegisterSourceOutput(targets, (spc, pair) =>
- {
- try
- {
- var classDecl = pair.ClassDecl;
- var classSymbol = pair.Symbol!;
-
- // 再次确认是否真的含有目标 Attribute (语义检查)
- var attr = GetAttribute(classSymbol);
- if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute
-
- // 检查 partial
- if (!classDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
- {
- spc.ReportDiagnostic(Diagnostic.Create(
- CommonDiagnostics.ClassMustBePartial,
- classDecl.Identifier.GetLocation(),
- classSymbol.Name));
-
- return;
- }
-
- var source = Generate(classSymbol, attr);
- var hintName = $"{classSymbol.Name}.Logger.g.cs";
- spc.AddSource(hintName, SourceText.From(source, Encoding.UTF8));
- }
- catch (Exception ex)
- {
- // === 关键修复:生成错误报告文件 ===
- var errorSource = $"// source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}";
- // 替换非法字符以防文件名报错
- var safeName = pair.Symbol?.Name ?? "Unknown";
- spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8));
- }
- });
+ // 可以加自定义规则,比如确保类是 public 或实现某接口
+ return true;
}
///
- /// 获取类符号上的LogAttribute特性
+ /// 生成 Logger 字段的代码
///
- /// 类符号
- /// LogAttribute特性数据,如果不存在则返回null
- private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol)
+ protected override string Generate(INamedTypeSymbol symbol, AttributeData attr)
{
- return classSymbol.GetAttributes().FirstOrDefault(a =>
- {
- var cls = a.AttributeClass;
- if (cls == null) return false;
-
- // 宽松匹配:全名匹配 OR 名字匹配
- return cls.ToDisplayString() == AttributeMetadataName ||
- cls.Name == AttributeShortName;
- });
- }
-
- ///
- /// 生成日志字段代码
- ///
- /// 类符号
- /// LogAttribute特性数据
- /// 生成的C#代码字符串
- private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr)
- {
- var ns = classSymbol.ContainingNamespace.IsGlobalNamespace
+ var ns = symbol.ContainingNamespace.IsGlobalNamespace
? null
- : classSymbol.ContainingNamespace.ToDisplayString();
+ : symbol.ContainingNamespace.ToDisplayString();
- var className = classSymbol.Name;
+ var className = symbol.Name;
- // === 解析 Name ===
- var name = className; // 默认使用类名
-
- // 检查是否有构造函数参数
+ // 解析构造函数参数
+ var name = className;
if (attr.ConstructorArguments.Length > 0)
{
var argValue = attr.ConstructorArguments[0].Value;
-
- name = argValue switch
- {
- // 情况 1: 参数存在,但值为 null (例如 [Log] 且构造函数有默认值 null)
- null => className,
- // 情况 2: 参数存在,且是有效的字符串 (例如 [Log("MyCategory")])
- string s when !string.IsNullOrWhiteSpace(s) => s,
- _ => $"{className}_InvalidArg"
- };
+ name = argValue is string s && !string.IsNullOrWhiteSpace(s)
+ ? s
+ : className;
}
- // === 解析 Named Arguments (更加安全的获取方式) ===
+ // 解析命名参数
var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log";
- var access =
- GetNamedArg(attr, "AccessModifier")?.ToString() ??
- "private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名
-
- // 处理 bool 类型
+ var access = GetNamedArg(attr, "AccessModifier")?.ToString() ?? "private";
var isStaticObj = GetNamedArg(attr, "IsStatic");
- var isStatic = isStaticObj is not bool b || b; // 默认为 true
-
+ var isStatic = isStaticObj is not bool b || b; // 默认 true
var staticKeyword = isStatic ? "static " : "";
var sb = new StringBuilder();
sb.AppendLine("// ");
- sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILog 和 Log 类
+ sb.AppendLine("using GFramework.Core.logging;");
if (ns is not null)
{
- sb.AppendLine($"namespace {ns}");
- sb.AppendLine("{");
+ sb.AppendLine($"namespace {ns};");
+ sb.AppendLine();
}
- sb.AppendLine($" public partial class {className}");
- sb.AppendLine(" {");
- sb.AppendLine(" /// Auto-generated logger");
+ sb.AppendLine($"partial class {className}");
+ sb.AppendLine("{");
+ sb.AppendLine(" /// Auto-generated logger");
sb.AppendLine(
- $" {access} {staticKeyword}readonly ILogger {fieldName} = " +
- $"new ConsoleLoggerFactory().GetLogger(\"{name}\");");
- sb.AppendLine(" }");
+ $" {access} {staticKeyword}readonly ILogger {fieldName} = new ConsoleLoggerFactory().GetLogger(\"{name}\");");
+ sb.AppendLine("}");
- if (ns is not null)
- sb.AppendLine("}");
-
- return sb.ToString();
+ return sb.ToString().TrimEnd();
}
///
- /// 从特性数据中获取命名参数的值
+ /// 可以自定义生成文件名
///
- /// 特性数据
- /// 参数名称
- /// 参数值,如果不存在则返回null
+ protected override string GetHintName(INamedTypeSymbol symbol)
+ => $"{symbol.Name}.Logger.g.cs";
+
private static object? GetNamedArg(AttributeData attr, string name)
- {
- return (from kv in attr.NamedArguments where kv.Key == name select kv.Value.Value).FirstOrDefault();
- }
+ => attr.NamedArguments.FirstOrDefault(kv => kv.Key == name).Value.Value;
}
\ No newline at end of file