From 330bd8b0b067761174368d7e393b068da2f33301 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Thu, 5 Mar 2026 22:18:30 +0800
Subject: [PATCH] =?UTF-8?q?feat(generator):=20=E6=B7=BB=E5=8A=A0=E4=BC=98?=
=?UTF-8?q?=E5=85=88=E7=BA=A7=E6=BA=90=E4=BB=A3=E7=A0=81=E7=94=9F=E6=88=90?=
=?UTF-8?q?=E5=99=A8=E5=92=8C=E7=9B=B8=E5=85=B3=E5=88=86=E6=9E=90=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 PriorityGenerator 源生成器,自动生成 IPrioritized 接口实现
- 添加 PriorityAttribute 特性,用于标记类的优先级值
- 实现 PriorityUsageAnalyzer 分析器,检测优先级使用建议
- 添加预定义的 PriorityGroup 常量,提供标准优先级分组
- 在 AnalyzerReleases.Unshipped.md 中注册新的诊断规则
- 更新项目依赖,升级 Meziantou.Analyzer 和 Polyfill 版本
- 为测试项目添加源生成器项目引用
- 添加 PriorityGenerator 的快照测试用例
---
.../bases/PriorityGroup.cs | 71 +++++
.../GFramework.Core.Tests.csproj | 3 +
.../bases/PriorityAttribute.cs | 35 +++
.../GFramework.SourceGenerators.Common.csproj | 1 +
.../bases/PriorityGeneratorSnapshotTests.cs | 266 ++++++++++++++++++
.../AnalyzerReleases.Unshipped.md | 13 +-
.../analyzers/PriorityUsageAnalyzer.cs | 129 +++++++++
.../bases/PriorityGenerator.cs | 134 +++++++++
.../diagnostics/PriorityDiagnostic.cs | 63 +++++
9 files changed, 711 insertions(+), 4 deletions(-)
create mode 100644 GFramework.Core.Abstractions/bases/PriorityGroup.cs
create mode 100644 GFramework.SourceGenerators.Abstractions/bases/PriorityAttribute.cs
create mode 100644 GFramework.SourceGenerators.Tests/bases/PriorityGeneratorSnapshotTests.cs
create mode 100644 GFramework.SourceGenerators/analyzers/PriorityUsageAnalyzer.cs
create mode 100644 GFramework.SourceGenerators/bases/PriorityGenerator.cs
create mode 100644 GFramework.SourceGenerators/diagnostics/PriorityDiagnostic.cs
diff --git a/GFramework.Core.Abstractions/bases/PriorityGroup.cs b/GFramework.Core.Abstractions/bases/PriorityGroup.cs
new file mode 100644
index 0000000..cee876c
--- /dev/null
+++ b/GFramework.Core.Abstractions/bases/PriorityGroup.cs
@@ -0,0 +1,71 @@
+namespace GFramework.Core.Abstractions.bases;
+
+///
+/// 预定义的优先级分组常量
+///
+///
+/// 提供标准化的优先级值,用于统一管理系统、服务等组件的执行顺序。
+/// 优先级值越小,优先级越高(负数表示高优先级)。
+///
+public static class PriorityGroup
+{
+ ///
+ /// 关键优先级 - 最高优先级,用于核心系统和基础设施
+ ///
+ ///
+ /// 适用场景:
+ /// - 日志系统
+ /// - 配置管理
+ /// - IoC 容器初始化
+ /// - 架构核心组件
+ ///
+ public const int Critical = -100;
+
+ ///
+ /// 高优先级 - 用于重要但非核心的系统
+ ///
+ ///
+ /// 适用场景:
+ /// - 事件总线
+ /// - 资源管理器
+ /// - 输入系统
+ /// - 网络管理器
+ ///
+ public const int High = -50;
+
+ ///
+ /// 普通优先级 - 默认优先级
+ ///
+ ///
+ /// 适用场景:
+ /// - 游戏逻辑系统
+ /// - UI 系统
+ /// - 音频系统
+ /// - 大部分业务逻辑
+ ///
+ public const int Normal = 0;
+
+ ///
+ /// 低优先级 - 用于非关键系统
+ ///
+ ///
+ /// 适用场景:
+ /// - 统计系统
+ /// - 调试工具
+ /// - 性能监控
+ /// - 辅助功能
+ ///
+ public const int Low = 50;
+
+ ///
+ /// 延迟优先级 - 最低优先级,用于可延迟执行的系统
+ ///
+ ///
+ /// 适用场景:
+ /// - 分析和遥测
+ /// - 后台数据同步
+ /// - 缓存清理
+ /// - 非紧急任务
+ ///
+ public const int Deferred = 100;
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/GFramework.Core.Tests.csproj b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
index bd51047..eed0620 100644
--- a/GFramework.Core.Tests/GFramework.Core.Tests.csproj
+++ b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
@@ -19,6 +19,9 @@
+
+
+
diff --git a/GFramework.SourceGenerators.Abstractions/bases/PriorityAttribute.cs b/GFramework.SourceGenerators.Abstractions/bases/PriorityAttribute.cs
new file mode 100644
index 0000000..8c2d399
--- /dev/null
+++ b/GFramework.SourceGenerators.Abstractions/bases/PriorityAttribute.cs
@@ -0,0 +1,35 @@
+namespace GFramework.SourceGenerators.Abstractions.bases;
+
+///
+/// 标记类的优先级,自动生成 接口实现
+///
+///
+/// 使用此特性可以避免手动实现 IPrioritized 接口。
+/// 优先级值越小,优先级越高(负数表示高优先级)。
+///
+///
+///
+/// [Priority(10)]
+/// public partial class MySystem : AbstractSystem
+/// {
+/// // 自动生成: public int Priority => 10;
+/// }
+///
+///
+[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+public sealed class PriorityAttribute : Attribute
+{
+ ///
+ /// 初始化 类的新实例
+ ///
+ /// 优先级值,越小优先级越高
+ public PriorityAttribute(int value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// 获取优先级值
+ ///
+ public int Value { get; }
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
index 91eb394..52e5487 100644
--- a/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
+++ b/GFramework.SourceGenerators.Common/GFramework.SourceGenerators.Common.csproj
@@ -30,6 +30,7 @@
all
runtime; build; native; contentfiles; analyzers
+
diff --git a/GFramework.SourceGenerators.Tests/bases/PriorityGeneratorSnapshotTests.cs b/GFramework.SourceGenerators.Tests/bases/PriorityGeneratorSnapshotTests.cs
new file mode 100644
index 0000000..c5c7c56
--- /dev/null
+++ b/GFramework.SourceGenerators.Tests/bases/PriorityGeneratorSnapshotTests.cs
@@ -0,0 +1,266 @@
+using System.IO;
+using GFramework.SourceGenerators.bases;
+using GFramework.SourceGenerators.Tests.core;
+using NUnit.Framework;
+
+namespace GFramework.SourceGenerators.Tests.bases;
+
+///
+/// Priority 生成器快照测试类
+///
+[TestFixture]
+public class PriorityGeneratorSnapshotTests
+{
+ ///
+ /// 测试基本的 Priority 特性生成
+ ///
+ [Test]
+ public async Task Snapshot_BasicPriority()
+ {
+ const string source = """
+ using System;
+
+ namespace GFramework.SourceGenerators.Abstractions.bases
+ {
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PriorityAttribute : Attribute
+ {
+ public int Value { get; }
+ public PriorityAttribute(int value) { Value = value; }
+ }
+ }
+
+ namespace GFramework.Core.Abstractions.bases
+ {
+ public interface IPrioritized
+ {
+ int Priority { get; }
+ }
+ }
+
+ namespace TestApp
+ {
+ using GFramework.SourceGenerators.Abstractions.bases;
+
+ [Priority(10)]
+ public partial class MySystem
+ {
+ }
+ }
+ """;
+
+ await GeneratorSnapshotTest.RunAsync(
+ source,
+ Path.Combine(
+ TestContext.CurrentContext.TestDirectory,
+ "bases",
+ "snapshots",
+ "PriorityGenerator",
+ "BasicPriority"));
+ }
+
+ ///
+ /// 测试负数优先级
+ ///
+ [Test]
+ public async Task Snapshot_NegativePriority()
+ {
+ const string source = """
+ using System;
+
+ namespace GFramework.SourceGenerators.Abstractions.bases
+ {
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PriorityAttribute : Attribute
+ {
+ public int Value { get; }
+ public PriorityAttribute(int value) { Value = value; }
+ }
+ }
+
+ namespace GFramework.Core.Abstractions.bases
+ {
+ public interface IPrioritized
+ {
+ int Priority { get; }
+ }
+ }
+
+ namespace TestApp
+ {
+ using GFramework.SourceGenerators.Abstractions.bases;
+
+ [Priority(-100)]
+ public partial class CriticalSystem
+ {
+ }
+ }
+ """;
+
+ await GeneratorSnapshotTest.RunAsync(
+ source,
+ Path.Combine(
+ TestContext.CurrentContext.TestDirectory,
+ "bases",
+ "snapshots",
+ "PriorityGenerator",
+ "NegativePriority"));
+ }
+
+ ///
+ /// 测试使用 PriorityGroup 枚举
+ ///
+ [Test]
+ public async Task Snapshot_PriorityGroup()
+ {
+ const string source = """
+ using System;
+
+ namespace GFramework.SourceGenerators.Abstractions.bases
+ {
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PriorityAttribute : Attribute
+ {
+ public int Value { get; }
+ public PriorityAttribute(int value) { Value = value; }
+ }
+ }
+
+ namespace GFramework.Core.Abstractions.bases
+ {
+ public interface IPrioritized
+ {
+ int Priority { get; }
+ }
+
+ public static class PriorityGroup
+ {
+ public const int Critical = -100;
+ public const int High = -50;
+ public const int Normal = 0;
+ public const int Low = 50;
+ public const int Deferred = 100;
+ }
+ }
+
+ namespace TestApp
+ {
+ using GFramework.SourceGenerators.Abstractions.bases;
+ using GFramework.Core.Abstractions.bases;
+
+ [Priority(PriorityGroup.High)]
+ public partial class HighPrioritySystem
+ {
+ }
+ }
+ """;
+
+ await GeneratorSnapshotTest.RunAsync(
+ source,
+ Path.Combine(
+ TestContext.CurrentContext.TestDirectory,
+ "bases",
+ "snapshots",
+ "PriorityGenerator",
+ "PriorityGroup"));
+ }
+
+ ///
+ /// 测试泛型类支持
+ ///
+ [Test]
+ public async Task Snapshot_GenericClass()
+ {
+ const string source = """
+ using System;
+
+ namespace GFramework.SourceGenerators.Abstractions.bases
+ {
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PriorityAttribute : Attribute
+ {
+ public int Value { get; }
+ public PriorityAttribute(int value) { Value = value; }
+ }
+ }
+
+ namespace GFramework.Core.Abstractions.bases
+ {
+ public interface IPrioritized
+ {
+ int Priority { get; }
+ }
+ }
+
+ namespace TestApp
+ {
+ using GFramework.SourceGenerators.Abstractions.bases;
+
+ [Priority(20)]
+ public partial class GenericSystem
+ {
+ }
+ }
+ """;
+
+ await GeneratorSnapshotTest.RunAsync(
+ source,
+ Path.Combine(
+ TestContext.CurrentContext.TestDirectory,
+ "bases",
+ "snapshots",
+ "PriorityGenerator",
+ "GenericClass"));
+ }
+
+ ///
+ /// 测试嵌套类支持
+ ///
+ [Test]
+ public async Task Snapshot_NestedClass()
+ {
+ const string source = """
+ using System;
+
+ namespace GFramework.SourceGenerators.Abstractions.bases
+ {
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
+ public sealed class PriorityAttribute : Attribute
+ {
+ public int Value { get; }
+ public PriorityAttribute(int value) { Value = value; }
+ }
+ }
+
+ namespace GFramework.Core.Abstractions.bases
+ {
+ public interface IPrioritized
+ {
+ int Priority { get; }
+ }
+ }
+
+ namespace TestApp
+ {
+ using GFramework.SourceGenerators.Abstractions.bases;
+
+ public class OuterClass
+ {
+ [Priority(30)]
+ public partial class NestedSystem
+ {
+ }
+ }
+ }
+ """;
+
+ await GeneratorSnapshotTest.RunAsync(
+ source,
+ Path.Combine(
+ TestContext.CurrentContext.TestDirectory,
+ "bases",
+ "snapshots",
+ "PriorityGenerator",
+ "NestedClass"));
+ }
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
index 4cf23a9..ce9dd59 100644
--- a/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
+++ b/GFramework.SourceGenerators/AnalyzerReleases.Unshipped.md
@@ -3,7 +3,12 @@
### New Rules
- 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
+ Rule ID | Category | Severity | Notes
+-----------------------|----------------------------------|----------|------------------------
+ GF_Logging_001 | GFramework.Godot.logging | Warning | LoggerDiagnostics
+ GF_Rule_001 | GFramework.SourceGenerators.rule | Error | ContextAwareDiagnostic
+ GF_Priority_001 | GFramework.Priority | Error | PriorityDiagnostic
+ GF_Priority_002 | GFramework.Priority | Warning | PriorityDiagnostic
+ GF_Priority_003 | GFramework.Priority | Error | PriorityDiagnostic
+ GF_Priority_004 | GFramework.Priority | Error | PriorityDiagnostic
+ GF_Priority_Usage_001 | GFramework.Usage | Info | PriorityUsageAnalyzer
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/analyzers/PriorityUsageAnalyzer.cs b/GFramework.SourceGenerators/analyzers/PriorityUsageAnalyzer.cs
new file mode 100644
index 0000000..37d82a7
--- /dev/null
+++ b/GFramework.SourceGenerators/analyzers/PriorityUsageAnalyzer.cs
@@ -0,0 +1,129 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+
+namespace GFramework.SourceGenerators.analyzers;
+
+///
+/// 优先级使用分析器,检测应该使用 GetAllByPriority 而非 GetAll 的场景
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class PriorityUsageAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ /// 诊断 ID
+ ///
+ private const string DiagnosticId = "GF_Priority_Usage_001";
+
+ ///
+ /// 诊断规则
+ ///
+ private static readonly DiagnosticDescriptor Rule = new(
+ id: DiagnosticId,
+ title: "建议使用 GetAllByPriority",
+ messageFormat: "类型 '{0}' 实现了 IPrioritized 接口,建议使用 GetAllByPriority<{0}>() 而非 GetAll<{0}>()",
+ category: "GFramework.Usage",
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true,
+ description: "当获取实现了 IPrioritized 接口的服务时,应使用 GetAllByPriority 方法以确保按优先级排序。");
+
+ ///
+ /// 支持的诊断规则
+ ///
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ ///
+ /// 初始化分析器
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(compilationContext =>
+ {
+ // 缓存符号查找
+ var iPrioritized = compilationContext.Compilation.GetTypeByMetadataName(
+ "GFramework.Core.Abstractions.bases.IPrioritized");
+
+ if (iPrioritized == null)
+ return;
+
+ var iocContainer = compilationContext.Compilation.GetTypeByMetadataName(
+ "GFramework.Core.Abstractions.ioc.IIocContainer");
+
+ var architectureContext = compilationContext.Compilation.GetTypeByMetadataName(
+ "GFramework.Core.Abstractions.architecture.IArchitectureContext");
+
+ compilationContext.RegisterOperationAction(
+ operationContext => AnalyzeInvocation(
+ operationContext,
+ iPrioritized,
+ iocContainer,
+ architectureContext),
+ OperationKind.Invocation);
+ });
+ }
+
+ ///
+ /// 分析方法调用
+ ///
+ private static void AnalyzeInvocation(
+ OperationAnalysisContext context,
+ INamedTypeSymbol iPrioritized,
+ INamedTypeSymbol? iocContainer,
+ INamedTypeSymbol? architectureContext)
+ {
+ var invocation = (IInvocationOperation)context.Operation;
+ var method = invocation.TargetMethod;
+
+ // 检查方法名是否为 GetAll
+ if (method.Name != "GetAll")
+ return;
+
+ // 检查是否为泛型方法
+ if (!method.IsGenericMethod || method.TypeArguments.Length != 1)
+ return;
+
+ // 检查方法来源
+ var containingType = method.ContainingType;
+ if (iocContainer != null && SymbolEqualityComparer.Default.Equals(containingType, iocContainer))
+ {
+ // 来自 IIocContainer
+ }
+ else if (architectureContext != null &&
+ SymbolEqualityComparer.Default.Equals(containingType, architectureContext))
+ {
+ // 来自 IArchitectureContext
+ }
+ else
+ {
+ return;
+ }
+
+ // 检查泛型参数是否实现了 IPrioritized
+ var typeArgument = method.TypeArguments[0];
+ if (typeArgument is not INamedTypeSymbol namedType)
+ return;
+
+ if (!ImplementsInterface(namedType, iPrioritized))
+ return;
+
+ // 报告诊断
+ var diagnostic = Diagnostic.Create(
+ Rule,
+ invocation.Syntax.GetLocation(),
+ typeArgument.ToDisplayString());
+
+ context.ReportDiagnostic(diagnostic);
+ }
+
+ ///
+ /// 检查类型是否实现了指定接口
+ ///
+ private static bool ImplementsInterface(INamedTypeSymbol type, INamedTypeSymbol interfaceType)
+ {
+ return type.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, interfaceType));
+ }
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/bases/PriorityGenerator.cs b/GFramework.SourceGenerators/bases/PriorityGenerator.cs
new file mode 100644
index 0000000..a509347
--- /dev/null
+++ b/GFramework.SourceGenerators/bases/PriorityGenerator.cs
@@ -0,0 +1,134 @@
+using System.Text;
+using GFramework.SourceGenerators.Common.constants;
+using GFramework.SourceGenerators.Common.generator;
+using GFramework.SourceGenerators.diagnostics;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace GFramework.SourceGenerators.bases;
+
+///
+/// Priority 特性生成器,为标记了 [Priority] 的类自动生成 IPrioritized 接口实现
+///
+[Generator]
+public sealed class PriorityGenerator : MetadataAttributeClassGeneratorBase
+{
+ ///
+ /// 获取特性的元数据名称
+ ///
+ protected override string AttributeMetadataName =>
+ $"{PathContests.SourceGeneratorsAbstractionsPath}.bases.PriorityAttribute";
+
+ ///
+ /// 获取特性的短名称(不包含后缀)
+ ///
+ protected override string AttributeShortNameWithoutSuffix => "Priority";
+
+ ///
+ /// 验证符号是否符合生成条件
+ ///
+ protected override bool ValidateSymbol(
+ SourceProductionContext context,
+ Compilation compilation,
+ ClassDeclarationSyntax syntax,
+ INamedTypeSymbol symbol,
+ AttributeData attr)
+ {
+ // 1. 必须是 class
+ if (symbol.TypeKind != TypeKind.Class)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ PriorityDiagnostic.OnlyApplyToClass,
+ syntax.Identifier.GetLocation(),
+ symbol.ToDisplayString()));
+ return false;
+ }
+
+ // 2. 必须是 partial
+ if (!syntax.Modifiers.Any(SyntaxKind.PartialKeyword))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ PriorityDiagnostic.MustBePartial,
+ syntax.Identifier.GetLocation(),
+ symbol.Name));
+ return false;
+ }
+
+ // 3. 检查是否已手动实现 IPrioritized
+ var iPrioritized = compilation.GetTypeByMetadataName(
+ $"{PathContests.CoreAbstractionsNamespace}.bases.IPrioritized");
+
+ if (iPrioritized != null && symbol.AllInterfaces.Contains(iPrioritized, SymbolEqualityComparer.Default))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ PriorityDiagnostic.AlreadyImplemented,
+ syntax.Identifier.GetLocation(),
+ symbol.Name));
+ return false;
+ }
+
+ // 4. 验证特性参数
+ if (attr.ConstructorArguments.Length == 0 ||
+ attr.ConstructorArguments[0].Value is not int)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ PriorityDiagnostic.InvalidValue,
+ syntax.Identifier.GetLocation()));
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// 生成源代码
+ ///
+ protected override string Generate(
+ SourceProductionContext context,
+ Compilation compilation,
+ INamedTypeSymbol symbol,
+ AttributeData attr)
+ {
+ var ns = symbol.ContainingNamespace.IsGlobalNamespace
+ ? null
+ : symbol.ContainingNamespace.ToDisplayString();
+
+ var priorityValue = (int)attr.ConstructorArguments[0].Value!;
+
+ var sb = new StringBuilder();
+ sb.AppendLine("// ");
+ sb.AppendLine("#nullable enable");
+ sb.AppendLine();
+
+ if (ns is not null)
+ {
+ sb.AppendLine($"namespace {ns};");
+ sb.AppendLine();
+ }
+
+ // 生成泛型参数(如果有)
+ var typeParameters = symbol.TypeParameters.Length > 0
+ ? $"<{string.Join(", ", symbol.TypeParameters.Select(tp => tp.Name))}>"
+ : string.Empty;
+
+ sb.AppendLine(
+ $"partial class {symbol.Name}{typeParameters} : global::GFramework.Core.Abstractions.bases.IPrioritized");
+ sb.AppendLine("{");
+ sb.AppendLine(" /// ");
+ sb.AppendLine($" /// 获取优先级值: {priorityValue}");
+ sb.AppendLine(" /// ");
+ sb.AppendLine($" public int Priority => {priorityValue};");
+ sb.AppendLine("}");
+
+ return sb.ToString().TrimEnd();
+ }
+
+ ///
+ /// 获取生成文件的提示名称
+ ///
+ protected override string GetHintName(INamedTypeSymbol symbol)
+ {
+ return $"{symbol.Name}.Priority.g.cs";
+ }
+}
\ No newline at end of file
diff --git a/GFramework.SourceGenerators/diagnostics/PriorityDiagnostic.cs b/GFramework.SourceGenerators/diagnostics/PriorityDiagnostic.cs
new file mode 100644
index 0000000..f9fb2d7
--- /dev/null
+++ b/GFramework.SourceGenerators/diagnostics/PriorityDiagnostic.cs
@@ -0,0 +1,63 @@
+using Microsoft.CodeAnalysis;
+
+namespace GFramework.SourceGenerators.diagnostics;
+
+///
+/// Priority 特性相关的诊断信息
+///
+internal static class PriorityDiagnostic
+{
+ private const string Category = "GFramework.Priority";
+
+ ///
+ /// GF_Priority_001: Priority 特性只能应用于类
+ ///
+ public static readonly DiagnosticDescriptor OnlyApplyToClass = new(
+ id: "GF_Priority_001",
+ title: "Priority 特性只能应用于类",
+ messageFormat: "Priority 特性只能应用于类,不能应用于 '{0}'",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: "Priority 特性设计用于类级别的优先级标记,不支持其他类型。"
+ );
+
+ ///
+ /// GF_Priority_002: 类已手动实现 IPrioritized 接口
+ ///
+ public static readonly DiagnosticDescriptor AlreadyImplemented = new(
+ id: "GF_Priority_002",
+ title: "类已实现 IPrioritized 接口",
+ messageFormat: "类 '{0}' 已手动实现 IPrioritized 接口,将跳过自动生成",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "当类已经手动实现 IPrioritized 接口时,源生成器将跳过代码生成以避免冲突。"
+ );
+
+ ///
+ /// GF_Priority_003: 类必须声明为 partial
+ ///
+ public static readonly DiagnosticDescriptor MustBePartial = new(
+ id: "GF_Priority_003",
+ title: "类必须声明为 partial",
+ messageFormat: "类 '{0}' 使用了 Priority 特性,必须声明为 partial",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: "源生成器需要在 partial 类中生成 IPrioritized 接口实现。"
+ );
+
+ ///
+ /// GF_Priority_004: Priority 值缺失或无效
+ ///
+ public static readonly DiagnosticDescriptor InvalidValue = new(
+ id: "GF_Priority_004",
+ title: "Priority 值无效",
+ messageFormat: "Priority 特性的值无效或缺失",
+ category: Category,
+ defaultSeverity: DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description: "Priority 特性必须提供一个有效的整数值。"
+ );
+}
\ No newline at end of file