From bc462987afbfe53d1f328e7cdfa1dfac1c59f114 Mon Sep 17 00:00:00 2001 From: GwWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 28 Dec 2025 13:39:38 +0800 Subject: [PATCH] =?UTF-8?q?refactor(logging):=20=E9=87=8D=E6=9E=84Godot?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=94=9F=E6=88=90=E5=99=A8=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将日志生成器注释从LogAttribute更新为GodotLogAttribute - 移除冗余的常量定义AttributeShortName - 优化语法提供程序的查询逻辑,简化LINQ表达式 - 更新方法注释,明确源生成器初始化功能 - 简化属性数据获取逻辑,使用更直接的LINQ查询 - 优化代码生成部分的变量命名,提高可读性 - 改进错误处理的代码生成格式,统一命名规范 - 移除不必要的空行和注释,精简代码结构 --- .../logging/GodotLoggerGenerator.cs | 113 +++++++----------- 1 file changed, 40 insertions(+), 73 deletions(-) diff --git a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs index e5f8dbd..589cc38 100644 --- a/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs +++ b/GFramework.Godot.SourceGenerators/logging/GodotLoggerGenerator.cs @@ -11,7 +11,7 @@ using Microsoft.CodeAnalysis.Text; namespace GFramework.Godot.SourceGenerators.logging; /// -/// 日志生成器,用于为标记了LogAttribute的类自动生成日志字段 +/// 日志生成器,用于为标记了GodotLogAttribute的类自动生成日志字段 /// [Generator] public sealed class GodotLoggerGenerator : IIncrementalGenerator @@ -19,37 +19,33 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator private const string AttributeMetadataName = $"{PathContests.RootAbstractionsPath}.logging.GodotLogAttribute"; - private const string AttributeShortName = "GodotLogAttribute"; private const string AttributeShortNameWithoutSuffix = "GodotLog"; /// - /// 初始化生成器,设置语法过滤和代码生成逻辑 + /// 初始化源生成器,设置语法提供程序和源输出注册 /// /// 增量生成器初始化上下文 public void Initialize(IncrementalGeneratorInitializationContext context) { - // 1. 语法过滤:快速筛选候选类 - var targets = context.SyntaxProvider.CreateSyntaxProvider( + // 创建语法提供程序,用于查找标记了GodotLogAttribute的类声明 + 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); - }); + return cls.AttributeLists + .SelectMany(a => a.Attributes) + .Any(a => a.Name.ToString().Contains(AttributeShortNameWithoutSuffix)); }, static (ctx, _) => { - var classDecl = (ClassDeclarationSyntax)ctx.Node; - var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl); - return (ClassDecl: classDecl, Symbol: symbol); + var cls = (ClassDeclarationSyntax)ctx.Node; + var sym = ctx.SemanticModel.GetDeclaredSymbol(cls); + return (ClassDecl: cls, Symbol: sym); }) .Where(x => x.Symbol is not null); - // 2. 生成代码 + // 注册源输出,为符合条件的类生成日志字段代码 context.RegisterSourceOutput(targets, (spc, pair) => { try @@ -57,102 +53,76 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator var classDecl = pair.ClassDecl; var classSymbol = pair.Symbol!; - // 再次确认是否真的含有目标 Attribute (语义检查) var attr = GetAttribute(classSymbol); - if (attr == null) return; // 可能是名字相似但不是我们要的 Attribute + if (attr is null) return; - // 检查 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)); + spc.AddSource($"{classSymbol.Name}.Logger.g.cs", 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"; + var errorSource = $"// Source generator error: {ex.Message}\n// StackTrace:\n// {ex.StackTrace}"; spc.AddSource($"{safeName}.Logger.Error.g.cs", SourceText.From(errorSource, Encoding.UTF8)); } }); } /// - /// 获取类符号上的LogAttribute特性 + /// 获取类符号上的GodotLogAttribute属性数据 /// /// 类符号 - /// LogAttribute特性数据,如果不存在则返回null + /// GodotLogAttribute属性数据,如果未找到则返回null private static AttributeData? GetAttribute(INamedTypeSymbol classSymbol) - { - return classSymbol.GetAttributes().FirstOrDefault(a => + => classSymbol.GetAttributes().FirstOrDefault(a => { var cls = a.AttributeClass; - if (cls == null) return false; - - // 宽松匹配:全名匹配 OR 名字匹配 - return cls.ToDisplayString() == AttributeMetadataName || - cls.Name == AttributeShortName; + return cls != null && + (cls.ToDisplayString() == AttributeMetadataName || + cls.Name.StartsWith(AttributeShortNameWithoutSuffix)); }); - } /// - /// 生成日志字段代码 + /// 生成日志字段的源代码 /// /// 类符号 - /// LogAttribute特性数据 - /// 生成的C#代码字符串 + /// GodotLogAttribute属性数据 + /// 生成的源代码字符串 private static string Generate(INamedTypeSymbol classSymbol, AttributeData attr) { var ns = classSymbol.ContainingNamespace.IsGlobalNamespace ? null : classSymbol.ContainingNamespace.ToDisplayString(); - var className = classSymbol.Name; - // === 解析 Name === - var name = className; // 默认使用类名 - - // 检查是否有构造函数参数 - if (attr.ConstructorArguments.Length > 0) + // 解析构造函数参数 + var name = className; + if (attr.ConstructorArguments.Length > 0 && attr.ConstructorArguments[0].Value is string s && + !string.IsNullOrWhiteSpace(s)) { - var argValue = attr.ConstructorArguments[0].Value; - - name = argValue switch - { - // 情况 1: 参数存在,但值为 null (例如 [GodotLog] 且构造函数有默认值 null) - null => className, - // 情况 2: 参数存在,且是有效的字符串 (例如 [GodotLog("MyCategory")]) - string s when !string.IsNullOrWhiteSpace(s) => s, - _ => $"{className}_InvalidArg" - }; + name = s; } - // === 解析 Named Arguments (更加安全的获取方式) === + // 解析命名参数 var fieldName = GetNamedArg(attr, "FieldName")?.ToString() ?? "_log"; - var access = - GetNamedArg(attr, "AccessModifier")?.ToString() ?? - "private"; // 注意:如果你的 AccessModifier 是枚举,这里得到的可能是 int 或枚举名 - - // 处理 bool 类型 - var isStaticObj = GetNamedArg(attr, "IsStatic"); - var isStatic = isStaticObj is not bool b || b; // 默认为 true - + var access = GetNamedArg(attr, "AccessModifier")?.ToString() ?? "private"; + var isStatic = GetNamedArg(attr, "IsStatic") is not bool b || b; var staticKeyword = isStatic ? "static " : ""; var sb = new StringBuilder(); sb.AppendLine("// "); - sb.AppendLine("using GFramework.Core.logging;"); // 确保这里引用了 ILogger - sb.AppendLine("using GFramework.Godot.logging;"); // 确保这里引用了 GodotLoggerFactory + sb.AppendLine("using GFramework.Core.logging;"); + sb.AppendLine("using GFramework.Godot.logging;"); + sb.AppendLine(); if (ns is not null) { @@ -164,8 +134,7 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator sb.AppendLine(" {"); sb.AppendLine(" /// Auto-generated logger"); sb.AppendLine( - $" {access} {staticKeyword}readonly ILogger {fieldName} = " + - $"new GodotLoggerFactory().GetLogger(\"{name}\");"); + $" {access} {staticKeyword}readonly ILogger {fieldName} = new GodotLoggerFactory().GetLogger(\"{name}\");"); sb.AppendLine(" }"); if (ns is not null) @@ -175,13 +144,11 @@ public sealed class GodotLoggerGenerator : IIncrementalGenerator } /// - /// 从特性数据中获取命名参数的值 + /// 从属性数据中获取指定名称的命名参数值 /// - /// 特性数据 + /// 属性数据 /// 参数名称 - /// 参数值,如果不存在则返回null + /// 参数值,如果未找到则返回null 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