mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 新增 ContextAwareExtensions 扩展类,提供便捷的上下文访问方法 - 为 IContextAware 接口添加 GetContext 方法以获取架构上下文 - 更新 ContextAwareBase 基类实现 GetContext 方法 - 改进源代码生成器的 Generate 方法参数结构 - 重构 ContextAwareGenerator 生成器实现接口方法自动实现 - 更新单元测试以验证新生成的上下文感知代码正确性
187 lines
5.9 KiB
C#
187 lines
5.9 KiB
C#
using System.Linq;
|
|
using System.Text;
|
|
using GFramework.SourceGenerators.Common.constants;
|
|
using GFramework.SourceGenerators.Common.generator;
|
|
using GFramework.SourceGenerators.diagnostics;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
|
|
namespace GFramework.SourceGenerators.rule;
|
|
|
|
[Generator]
|
|
public sealed class ContextAwareGenerator : MetadataAttributeClassGeneratorBase
|
|
{
|
|
protected override string AttributeMetadataName =>
|
|
$"{PathContests.SourceGeneratorsAbstractionsPath}.rule.ContextAwareAttribute";
|
|
|
|
protected override string AttributeShortNameWithoutSuffix => "ContextAware";
|
|
|
|
protected override bool ValidateSymbol(
|
|
SourceProductionContext context,
|
|
Compilation compilation,
|
|
ClassDeclarationSyntax syntax,
|
|
INamedTypeSymbol symbol,
|
|
AttributeData attr)
|
|
{
|
|
var iContextAware = compilation.GetTypeByMetadataName(
|
|
$"{PathContests.CoreAbstractionsNamespace}.rule.IContextAware");
|
|
|
|
if (iContextAware is null)
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
ContextAwareDiagnostic.ClassMustImplementIContextAware,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.Name));
|
|
return false;
|
|
}
|
|
|
|
if (!symbol.AllInterfaces.Any(i =>
|
|
SymbolEqualityComparer.Default.Equals(i, iContextAware)))
|
|
{
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
ContextAwareDiagnostic.ClassMustImplementIContextAware,
|
|
syntax.Identifier.GetLocation(),
|
|
symbol.Name));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成源代码
|
|
/// </summary>
|
|
/// <param name="context">源生产上下文</param>
|
|
/// <param name="compilation">编译对象</param>
|
|
/// <param name="symbol">命名类型符号</param>
|
|
/// <param name="attr">属性数据</param>
|
|
/// <returns>生成的源代码字符串</returns>
|
|
protected override string Generate(
|
|
SourceProductionContext context,
|
|
Compilation compilation,
|
|
INamedTypeSymbol symbol,
|
|
AttributeData attr)
|
|
{
|
|
var ns = symbol.ContainingNamespace.IsGlobalNamespace
|
|
? null
|
|
: symbol.ContainingNamespace.ToDisplayString();
|
|
var iContextAware = compilation.GetTypeByMetadataName(
|
|
$"{PathContests.CoreAbstractionsNamespace}.rule.IContextAware");
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("// <auto-generated/>");
|
|
sb.AppendLine("#nullable enable");
|
|
sb.AppendLine();
|
|
|
|
if (ns is not null)
|
|
{
|
|
sb.AppendLine($"namespace {ns};");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
sb.AppendLine($"partial class {symbol.Name}");
|
|
sb.AppendLine("{");
|
|
|
|
GenerateContextProperty(sb);
|
|
GenerateInterfaceImplementations(sb, iContextAware!);
|
|
|
|
sb.AppendLine("}");
|
|
return sb.ToString().TrimEnd();
|
|
}
|
|
|
|
protected override string GetHintName(INamedTypeSymbol symbol)
|
|
=> $"{symbol.Name}.ContextAware.g.cs";
|
|
|
|
// =========================
|
|
// 生成 Context 属性
|
|
// =========================
|
|
private static void GenerateContextProperty(StringBuilder sb)
|
|
{
|
|
sb.AppendLine(" /// <summary>");
|
|
sb.AppendLine(" /// 自动注入的架构上下文");
|
|
sb.AppendLine(" /// </summary>");
|
|
sb.AppendLine(
|
|
$" protected {PathContests.CoreAbstractionsNamespace}.architecture.IArchitectureContext Context {{ get; private set; }} = null!;");
|
|
sb.AppendLine();
|
|
}
|
|
|
|
// =========================
|
|
// 自动实现接口方法
|
|
// =========================
|
|
private static void GenerateInterfaceImplementations(
|
|
StringBuilder sb,
|
|
INamedTypeSymbol interfaceSymbol)
|
|
{
|
|
var interfaceFullName = interfaceSymbol.ToDisplayString(
|
|
SymbolDisplayFormat.FullyQualifiedFormat);
|
|
|
|
foreach (var member in interfaceSymbol.GetMembers().OfType<IMethodSymbol>())
|
|
{
|
|
if (member.MethodKind != MethodKind.Ordinary)
|
|
continue;
|
|
|
|
GenerateMethod(sb, interfaceFullName, member);
|
|
sb.AppendLine();
|
|
}
|
|
}
|
|
|
|
private static void GenerateMethod(
|
|
StringBuilder sb,
|
|
string interfaceFullName,
|
|
IMethodSymbol method)
|
|
{
|
|
var returnType = method.ReturnType.ToDisplayString(
|
|
SymbolDisplayFormat.FullyQualifiedFormat);
|
|
|
|
var parameters = string.Join(", ",
|
|
method.Parameters.Select(p =>
|
|
$"{p.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {p.Name}"));
|
|
|
|
sb.AppendLine(
|
|
$" {returnType} {interfaceFullName}.{method.Name}({parameters})");
|
|
|
|
sb.AppendLine(" {");
|
|
GenerateMethodBody(sb, method);
|
|
sb.AppendLine(" }");
|
|
}
|
|
|
|
// =========================
|
|
// 方法语义策略
|
|
// =========================
|
|
private static void GenerateMethodBody(
|
|
StringBuilder sb,
|
|
IMethodSymbol method)
|
|
{
|
|
switch (method.Name)
|
|
{
|
|
case "SetContext":
|
|
sb.AppendLine(" Context = context;");
|
|
break;
|
|
|
|
case "GetContext":
|
|
sb.AppendLine(" return Context;");
|
|
break;
|
|
|
|
default:
|
|
GenerateFallbackBody(sb, method);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void GenerateFallbackBody(
|
|
StringBuilder sb,
|
|
IMethodSymbol method)
|
|
{
|
|
if (method.ReturnsVoid)
|
|
{
|
|
sb.AppendLine(" // no-op");
|
|
}
|
|
else
|
|
{
|
|
sb.AppendLine(
|
|
$" throw new System.NotImplementedException(");
|
|
sb.AppendLine(
|
|
$" \"Method '{method.Name}' is not supported by ContextAwareGenerator.\");");
|
|
}
|
|
}
|
|
} |