using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace GFramework.SourceGenerators.Common.generator;
///
/// 属性枚举生成器基类,用于基于特定属性的枚举进行源代码生成
///
public abstract class AttributeEnumGeneratorBase : IIncrementalGenerator
{
///
/// 获取属性的短名称(不包含后缀)
///
protected abstract string AttributeShortNameWithoutSuffix { get; }
///
/// 初始化增量生成器
///
/// 增量生成器初始化上下文
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 创建语法提供程序,查找带有指定属性的枚举声明
var candidates = context.SyntaxProvider.CreateSyntaxProvider(
(node, _) =>
node is EnumDeclarationSyntax eds &&
eds.AttributeLists
.SelectMany(a => a.Attributes)
.Any(a => a.Name.ToString()
.Contains(AttributeShortNameWithoutSuffix)),
(ctx, _) =>
{
var syntax = (EnumDeclarationSyntax)ctx.Node;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(syntax, cancellationToken: _) as INamedTypeSymbol;
return (syntax, symbol);
})
.Where(x => x.symbol is not null);
var combined = candidates.Combine(context.CompilationProvider);
// 注册源输出,生成最终的源代码
context.RegisterSourceOutput(combined, (spc, pair) =>
{
var ((syntax, symbol), compilation) = pair;
var attr = ResolveAttribute(compilation, symbol!);
if (attr is null)
return;
if (!ValidateSymbol(spc, compilation, syntax, symbol!, attr))
return;
spc.AddSource(
GetHintName(symbol!),
Generate(symbol!, attr));
});
}
///
/// 解析指定符号上的属性数据
///
/// 编译对象
/// 命名类型符号
/// 属性数据对象,如果未找到则返回null
protected abstract AttributeData? ResolveAttribute(
Compilation compilation,
INamedTypeSymbol symbol);
///
/// 验证符号是否符合生成要求
///
/// 源生产上下文
/// 编译对象
/// 枚举声明语法节点
/// 命名类型符号
/// 属性数据
/// 验证是否通过
protected abstract bool ValidateSymbol(
SourceProductionContext context,
Compilation compilation,
EnumDeclarationSyntax syntax,
INamedTypeSymbol symbol,
AttributeData attr);
///
/// 生成源代码
///
/// 命名类型符号
/// 属性数据
/// 生成的源代码字符串
protected abstract string Generate(
INamedTypeSymbol symbol,
AttributeData attr);
///
/// 获取生成文件的提示名称
///
/// 命名类型符号
/// 生成文件的提示名称
protected virtual string GetHintName(INamedTypeSymbol symbol)
=> $"{symbol.Name}.g.cs";
}