diff --git a/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs b/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs
index dec09f2..ba1b837 100644
--- a/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs
+++ b/GFramework.SourceGenerators.Tests/rule/ContextAwareGeneratorTests.cs
@@ -42,14 +42,6 @@ public class ContextAwareGeneratorTests
""";
const string frameworkStub = """
- namespace GFramework.Core.rule
- {
- public interface IContextAware
- {
- void SetContext(GFramework.Core.architecture.IArchitectureContext context);
- }
- }
-
namespace GFramework.Core.architecture
{
public interface IArchitectureContext {}
@@ -61,7 +53,7 @@ public class ContextAwareGeneratorTests
#nullable enable
namespace TestApp;
- partial class MyRule : GFramework.Core.rule.IContextAware
+ partial class MyRule
{
protected GFramework.Core.architecture.IArchitectureContext Context { get; private set; } = null!;
void GFramework.Core.rule.IContextAware.SetContext(
diff --git a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
index 0ba5463..db19379 100644
--- a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
+++ b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
@@ -3,6 +3,7 @@
### New Rules
- Rule ID | Category | Severity | Notes
-----------------|--------------------------|----------|-------------------
- GF_Logging_001 | GFramework.Godot.Logging | Warning | LoggerDiagnostics
\ No newline at end of file
+ Rule ID | Category | Severity | Notes
+----------------|----------------------------------|----------|------------------------
+ GF_Logging_001 | GFramework.Godot.Logging | Warning | LoggerDiagnostics
+ GF_Rule_001 | GFramework.SourceGenerators.rule | Error | ContextAwareDiagnostic
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs b/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
index 8a2d945..743ddc9 100644
--- a/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
+++ b/GFramework.SourceGenerators/logging/LoggerDiagnostic.cs
@@ -15,7 +15,7 @@ internal static class LoggerDiagnostics
"GF_Logging_001",
"LogAttribute cannot generate Logger",
"LogAttribute on class '{0}' is ineffective: {1}",
- "GFramework.Godot.Logging",
+ "GFramework.Godot.logging",
DiagnosticSeverity.Warning,
true);
}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/rule/ContextAwareDiagnostic.cs b/GFramework.SourceGenerators/rule/ContextAwareDiagnostic.cs
new file mode 100644
index 0000000..fcf58ff
--- /dev/null
+++ b/GFramework.SourceGenerators/rule/ContextAwareDiagnostic.cs
@@ -0,0 +1,28 @@
+using Microsoft.CodeAnalysis;
+
+namespace GFramework.SourceGenerators.rule;
+
+///
+/// 提供与上下文感知相关的诊断规则定义
+///
+public static class ContextAwareDiagnostic
+{
+ ///
+ /// 定义类必须实现IContextAware接口的诊断规则
+ ///
+ ///
+ /// 诊断ID: GF_Rule_001
+ /// 诊断类别: GFramework.SourceGenerators.rule
+ /// 严重级别: 错误
+ /// 启用状态: true
+ /// 消息格式: "Class '{0}' must implement IContextAware"
+ ///
+ public static readonly DiagnosticDescriptor ClassMustImplementIContextAware = new(
+ "GF_Rule_001",
+ "Class must implement IContextAware",
+ "Class '{0}' must implement IContextAware",
+ "GFramework.SourceGenerators.rule",
+ DiagnosticSeverity.Error,
+ true
+ );
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
index 0f005c9..180e823 100644
--- a/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
+++ b/GFramework.SourceGenerators/rule/ContextAwareGenerator.cs
@@ -15,75 +15,87 @@ public sealed class ContextAwareGenerator : IIncrementalGenerator
public void Initialize(IncrementalGeneratorInitializationContext context)
{
- // 1. 找到所有 class 声明
- var classDeclarations = context.SyntaxProvider
+ // 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(
- classDeclarations,
- static (spc, source) => Generate(spc, source!)
- );
+ // 2️⃣ 注册生成输出
+ context.RegisterSourceOutput(candidates, static (spc, symbol) =>
+ {
+ if (symbol != null)
+ GenerateOutput(spc, symbol);
+ });
}
+ #region 候选类查找
+
private static INamedTypeSymbol? GetCandidate(GeneratorSyntaxContext context)
{
- var classDecl = (ClassDeclarationSyntax)context.Node;
-
- if (classDecl.AttributeLists.Count == 0)
+ if (context.SemanticModel.GetDeclaredSymbol(context.Node) is not INamedTypeSymbol symbol)
return null;
- if (context.SemanticModel.GetDeclaredSymbol(classDecl)
- is not { } symbol)
- return null;
+ // 仅筛选带有 ContextAwareAttribute 的类
+ var hasAttr = symbol.GetAttributes()
+ .Any(attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName);
- return Enumerable.Any(symbol.GetAttributes(),
- attr => attr.AttributeClass?.ToDisplayString() == AttributeMetadataName)
- ? symbol
- : null;
+ return hasAttr ? symbol : null;
}
+ #endregion
- private static void Generate(
- SourceProductionContext context,
- INamedTypeSymbol symbol)
+ #region 输出生成 + 诊断
+
+ private static void GenerateOutput(SourceProductionContext context, INamedTypeSymbol symbol)
{
- var syntax = symbol.DeclaringSyntaxReferences
- .FirstOrDefault()?
- .GetSyntax() as ClassDeclarationSyntax;
+ var syntax = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as ClassDeclarationSyntax;
+ if (syntax == null)
+ return;
- if (syntax is null || !syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
+ // 1️⃣ 必须是 partial
+ if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
- context.ReportDiagnostic(
- Diagnostic.Create(
- CommonDiagnostics.ClassMustBePartial,
- syntax?.Identifier.GetLocation(),
- symbol.Name
- )
- );
+ 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
- );
+ context
+ .AddSource(
+ $"{symbol.Name}.ContextAware.g.cs",
+ source);
}
+ #endregion
+
+ #region 源码生成
+
private static string GenerateSource(string? ns, INamedTypeSymbol symbol)
{
var sb = new StringBuilder();
-
sb.AppendLine("// ");
sb.AppendLine("#nullable enable");
@@ -93,20 +105,20 @@ public sealed class ContextAwareGenerator : IIncrementalGenerator
sb.AppendLine();
}
- sb.AppendLine($"partial class {symbol.Name} : GFramework.Core.rule.IContextAware");
+ sb.AppendLine($"partial class {symbol.Name}");
sb.AppendLine("{");
-
sb.AppendLine(
" protected GFramework.Core.architecture.IArchitectureContext Context { get; private set; } = null!;");
-
+ sb.AppendLine();
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
}
\ No newline at end of file
diff --git a/GFramework.sln.DotSettings.user b/GFramework.sln.DotSettings.user
index 51ff59d..ed71e26 100644
--- a/GFramework.sln.DotSettings.user
+++ b/GFramework.sln.DotSettings.user
@@ -1,11 +1,17 @@
ForceIncluded
ForceIncluded
+ ForceIncluded
ForceIncluded
ForceIncluded
ForceIncluded
- <SessionState ContinuousTestingMode="0" IsActive="True" Name="Generates_ContextAware_Code" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <SessionState ContinuousTestingMode="0" Name="Generates_ContextAware_Code" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
<TestAncestor>
<TestId>NUnit3x::BB047F43-6AA0-4EA0-8AE9-E6B9784D9E8E::net8.0::GFramework.SourceGenerators.Tests.rule.ContextAwareGeneratorTests</TestId>
</TestAncestor>
+</SessionState>
+ <SessionState ContinuousTestingMode="0" IsActive="True" Name="Generates_ContextAware_Code" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <TestAncestor>
+ <TestId>NUnit3x::BB047F43-6AA0-4EA0-8AE9-E6B9784D9E8E::net8.0::GFramework.SourceGenerators.Tests.rule.ContextAwareGeneratorTests.Generates_ContextAware_Code</TestId>
+ </TestAncestor>
</SessionState>
\ No newline at end of file