فهرست منبع

fix https://github.com/godotengine/godot/issues/104135 Generator for C# makes illegal code for a GodotObject with a primary ctor
Co-authored-by: Raul Santos <[email protected]>

Ivan Shakhov 6 ماه پیش
والد
کامیت
724c0021c7

+ 7 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs

@@ -22,6 +22,13 @@ public class ScriptPropertyDefValGeneratorTests
         );
     }
 
+    [Fact]
+    public async void ExportedProperties2()
+    {
+        await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify(
+            "ExportedProperties2.cs", "ExportedProperties2_ScriptPropertyDefVal.generated.cs");
+    }
+
     [Fact]
     public async void ExportedComplexStrings()
     {

+ 25 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportedProperties2_ScriptPropertyDefVal.generated.cs

@@ -0,0 +1,25 @@
+partial class ExportedProperties2
+{
+#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
+#if TOOLS
+    /// <summary>
+    /// Get the default values for all properties declared in this class.
+    /// This method is used by Godot to determine the value that will be
+    /// used by the inspector when resetting properties.
+    /// Do not call this method.
+    /// </summary>
+    [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+    internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues()
+    {
+        var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(3);
+        int __Health_default_value = default;
+        values.Add(PropertyName.@Health, global::Godot.Variant.From<int>(__Health_default_value));
+        global::Godot.Resource __SubResource_default_value = default;
+        values.Add(PropertyName.@SubResource, global::Godot.Variant.From<global::Godot.Resource>(__SubResource_default_value));
+        string[] __Strings_default_value = default;
+        values.Add(PropertyName.@Strings, global::Godot.Variant.From<string[]>(__Strings_default_value));
+        return values;
+    }
+#endif // TOOLS
+#pragma warning restore CS0109
+}

+ 13 - 0
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportedProperties2.cs

@@ -0,0 +1,13 @@
+using Godot;
+using System;
+
+[GlobalClass]
+public partial class ExportedProperties2(int health, Resource subResource, string[] strings) : Resource
+{
+    [Export]
+    public int Health { get; set; } = health;
+    [Export]
+    public Resource SubResource { get; set; } = subResource;
+    [Export]
+    public string[] Strings { get; set; } = strings ?? System.Array.Empty<string>();
+}

+ 105 - 1
modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs

@@ -215,7 +215,11 @@ namespace Godot.SourceGenerators
                     if (propertyDeclarationSyntax.Initializer != null)
                     {
                         var sm = context.Compilation.GetSemanticModel(propertyDeclarationSyntax.Initializer.SyntaxTree);
-                        value = propertyDeclarationSyntax.Initializer.Value.FullQualifiedSyntax(sm);
+                        var initializerValue = propertyDeclarationSyntax.Initializer.Value;
+                        if (!IsStaticallyResolvable(initializerValue, sm))
+                            value = "default";
+                        else
+                            value = propertyDeclarationSyntax.Initializer.Value.FullQualifiedSyntax(sm);
                     }
                     else
                     {
@@ -418,6 +422,106 @@ namespace Godot.SourceGenerators
             context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
         }
 
+        private static bool IsStaticallyResolvable(ExpressionSyntax expression, SemanticModel semanticModel)
+        {
+            // Handle literals (e.g., `10`, `"string"`, `true`, etc.)
+            if (expression is LiteralExpressionSyntax)
+            {
+                return true;
+            }
+
+            // Handle identifiers (e.g., variable names)
+            if (expression is IdentifierNameSyntax identifier)
+            {
+                var symbolInfo = semanticModel.GetSymbolInfo(identifier).Symbol;
+
+                // Ensure it's a static member
+                return symbolInfo is { IsStatic: true };
+            }
+
+            // Handle member access (e.g., `MyClass.StaticValue`)
+            if (expression is MemberAccessExpressionSyntax memberAccess)
+            {
+                var symbolInfo = semanticModel.GetSymbolInfo(memberAccess).Symbol;
+
+                // Ensure it's referring to a static member
+                return symbolInfo is { IsStatic: true };
+            }
+
+            // Handle object creation expressions (e.g., `new Vector2(1.0f, 2.0f)`)
+            if (expression is ObjectCreationExpressionSyntax objectCreation)
+            {
+                // Recursively ensure all its arguments are self-contained
+                if (objectCreation.ArgumentList == null)
+                {
+                    return true;
+                }
+                foreach (var argument in objectCreation.ArgumentList.Arguments)
+                {
+                    if (!IsStaticallyResolvable(argument.Expression, semanticModel))
+                    {
+                        return false;
+                    }
+                }
+
+                return true;
+            }
+
+            if (expression is ImplicitObjectCreationExpressionSyntax)
+            {
+                return true;
+            }
+
+            if (expression is InvocationExpressionSyntax invocationExpression)
+            {
+                // Resolve the method being invoked
+                var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression).Symbol;
+
+                if (symbolInfo is IMethodSymbol methodSymbol)
+                {
+                    // Ensure the method is static
+                    if (methodSymbol.IsStatic)
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            if (expression is InterpolatedStringExpressionSyntax interpolatedString)
+            {
+                foreach (var content in interpolatedString.Contents)
+                {
+                    if (content is not InterpolationSyntax interpolation)
+                    {
+                        continue;
+                    }
+                    // Analyze the expression inside `${...}`
+                    var interpolatedExpression = interpolation.Expression;
+
+                    if (!IsStaticallyResolvable(interpolatedExpression, semanticModel))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            if (expression is InitializerExpressionSyntax initializerExpressionSyntax)
+            {
+                foreach (var content in initializerExpressionSyntax.Expressions)
+                {
+                    if (!IsStaticallyResolvable(content, semanticModel))
+                    {
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            // Handle other expressions conservatively (e.g., method calls, instance references, etc.)
+            return false;
+        }
+
         private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType)
         {
             if (marshalType == MarshalType.GodotObjectOrDerived)