GFramework/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
GwWuYou eaa706a7e4 refactor(source-generator): 重构ContextAware生成器的代码格式
- 将多行字符串插值改为逐行添加以改善代码可读性
- 更新测试用例中的源代码和期望输出格式
- 修改项目文件中的目标框架为net8.0
- 添加必要的包引用Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Common
- 移除解决方案用户设置中的过期配置项
2025-12-26 22:51:57 +08:00

113 lines
3.4 KiB
C#

using System.Linq;
using System.Text;
using GFramework.SourceGenerators.Common.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.Attributes.rule.ContextAwareAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 1. 找到所有 class 声明
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, _) => GetCandidate(ctx)
)
.Where(static s => s is not null);
// 2. 生成代码
context.RegisterSourceOutput(
classDeclarations,
static (spc, source) => Generate(spc, source!)
);
}
private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext context)
{
var classDecl = (ClassDeclarationSyntax)context.Node;
if (classDecl.AttributeLists.Count == 0)
return null;
if (context.SemanticModel.GetDeclaredSymbol(classDecl)
is not { } symbol)
return null;
return Enumerable.Any(symbol.GetAttributes(),
attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName)
? symbol
: null;
}
private static void Generate(
SourceProductionContext context,
INamedTypeSymbol symbol)
{
var syntax = symbol.DeclaringSyntaxReferences
.FirstOrDefault()?
.GetSyntax() as ClassDeclarationSyntax;
if (syntax is null || !syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
context.ReportDiagnostic(
Diagnostic.Create(
CommonDiagnostics.ClassMustBePartial,
syntax?.Identifier.GetLocation(),
symbol.Name
)
);
return;
}
var ns = symbol.ContainingNamespace.IsGlobalNamespace
? null
: symbol.ContainingNamespace.ToDisplayString();
var source = GenerateSource(ns, symbol);
context.AddSource(
$"{symbol.Name}.ContextAware.g.cs",
source
);
}
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} : GFramework.Core.rule.IContextAware");
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();
}
}