GFramework/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
GwWuYou 5f55a1b8db refactor(generator): 将Attributes项目重命名为Abstractions并更新引用
- 将GFramework.SourceGenerators.Attributes重命名为GFramework.SourceGenerators.Abstractions
- 将GFramework.Godot.SourceGenerators.Attributes重命名为GFramework.Godot.SourceGenerators.Abstractions
- 更新所有源生成器中对Attribute命名空间的引用
- 修改项目引用从Attributes指向Abstractions
- 添加程序集打包配置到生成项目
- 更新解决方案文件中的项目引用路径
- 修正测试文件中的命名空间引用
2025-12-27 21:29:13 +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.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.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
}