GFramework/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
GwWuYou eebd7de409 fix(generator): 修复ContextAware生成器的代码生成问题
- 修复生成代码末尾多余的换行符问题
- 添加GFramework.Core.rule命名空间引用
- 在测试中添加框架存根代码以提供必要的接口依赖
- 更新解决方案设置以包含新的分析器测试文件
2025-12-26 23:10:15 +08:00

112 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().TrimEnd();
}
}