GeWuYou 5b996d8618 feat(generator): 添加 BindNodeSignal 源生成器实现
- 实现 BindNodeSignalGenerator 源生成器,用于自动生成 Godot 节点事件绑定与解绑逻辑
- 添加 BindNodeSignalAttribute 特性,标记需要生成绑定逻辑的事件处理方法
- 实现完整的诊断系统,包括嵌套类型、静态方法、字段类型等错误检查
- 添加生命周期方法调用检查,在 _Ready 和 _ExitTree 中验证生成方法的调用
- 支持方法签名与事件委托的兼容性验证
- 实现单元测试覆盖各种使用场景和错误情况
2026-03-31 09:39:06 +08:00

117 lines
4.3 KiB
C#

using GFramework.SourceGenerators.Common.Constants;
namespace GFramework.Godot.SourceGenerators.Diagnostics;
/// <summary>
/// BindNodeSignal 生成器相关诊断。
/// </summary>
public static class BindNodeSignalDiagnostics
{
/// <summary>
/// 嵌套类型不受支持。
/// </summary>
public static readonly DiagnosticDescriptor NestedClassNotSupported =
new(
"GF_Godot_BindNodeSignal_001",
"Nested classes are not supported",
"Class '{0}' cannot use [BindNodeSignal] inside a nested type",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// static 方法不受支持。
/// </summary>
public static readonly DiagnosticDescriptor StaticMethodNotSupported =
new(
"GF_Godot_BindNodeSignal_002",
"Static methods are not supported",
"Method '{0}' cannot be static when using [BindNodeSignal]",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 节点字段不存在。
/// </summary>
public static readonly DiagnosticDescriptor NodeFieldNotFound =
new(
"GF_Godot_BindNodeSignal_003",
"Referenced node field was not found",
"Method '{0}' references node field '{1}', but no matching field exists on class '{2}'",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 节点字段必须是实例字段。
/// </summary>
public static readonly DiagnosticDescriptor NodeFieldMustBeInstanceField =
new(
"GF_Godot_BindNodeSignal_004",
"Referenced node field must be an instance field",
"Method '{0}' references node field '{1}', but that field must be an instance field",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 字段类型必须继承自 Godot.Node。
/// </summary>
public static readonly DiagnosticDescriptor FieldTypeMustDeriveFromNode =
new(
"GF_Godot_BindNodeSignal_005",
"Field type must derive from Godot.Node",
"Field '{0}' must be a Godot.Node type to use [BindNodeSignal]",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 目标事件不存在。
/// </summary>
public static readonly DiagnosticDescriptor SignalNotFound =
new(
"GF_Godot_BindNodeSignal_006",
"Referenced event was not found",
"Field '{0}' does not contain an event named '{1}'",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 方法签名与事件委托不兼容。
/// </summary>
public static readonly DiagnosticDescriptor MethodSignatureNotCompatible =
new(
"GF_Godot_BindNodeSignal_007",
"Method signature is not compatible with the referenced event",
"Method '{0}' is not compatible with event '{1}' on field '{2}'",
PathContests.GodotNamespace,
DiagnosticSeverity.Error,
true);
/// <summary>
/// 现有 _Ready 中未调用生成绑定逻辑。
/// </summary>
public static readonly DiagnosticDescriptor ManualReadyHookRequired =
new(
"GF_Godot_BindNodeSignal_008",
"Call generated signal binding from _Ready",
"Class '{0}' defines _Ready(); call __BindNodeSignals_Generated() there to bind [BindNodeSignal] handlers",
PathContests.GodotNamespace,
DiagnosticSeverity.Warning,
true);
/// <summary>
/// 现有 _ExitTree 中未调用生成解绑逻辑。
/// </summary>
public static readonly DiagnosticDescriptor ManualExitTreeHookRequired =
new(
"GF_Godot_BindNodeSignal_009",
"Call generated signal unbinding from _ExitTree",
"Class '{0}' defines _ExitTree(); call __UnbindNodeSignals_Generated() there to unbind [BindNodeSignal] handlers",
PathContests.GodotNamespace,
DiagnosticSeverity.Warning,
true);
}