diff --git a/GFramework.Generator/AnalyzerReleases.Unshipped.md b/GFramework.Generator/AnalyzerReleases.Unshipped.md index 6e2cad7..805387c 100644 --- a/GFramework.Generator/AnalyzerReleases.Unshipped.md +++ b/GFramework.Generator/AnalyzerReleases.Unshipped.md @@ -5,4 +5,5 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- -GFLOG001 | GFramework.Logging | Error | Diagnostics \ No newline at end of file +GFLOG001 | GFramework.Logging | Error | Diagnostics +GFW_LOG001 | GFramework.Logging | Warning | Diagnostics \ No newline at end of file diff --git a/GFramework.Generator/generator/logging/Diagnostic.cs b/GFramework.Generator/generator/logging/Diagnostic.cs index 6dd2100..b52210e 100644 --- a/GFramework.Generator/generator/logging/Diagnostic.cs +++ b/GFramework.Generator/generator/logging/Diagnostic.cs @@ -7,8 +7,9 @@ namespace GFramework.Generator.generator.logging; /// internal static class Diagnostics { + /// - /// 定义一个诊断描述符,用于检查使用[Log]特性的类是否声明为partial + /// 定义诊断描述符:要求使用[Log]特性的类必须声明为partial /// /// /// 当类使用[Log]特性但未声明为partial时,编译器将报告此错误 @@ -22,4 +23,17 @@ internal static class Diagnostics DiagnosticSeverity.Error, isEnabledByDefault: true ); + + /// + /// 定义诊断描述符:LogAttribute无法生成Logger的错误情况 + /// + public static readonly DiagnosticDescriptor LogAttributeInvalid = + new( + id: "GFW_LOG001", + title: "LogAttribute 无法生成 Logger", + messageFormat: "类 '{0}' 上的 LogAttribute 无法生效:{1}", + category: "GFramework.Logging", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + } diff --git a/GFramework.Generator/generator/logging/LoggerGenerator.cs b/GFramework.Generator/generator/logging/LoggerGenerator.cs index 7c24473..f3bd42a 100644 --- a/GFramework.Generator/generator/logging/LoggerGenerator.cs +++ b/GFramework.Generator/generator/logging/LoggerGenerator.cs @@ -30,14 +30,14 @@ namespace GFramework.Generator.generator.logging // 2. 在 SyntaxProvider 阶段就拿到 SemanticModel var candidates = context.SyntaxProvider.CreateSyntaxProvider( - static (node, _) => node is ClassDeclarationSyntax, - 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); + static (node, _) => node is ClassDeclarationSyntax, + 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); // 3. 合并 Attribute Symbol 并筛选 var targets = @@ -70,7 +70,7 @@ namespace GFramework.Generator.generator.logging return; } - var source = Generate(classSymbol); + var source = Generate(classSymbol, spc); spc.AddSource( $"{classSymbol.Name}.Logger.g.cs", SourceText.From(source, Encoding.UTF8)); @@ -81,8 +81,9 @@ namespace GFramework.Generator.generator.logging /// 生成日志字段代码 /// /// 类符号 + /// 源代码生成上下文 /// 生成的代码字符串 - private static string Generate(INamedTypeSymbol classSymbol) + private static string Generate(INamedTypeSymbol classSymbol, SourceProductionContext spc) { var ns = classSymbol.ContainingNamespace.IsGlobalNamespace ? null @@ -91,24 +92,47 @@ namespace GFramework.Generator.generator.logging var className = classSymbol.Name; var attr = classSymbol.GetAttributes() - .First(a => a.AttributeClass!.ToDisplayString() == AttributeMetadataName); + .FirstOrDefault(a => + a.AttributeClass?.ToDisplayString() == AttributeMetadataName); - string categoryExpr; - - if (attr.ConstructorArguments.Length > 0 && - attr.ConstructorArguments[0].Value is string s && - !string.IsNullOrWhiteSpace(s)) + if (attr is null) { - // 用户显式指定字符串 - categoryExpr = $"\"{s}\""; + // 理论上不会发生,但防御式处理 + spc.ReportDiagnostic( + Diagnostic.Create( + Diagnostics.LogAttributeInvalid, + classSymbol.Locations.FirstOrDefault(), + className, + "未找到 LogAttribute" + )); + return string.Empty; + } + + // === 解析 category === + string category; + + if (attr.ConstructorArguments.Length == 0) + { + // 默认:类名 + category = className; + } + else if (attr.ConstructorArguments[0].Value is string s && + !string.IsNullOrWhiteSpace(s)) + { + category = s; } else { - // 默认使用 nameof(Class) - categoryExpr = $"nameof({className})"; + spc.ReportDiagnostic( + Diagnostic.Create( + Diagnostics.LogAttributeInvalid, + classSymbol.Locations.FirstOrDefault(), + className, + "LogAttribute 的构造参数不是有效的 string" + )); + return string.Empty; } - var fieldName = GetNamedArg(attr, "FieldName", "_log"); var access = GetNamedArg(attr, "AccessModifier", "private"); var isStatic = GetNamedArg(attr, "IsStatic", true); @@ -128,8 +152,8 @@ namespace GFramework.Generator.generator.logging sb.AppendLine($" public partial class {className}"); sb.AppendLine(" {"); sb.AppendLine( - $" {access} {staticKeyword}readonly ILog {fieldName} =" + - $" Log.CreateLogger(\"{categoryExpr}\");"); + $" {access} {staticKeyword}readonly ILog {fieldName} = " + + $"Log.CreateLogger(\"{category}\");"); sb.AppendLine(" }"); if (ns is not null) @@ -138,6 +162,7 @@ namespace GFramework.Generator.generator.logging return sb.ToString(); } + /// /// 获取属性参数的值 /// @@ -153,7 +178,8 @@ namespace GFramework.Generator.generator.logging if (kv.Key == name && kv.Value.Value is T v) return v; } + return defaultValue; } } -} +} \ No newline at end of file