mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 04:59:01 +08:00
feat(logging): 添加LogAttribute诊断验证功能
- 定义新的诊断描述符GFW_LOG001用于检测LogAttribute无法生成Logger的情况 - 在代码生成过程中验证LogAttribute是否存在及构造参数有效性 - 当LogAttribute缺失或参数无效时报告诊断错误并提供详细信息 - 修复代码格式化问题并优化代码生成逻辑 - 更新AnalyzerReleases.Unshipped.md文档添加新诊断规则 - 改进Generate方法参数传递和错误处理机制
This commit is contained in:
parent
0221ed92a6
commit
d4b37345db
@ -5,4 +5,5 @@
|
|||||||
|
|
||||||
Rule ID | Category | Severity | Notes
|
Rule ID | Category | Severity | Notes
|
||||||
--------|----------|----------|-------
|
--------|----------|----------|-------
|
||||||
GFLOG001 | GFramework.Logging | Error | Diagnostics
|
GFLOG001 | GFramework.Logging | Error | Diagnostics
|
||||||
|
GFW_LOG001 | GFramework.Logging | Warning | Diagnostics
|
||||||
@ -7,8 +7,9 @@ namespace GFramework.Generator.generator.logging;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class Diagnostics
|
internal static class Diagnostics
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 定义一个诊断描述符,用于检查使用[Log]特性的类是否声明为partial
|
/// 定义诊断描述符:要求使用[Log]特性的类必须声明为partial
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// 当类使用[Log]特性但未声明为partial时,编译器将报告此错误
|
/// 当类使用[Log]特性但未声明为partial时,编译器将报告此错误
|
||||||
@ -22,4 +23,17 @@ internal static class Diagnostics
|
|||||||
DiagnosticSeverity.Error,
|
DiagnosticSeverity.Error,
|
||||||
isEnabledByDefault: true
|
isEnabledByDefault: true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定义诊断描述符:LogAttribute无法生成Logger的错误情况
|
||||||
|
/// </summary>
|
||||||
|
public static readonly DiagnosticDescriptor LogAttributeInvalid =
|
||||||
|
new(
|
||||||
|
id: "GFW_LOG001",
|
||||||
|
title: "LogAttribute 无法生成 Logger",
|
||||||
|
messageFormat: "类 '{0}' 上的 LogAttribute 无法生效:{1}",
|
||||||
|
category: "GFramework.Logging",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
isEnabledByDefault: true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,14 +30,14 @@ namespace GFramework.Generator.generator.logging
|
|||||||
// 2. 在 SyntaxProvider 阶段就拿到 SemanticModel
|
// 2. 在 SyntaxProvider 阶段就拿到 SemanticModel
|
||||||
var candidates =
|
var candidates =
|
||||||
context.SyntaxProvider.CreateSyntaxProvider(
|
context.SyntaxProvider.CreateSyntaxProvider(
|
||||||
static (node, _) => node is ClassDeclarationSyntax,
|
static (node, _) => node is ClassDeclarationSyntax,
|
||||||
static (ctx, _) =>
|
static (ctx, _) =>
|
||||||
{
|
{
|
||||||
var classDecl = (ClassDeclarationSyntax)ctx.Node;
|
var classDecl = (ClassDeclarationSyntax)ctx.Node;
|
||||||
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
|
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
|
||||||
return (ClassDecl: classDecl, Symbol: symbol);
|
return (ClassDecl: classDecl, Symbol: symbol);
|
||||||
})
|
})
|
||||||
.Where(x => x.Symbol is not null);
|
.Where(x => x.Symbol is not null);
|
||||||
|
|
||||||
// 3. 合并 Attribute Symbol 并筛选
|
// 3. 合并 Attribute Symbol 并筛选
|
||||||
var targets =
|
var targets =
|
||||||
@ -70,7 +70,7 @@ namespace GFramework.Generator.generator.logging
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var source = Generate(classSymbol);
|
var source = Generate(classSymbol, spc);
|
||||||
spc.AddSource(
|
spc.AddSource(
|
||||||
$"{classSymbol.Name}.Logger.g.cs",
|
$"{classSymbol.Name}.Logger.g.cs",
|
||||||
SourceText.From(source, Encoding.UTF8));
|
SourceText.From(source, Encoding.UTF8));
|
||||||
@ -81,8 +81,9 @@ namespace GFramework.Generator.generator.logging
|
|||||||
/// 生成日志字段代码
|
/// 生成日志字段代码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="classSymbol">类符号</param>
|
/// <param name="classSymbol">类符号</param>
|
||||||
|
/// <param name="spc">源代码生成上下文</param>
|
||||||
/// <returns>生成的代码字符串</returns>
|
/// <returns>生成的代码字符串</returns>
|
||||||
private static string Generate(INamedTypeSymbol classSymbol)
|
private static string Generate(INamedTypeSymbol classSymbol, SourceProductionContext spc)
|
||||||
{
|
{
|
||||||
var ns = classSymbol.ContainingNamespace.IsGlobalNamespace
|
var ns = classSymbol.ContainingNamespace.IsGlobalNamespace
|
||||||
? null
|
? null
|
||||||
@ -91,24 +92,47 @@ namespace GFramework.Generator.generator.logging
|
|||||||
var className = classSymbol.Name;
|
var className = classSymbol.Name;
|
||||||
|
|
||||||
var attr = classSymbol.GetAttributes()
|
var attr = classSymbol.GetAttributes()
|
||||||
.First(a => a.AttributeClass!.ToDisplayString() == AttributeMetadataName);
|
.FirstOrDefault(a =>
|
||||||
|
a.AttributeClass?.ToDisplayString() == AttributeMetadataName);
|
||||||
|
|
||||||
string categoryExpr;
|
if (attr is null)
|
||||||
|
|
||||||
if (attr.ConstructorArguments.Length > 0 &&
|
|
||||||
attr.ConstructorArguments[0].Value is string s &&
|
|
||||||
!string.IsNullOrWhiteSpace(s))
|
|
||||||
{
|
{
|
||||||
// 用户显式指定字符串
|
// 理论上不会发生,但防御式处理
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
// 默认使用 nameof(Class)
|
spc.ReportDiagnostic(
|
||||||
categoryExpr = $"nameof({className})";
|
Diagnostic.Create(
|
||||||
|
Diagnostics.LogAttributeInvalid,
|
||||||
|
classSymbol.Locations.FirstOrDefault(),
|
||||||
|
className,
|
||||||
|
"LogAttribute 的构造参数不是有效的 string"
|
||||||
|
));
|
||||||
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var fieldName = GetNamedArg(attr, "FieldName", "_log");
|
var fieldName = GetNamedArg(attr, "FieldName", "_log");
|
||||||
var access = GetNamedArg(attr, "AccessModifier", "private");
|
var access = GetNamedArg(attr, "AccessModifier", "private");
|
||||||
var isStatic = GetNamedArg(attr, "IsStatic", true);
|
var isStatic = GetNamedArg(attr, "IsStatic", true);
|
||||||
@ -128,8 +152,8 @@ namespace GFramework.Generator.generator.logging
|
|||||||
sb.AppendLine($" public partial class {className}");
|
sb.AppendLine($" public partial class {className}");
|
||||||
sb.AppendLine(" {");
|
sb.AppendLine(" {");
|
||||||
sb.AppendLine(
|
sb.AppendLine(
|
||||||
$" {access} {staticKeyword}readonly ILog {fieldName} =" +
|
$" {access} {staticKeyword}readonly ILog {fieldName} = " +
|
||||||
$" Log.CreateLogger(\"{categoryExpr}\");");
|
$"Log.CreateLogger(\"{category}\");");
|
||||||
sb.AppendLine(" }");
|
sb.AppendLine(" }");
|
||||||
|
|
||||||
if (ns is not null)
|
if (ns is not null)
|
||||||
@ -138,6 +162,7 @@ namespace GFramework.Generator.generator.logging
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取属性参数的值
|
/// 获取属性参数的值
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -153,7 +178,8 @@ namespace GFramework.Generator.generator.logging
|
|||||||
if (kv.Key == name && kv.Value.Value is T v)
|
if (kv.Key == name && kv.Value.Value is T v)
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user