diff --git a/GFramework.SourceGenerators.Abstractions/enums/EnumExtensionsAttribute.cs b/GFramework.SourceGenerators.Abstractions/enums/GenerateEnumExtensionsAttribute.cs similarity index 100% rename from GFramework.SourceGenerators.Abstractions/enums/EnumExtensionsAttribute.cs rename to GFramework.SourceGenerators.Abstractions/enums/GenerateEnumExtensionsAttribute.cs diff --git a/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs b/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs index ec36fed..d12233a 100644 --- a/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs +++ b/GFramework.SourceGenerators.Common/generator/AttributeEnumGeneratorBase.cs @@ -1,21 +1,16 @@ -using System; -using System.Linq; -using System.Text; +using System.Linq; 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 +public abstract class AttributeEnumGeneratorBase : IIncrementalGenerator { /// - /// 获取特性的短名称(不包含后缀) + /// 获取属性的短名称(不包含后缀) /// protected abstract string AttributeShortNameWithoutSuffix { get; } @@ -25,35 +20,83 @@ public abstract class AttributeEnumGeneratorBase : IIncrementalGener /// 增量生成器初始化上下文 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); + // 创建语法提供程序,查找带有指定属性的枚举声明 + var candidates = context.SyntaxProvider.CreateSyntaxProvider( + (node, _) => + node is EnumDeclarationSyntax eds && + eds.AttributeLists + .SelectMany(a => a.Attributes) + .Any(a => a.Name.ToString() + .Contains(AttributeShortNameWithoutSuffix)), + (ctx, _) => + { + var syntax = (EnumDeclarationSyntax)ctx.Node; + var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax, cancellationToken: _) as INamedTypeSymbol; + return (syntax, symbol); + }) + .Where(x => x.symbol is not null); - // 注册源代码输出 - context.RegisterSourceOutput(enums, (spc, pair) => + var combined = candidates.Combine(context.CompilationProvider); + + // 注册源输出,生成最终的源代码 + context.RegisterSourceOutput(combined, (spc, pair) => { - var attr = pair.symbol!.GetAttributes() - .FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == typeof(TAttribute).FullName); - if (attr == null) return; + var ((syntax, symbol), compilation) = pair; - var src = Generate(pair.symbol!, attr); - spc.AddSource($"{pair.symbol!.Name}.EnumExtensions.g.cs", SourceText.From(src, Encoding.UTF8)); + var attr = ResolveAttribute(compilation, symbol!); + if (attr is null) + return; + + if (!ValidateSymbol(spc, compilation, syntax, symbol!, attr)) + return; + + spc.AddSource( + GetHintName(symbol!), + Generate(symbol!, attr)); }); } + /// + /// 解析指定符号上的属性数据 + /// + /// 编译对象 + /// 命名类型符号 + /// 属性数据对象,如果未找到则返回null + protected abstract AttributeData? ResolveAttribute( + Compilation compilation, + INamedTypeSymbol symbol); + + /// + /// 验证符号是否符合生成要求 + /// + /// 源生产上下文 + /// 编译对象 + /// 枚举声明语法节点 + /// 命名类型符号 + /// 属性数据 + /// 验证是否通过 + protected abstract bool ValidateSymbol( + SourceProductionContext context, + Compilation compilation, + EnumDeclarationSyntax syntax, + INamedTypeSymbol symbol, + AttributeData attr); + /// /// 生成源代码 /// - /// 枚举符号 - /// 特性数据 + /// 命名类型符号 + /// 属性数据 /// 生成的源代码字符串 - protected abstract string Generate(INamedTypeSymbol enumSymbol, AttributeData attr); + protected abstract string Generate( + INamedTypeSymbol symbol, + AttributeData attr); + + /// + /// 获取生成文件的提示名称 + /// + /// 命名类型符号 + /// 生成文件的提示名称 + protected virtual string GetHintName(INamedTypeSymbol symbol) + => $"{symbol.Name}.g.cs"; } \ No newline at end of file diff --git a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs index 62fa564..3d39590 100644 --- a/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs +++ b/GFramework.SourceGenerators/enums/EnumExtensionsGenerator.cs @@ -12,9 +12,9 @@ namespace GFramework.SourceGenerators.enums; /// 枚举扩展方法生成器,用于自动生成枚举相关的扩展方法 /// [Generator] -public sealed class EnumExtensionsGenerator : MetadataAttributeClassGeneratorBase +public sealed class EnumExtensionsGenerator : AttributeEnumGeneratorBase { - protected override string AttributeMetadataName => + private static string AttributeMetadataName => $"{PathContests.SourceGeneratorsAbstractionsPath}.enums.GenerateEnumExtensionsAttribute"; /// @@ -22,34 +22,30 @@ public sealed class EnumExtensionsGenerator : MetadataAttributeClassGeneratorBas /// protected override string AttributeShortNameWithoutSuffix => "GenerateEnumExtensions"; - /// - /// 验证符号是否为有效的枚举类型 - /// - /// 源生产上下文 - /// 编译对象 - /// 类声明语法节点 - /// 命名类型符号 - /// 属性数据 - /// 验证是否通过 - protected override bool ValidateSymbol( - SourceProductionContext context, - Compilation compilation, - ClassDeclarationSyntax syntax, - INamedTypeSymbol symbol, - AttributeData attr) + protected override AttributeData? ResolveAttribute(Compilation compilation, INamedTypeSymbol symbol) { - if (symbol.TypeKind != TypeKind.Enum) - { - var loc = syntax.Identifier.GetLocation(); - context.ReportDiagnostic(Diagnostic.Create( - CommonDiagnostics.ClassMustBePartial, // 可以定义一个新的 Enum 专用 Diagnostic - loc, - symbol.Name - )); - return false; - } + var attrSymbol = compilation.GetTypeByMetadataName(AttributeMetadataName); - return true; + if (attrSymbol is null) + return null; + + return symbol.GetAttributes() + .FirstOrDefault(a => + SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol)); + } + + protected override bool ValidateSymbol(SourceProductionContext context, Compilation compilation, + EnumDeclarationSyntax syntax, + INamedTypeSymbol symbol, AttributeData attr) + { + if (symbol.TypeKind == TypeKind.Enum) return true; + var loc = syntax.Identifier.GetLocation(); + context.ReportDiagnostic(Diagnostic.Create( + CommonDiagnostics.ClassMustBePartial, + loc, + symbol.Name + )); + return false; } /// @@ -75,16 +71,9 @@ public sealed class EnumExtensionsGenerator : MetadataAttributeClassGeneratorBas sb.AppendLine("// "); sb.AppendLine("using System;"); - if (!string.IsNullOrEmpty(ns)) - { - sb.AppendLine($"namespace {ns}"); - sb.AppendLine("{"); - } - else - { - sb.AppendLine("namespace EnumExtensionsGenerated"); - sb.AppendLine("{"); - } + sb.AppendLine(!string.IsNullOrEmpty(ns) ? $"namespace {ns}" : "namespace EnumExtensionsGenerated"); + + sb.AppendLine("{"); sb.AppendLine($" public static partial class {enumName}Extensions"); sb.AppendLine(" {");