GFramework/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
GwWuYou f3c5840ebe feat(diagnostic): 添加通用诊断描述符并重构诊断系统
- 添加 CommonDiagnostics 类提供通用诊断描述符
- 将诊断相关文件从 logging 目录移动到 diagnostics 目录
- 更新命名空间从 GFramework.SourceGenerators.Common.diagnostics 到 GFramework.SourceGenerators.diagnostics
- 修改诊断ID从 GFC001 到 GF_Common_Class_001
- 移除 GFramework.SourceGenerators.Common 项目引用
- 更新 AnalyzerReleases.Unshipped.md 文件中的诊断规则
- 重构 README.md 文件提供完整的项目介绍和使用指南
2025-12-27 22:51:39 +08:00

123 lines
3.9 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Linq;
using System.Text;
using GFramework.SourceGenerators.diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.rule;
[Generator]
public sealed class ContextAwareGenerator : IIncrementalGenerator
{
private const string AttributeMetadataName =
"GFramework.SourceGenerators.Abstractions.rule.ContextAwareAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 1⃣ 查找候选类
var candidates = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, _) => GetCandidate(ctx)
)
.Where(static s => s is not null);
// 2⃣ 注册生成输出
context.RegisterSourceOutput(candidates, static (spc, symbol) =>
{
if (symbol != null)
GenerateOutput(spc, symbol);
});
}
#region
private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext context)
{
if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not INamedTypeSymbol symbol)
return null;
// 仅筛选带有 ContextAwareAttribute 的类
var hasAttr = symbol.GetAttributes()
.Any(attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName);
return hasAttr ? symbol : null;
}
#endregion
#region +
private static void GenerateOutput(SourceProductionContext context, INamedTypeSymbol symbol)
{
var syntax = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as ClassDeclarationSyntax;
if (syntax == null)
return;
// 1⃣ 必须是 partial
if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
context.ReportDiagnostic(Diagnostic.Create(
CommonDiagnostics.ClassMustBePartial,
syntax.Identifier.GetLocation(),
symbol.Name
));
return;
}
// 2⃣ 必须实现 IContextAware直接或间接
if (!symbol.AllInterfaces.Any(i => i.ToDisplayString() == "GFramework.Core.rule.IContextAware"))
{
context.ReportDiagnostic(Diagnostic.Create(
ContextAwareDiagnostic.ClassMustImplementIContextAware,
syntax.Identifier.GetLocation(),
symbol.Name
));
return;
}
// 3⃣ 生成源码
var ns = symbol.ContainingNamespace.IsGlobalNamespace
? null
: symbol.ContainingNamespace.ToDisplayString();
var source = GenerateSource(ns, symbol);
context
.AddSource(
$"{symbol.Name}.ContextAware.g.cs",
source);
}
#endregion
#region
private static string GenerateSource(string? ns, INamedTypeSymbol symbol)
{
var sb = new StringBuilder();
sb.AppendLine("// <auto-generated/>");
sb.AppendLine("#nullable enable");
if (ns is not null)
{
sb.AppendLine($"namespace {ns};");
sb.AppendLine();
}
sb.AppendLine($"partial class {symbol.Name}");
sb.AppendLine("{");
sb.AppendLine(
" protected GFramework.Core.architecture.IArchitectureContext Context { get; private set; } = null!;");
sb.AppendLine(" void GFramework.Core.rule.IContextAware.SetContext(");
sb.AppendLine(" GFramework.Core.architecture.IArchitectureContext context)");
sb.AppendLine(" {");
sb.AppendLine(" Context = context;");
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString().TrimEnd();
}
#endregion
}