From 0095a7986988ecdd64ff646b0e6ec7411dbe1440 Mon Sep 17 00:00:00 2001 From: Raul Santos Date: Mon, 25 May 2026 15:43:35 +0200 Subject: [PATCH] [.NET] Add `IgnoreMember` attribute to exclude members from Godot Co-authored-by: kkjinping --- .../ExportedFields_ScriptProperties.generated.cs | 13 +++++++++++++ ...ExportedProperties_ScriptProperties.generated.cs | 13 +++++++++++++ .../TestData/Sources/ExportedFields.cs | 5 +++++ .../TestData/Sources/ExportedProperties.cs | 5 +++++ .../TestData/Sources/Methods.cs | 5 +++++ .../TestData/Sources/ScriptBoilerplate.cs | 6 ++++++ .../Godot.SourceGenerators/ExtensionMethods.cs | 3 +++ .../Godot.SourceGenerators/GodotClasses.cs | 1 + .../ScriptMethodsGenerator.cs | 2 ++ .../ScriptPropertiesGenerator.cs | 4 +++- .../ScriptPropertyDefValGenerator.cs | 4 +++- .../ScriptSerializationGenerator.cs | 4 +++- .../Core/Attributes/IgnoreMemberAttribute.cs | 10 ++++++++++ .../glue/GodotSharp/GodotSharp/GodotSharp.csproj | 1 + 14 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/IgnoreMemberAttribute.cs diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs index 3e5d034268..16e133a2b5 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedFields_ScriptProperties.generated.cs @@ -253,6 +253,10 @@ partial class ExportedFields /// public new static readonly global::Godot.StringName @_fieldGodotGenericArray = "_fieldGodotGenericArray"; /// + /// Cached name for the '_notIgnoredField' field. + /// + public new static readonly global::Godot.StringName @_notIgnoredField = "_notIgnoredField"; + /// /// Cached name for the '_fieldEmptyInt64Array' field. /// public new static readonly global::Godot.StringName @_fieldEmptyInt64Array = "_fieldEmptyInt64Array"; @@ -505,6 +509,10 @@ partial class ExportedFields this.@_fieldGodotGenericArray = global::Godot.NativeInterop.VariantUtils.ConvertToArray(value); return true; } + if (name == PropertyName.@_notIgnoredField) { + this.@_notIgnoredField = global::Godot.NativeInterop.VariantUtils.ConvertTo(value); + return true; + } if (name == PropertyName.@_fieldEmptyInt64Array) { this.@_fieldEmptyInt64Array = global::Godot.NativeInterop.VariantUtils.ConvertTo(value); return true; @@ -759,6 +767,10 @@ partial class ExportedFields value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.@_fieldGodotGenericArray); return true; } + if (name == PropertyName.@_notIgnoredField) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom(this.@_notIgnoredField); + return true; + } if (name == PropertyName.@_fieldEmptyInt64Array) { value = global::Godot.NativeInterop.VariantUtils.CreateFrom(this.@_fieldEmptyInt64Array); return true; @@ -835,6 +847,7 @@ partial class ExportedFields properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@_fieldGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@_fieldGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.@_notIgnoredField, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); properties.Add(new(type: (global::Godot.Variant.Type)31, name: PropertyName.@_fieldEmptyInt64Array, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); return properties; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs index 05ffd873be..dd2e1a0495 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties_ScriptProperties.generated.cs @@ -293,6 +293,10 @@ partial class ExportedProperties /// public new static readonly global::Godot.StringName @PropertyGodotGenericArray = "PropertyGodotGenericArray"; /// + /// Cached name for the 'NotIgnoredProperty' property. + /// + public new static readonly global::Godot.StringName @NotIgnoredProperty = "NotIgnoredProperty"; + /// /// Cached name for the '_notGeneratePropertyString' field. /// public new static readonly global::Godot.StringName @_notGeneratePropertyString = "_notGeneratePropertyString"; @@ -609,6 +613,10 @@ partial class ExportedProperties this.@PropertyGodotGenericArray = global::Godot.NativeInterop.VariantUtils.ConvertToArray(value); return true; } + if (name == PropertyName.@NotIgnoredProperty) { + this.@NotIgnoredProperty = global::Godot.NativeInterop.VariantUtils.ConvertTo(value); + return true; + } if (name == PropertyName.@_notGeneratePropertyString) { this.@_notGeneratePropertyString = global::Godot.NativeInterop.VariantUtils.ConvertTo(value); return true; @@ -927,6 +935,10 @@ partial class ExportedProperties value = global::Godot.NativeInterop.VariantUtils.CreateFromArray(this.@PropertyGodotGenericArray); return true; } + if (name == PropertyName.@NotIgnoredProperty) { + value = global::Godot.NativeInterop.VariantUtils.CreateFrom(this.@NotIgnoredProperty); + return true; + } if (name == PropertyName.@_notGeneratePropertyString) { value = global::Godot.NativeInterop.VariantUtils.CreateFrom(this.@_notGeneratePropertyString); return true; @@ -1044,6 +1056,7 @@ partial class ExportedProperties properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotArray, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); properties.Add(new(type: (global::Godot.Variant.Type)27, name: PropertyName.@PropertyGodotGenericDictionary, hint: (global::Godot.PropertyHint)23, hintString: "4/0:;1/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); properties.Add(new(type: (global::Godot.Variant.Type)28, name: PropertyName.@PropertyGodotGenericArray, hint: (global::Godot.PropertyHint)23, hintString: "2/0:", usage: (global::Godot.PropertyUsageFlags)4102, exported: true)); + properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.@NotIgnoredProperty, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false)); return properties; } #pragma warning restore CS0109 diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs index a89c4afc2e..3cb438056c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedFields.cs @@ -104,4 +104,9 @@ public partial class ExportedFields : GodotObject [Export] private Godot.Collections.Array _fieldGodotGenericArray = new() { 0, 1, 2, 3, 4, 5, 6 }; + + private int _notIgnoredField; + + [IgnoreMember] + private int _ignoredField; } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs index 2753c5ec9a..76d2d30d65 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties.cs @@ -223,4 +223,9 @@ public partial class ExportedProperties(string primaryCtorParameter) : GodotObje [Export] private Godot.Collections.Array PropertyGodotGenericArray { get; set; } = new() { 0, 1, 2, 3, 4, 5, 6 }; + + public int NotIgnoredProperty { get; set; } + + [IgnoreMember] + public int IgnoredProperty { get; set; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs index 1da9db8204..8526fbd260 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Methods.cs @@ -2,6 +2,11 @@ using Godot; public partial class Methods : GodotObject { + [IgnoreMember] + private void IgnoredMethod() + { + } + private void MethodWithOverload() { } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs index b431522e7c..5f91f89b7a 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ScriptBoilerplate.cs @@ -5,6 +5,12 @@ public partial class ScriptBoilerplate : Node private NodePath _nodePath; private int _velocity; + [IgnoreMember] + private int _ignoredField; + + [IgnoreMember] + private int IgnoredProperty { get; set; } + public override void _Process(double delta) { _ = delta; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index 03fa79aacf..802aa8e072 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -299,6 +299,9 @@ namespace Godot.SourceGenerators public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol) => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SystemFlagsAttr; + public static bool IsGodotIgnoreMemberAttribute(this INamedTypeSymbol symbol) + => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.IgnoreMemberAttr; + public static GodotMethodData? HasGodotCompatibleSignature( this IMethodSymbol method, MarshalUtils.TypeCache typeCache diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs index 3264ec2b54..8dee054652 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs @@ -11,6 +11,7 @@ namespace Godot.SourceGenerators public const string ExportGroupAttr = "Godot.ExportGroupAttribute"; public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute"; public const string ExportToolButtonAttr = "Godot.ExportToolButtonAttribute"; + public const string IgnoreMemberAttr = "Godot.IgnoreMemberAttribute"; public const string SignalAttr = "Godot.SignalAttribute"; public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute"; public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute"; diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 7cbab34c5b..ddff4ddf7a 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -128,6 +128,8 @@ namespace Godot.SourceGenerators var methodSymbols = members .Where(s => s.Kind == SymbolKind.Method && !s.IsImplicitlyDeclared) .Cast() + .Where(m => !m.GetAttributes() + .Any(a => a.AttributeClass?.IsGodotIgnoreMemberAttribute() ?? false)) .Where(m => m.MethodKind == MethodKind.Ordinary); var godotClassMethods = methodSymbols.WhereHasGodotCompatibleSignature(typeCache) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index 4dba18424c..475f6c1868 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -112,7 +112,9 @@ namespace Godot.SourceGenerators source.Append(symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); source.Append("\n{\n"); - var members = symbol.GetMembers(); + var members = symbol.GetMembers() + .Where(m => !m.GetAttributes() + .Any(a => a.AttributeClass?.IsGodotIgnoreMemberAttribute() ?? false)); var propertySymbols = members .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 5400115fa4..9d93ae094c 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -111,7 +111,9 @@ namespace Godot.SourceGenerators var exportedMembers = new List(); - var members = symbol.GetMembers(); + var members = symbol.GetMembers() + .Where(m => !m.GetAttributes() + .Any(a => a.AttributeClass?.IsGodotIgnoreMemberAttribute() ?? false)); var exportedProperties = members .Where(s => s.Kind == SymbolKind.Property) diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index 57e24ec25e..170584e5c8 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -110,7 +110,9 @@ namespace Godot.SourceGenerators source.Append(symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); source.Append("\n{\n"); - var members = symbol.GetMembers(); + var members = symbol.GetMembers() + .Where(m => !m.GetAttributes() + .Any(a => a.AttributeClass?.IsGodotIgnoreMemberAttribute() ?? false)); var propertySymbols = members .Where(s => !s.IsStatic && s.Kind == SymbolKind.Property) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/IgnoreMemberAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/IgnoreMemberAttribute.cs new file mode 100644 index 0000000000..8517df5fe6 --- /dev/null +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/IgnoreMemberAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace Godot +{ + /// + /// An attribute that excludes a member from registering as a method or property in Godot. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class IgnoreMemberAttribute : Attribute { } +} diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj index 474fdc9c94..08f60d5d4b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj +++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj @@ -74,6 +74,7 @@ +