using System; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace GFramework.SourceGenerators.enums; [Generator] public class EnumExtensionsGenerator : IIncrementalGenerator { private const string AttributeFullName = "GFramework.SourceGenerators.Abstractions.generator.enums.GenerateEnumExtensionsAttribute"; public void Initialize(IncrementalGeneratorInitializationContext context) { // 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 || ad.AttributeClass?.ToDisplayString().EndsWith(".GenerateEnumExtensionsAttribute") == true); return (Symbol: symbol, HasAttr: hasAttr); }) .Where(x => x.HasAttr) .Collect(); // 3. 为每个 enum 生成代码 context.RegisterSourceOutput(enumSymbols, (spc, list) => { 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)); } }); } private static string GenerateForEnum(INamedTypeSymbol enumSymbol) { var ns = enumSymbol.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(); var sb = new StringBuilder(); 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($" public static partial class {enumName}Extensions"); sb.AppendLine(" {"); // 1. 单项 IsX 方法 // 替换原第93行开始的 foreach 块 var memberChecks = members.Select(m => { var memberName = m.Name; var safeMethodName = $"Is{memberName}"; return $@" /// Auto-generated: 是否为 {memberName} public static bool {safeMethodName}(this {fullEnumName} value) => value == {fullEnumName}.{memberName}; "; }).ToArray(); sb.Append(string.Join("", memberChecks)); // 2. IsIn(params ...) 方法 sb.AppendLine(" /// Auto-generated: 判断是否属于指定集合"); 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;"); sb.AppendLine(" return false;"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine("}"); // namespace return sb.ToString(); } }