Skip to content

Commit 724c002

Browse files
committed
fix #104135 Generator for C# makes illegal code for a GodotObject with a primary ctor
Co-authored-by: Raul Santos <[email protected]>
1 parent 49cc57a commit 724c002

File tree

4 files changed

+150
-1
lines changed

4 files changed

+150
-1
lines changed

modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPropertyDefValGeneratorTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify(
2222
);
2323
}
2424

25+
[Fact]
26+
public async void ExportedProperties2()
27+
{
28+
await CSharpSourceGeneratorVerifier<ScriptPropertyDefValGenerator>.Verify(
29+
"ExportedProperties2.cs", "ExportedProperties2_ScriptPropertyDefVal.generated.cs");
30+
}
31+
2532
[Fact]
2633
public async void ExportedComplexStrings()
2734
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
partial class ExportedProperties2
2+
{
3+
#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword
4+
#if TOOLS
5+
/// <summary>
6+
/// Get the default values for all properties declared in this class.
7+
/// This method is used by Godot to determine the value that will be
8+
/// used by the inspector when resetting properties.
9+
/// Do not call this method.
10+
/// </summary>
11+
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
12+
internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues()
13+
{
14+
var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(3);
15+
int __Health_default_value = default;
16+
values.Add(PropertyName.@Health, global::Godot.Variant.From<int>(__Health_default_value));
17+
global::Godot.Resource __SubResource_default_value = default;
18+
values.Add(PropertyName.@SubResource, global::Godot.Variant.From<global::Godot.Resource>(__SubResource_default_value));
19+
string[] __Strings_default_value = default;
20+
values.Add(PropertyName.@Strings, global::Godot.Variant.From<string[]>(__Strings_default_value));
21+
return values;
22+
}
23+
#endif // TOOLS
24+
#pragma warning restore CS0109
25+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Godot;
2+
using System;
3+
4+
[GlobalClass]
5+
public partial class ExportedProperties2(int health, Resource subResource, string[] strings) : Resource
6+
{
7+
[Export]
8+
public int Health { get; set; } = health;
9+
[Export]
10+
public Resource SubResource { get; set; } = subResource;
11+
[Export]
12+
public string[] Strings { get; set; } = strings ?? System.Array.Empty<string>();
13+
}

modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,11 @@ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
215215
if (propertyDeclarationSyntax.Initializer != null)
216216
{
217217
var sm = context.Compilation.GetSemanticModel(propertyDeclarationSyntax.Initializer.SyntaxTree);
218-
value = propertyDeclarationSyntax.Initializer.Value.FullQualifiedSyntax(sm);
218+
var initializerValue = propertyDeclarationSyntax.Initializer.Value;
219+
if (!IsStaticallyResolvable(initializerValue, sm))
220+
value = "default";
221+
else
222+
value = propertyDeclarationSyntax.Initializer.Value.FullQualifiedSyntax(sm);
219223
}
220224
else
221225
{
@@ -418,6 +422,106 @@ void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType)
418422
context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
419423
}
420424

425+
private static bool IsStaticallyResolvable(ExpressionSyntax expression, SemanticModel semanticModel)
426+
{
427+
// Handle literals (e.g., `10`, `"string"`, `true`, etc.)
428+
if (expression is LiteralExpressionSyntax)
429+
{
430+
return true;
431+
}
432+
433+
// Handle identifiers (e.g., variable names)
434+
if (expression is IdentifierNameSyntax identifier)
435+
{
436+
var symbolInfo = semanticModel.GetSymbolInfo(identifier).Symbol;
437+
438+
// Ensure it's a static member
439+
return symbolInfo is { IsStatic: true };
440+
}
441+
442+
// Handle member access (e.g., `MyClass.StaticValue`)
443+
if (expression is MemberAccessExpressionSyntax memberAccess)
444+
{
445+
var symbolInfo = semanticModel.GetSymbolInfo(memberAccess).Symbol;
446+
447+
// Ensure it's referring to a static member
448+
return symbolInfo is { IsStatic: true };
449+
}
450+
451+
// Handle object creation expressions (e.g., `new Vector2(1.0f, 2.0f)`)
452+
if (expression is ObjectCreationExpressionSyntax objectCreation)
453+
{
454+
// Recursively ensure all its arguments are self-contained
455+
if (objectCreation.ArgumentList == null)
456+
{
457+
return true;
458+
}
459+
foreach (var argument in objectCreation.ArgumentList.Arguments)
460+
{
461+
if (!IsStaticallyResolvable(argument.Expression, semanticModel))
462+
{
463+
return false;
464+
}
465+
}
466+
467+
return true;
468+
}
469+
470+
if (expression is ImplicitObjectCreationExpressionSyntax)
471+
{
472+
return true;
473+
}
474+
475+
if (expression is InvocationExpressionSyntax invocationExpression)
476+
{
477+
// Resolve the method being invoked
478+
var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression).Symbol;
479+
480+
if (symbolInfo is IMethodSymbol methodSymbol)
481+
{
482+
// Ensure the method is static
483+
if (methodSymbol.IsStatic)
484+
{
485+
return true;
486+
}
487+
}
488+
}
489+
490+
if (expression is InterpolatedStringExpressionSyntax interpolatedString)
491+
{
492+
foreach (var content in interpolatedString.Contents)
493+
{
494+
if (content is not InterpolationSyntax interpolation)
495+
{
496+
continue;
497+
}
498+
// Analyze the expression inside `${...}`
499+
var interpolatedExpression = interpolation.Expression;
500+
501+
if (!IsStaticallyResolvable(interpolatedExpression, semanticModel))
502+
{
503+
return false;
504+
}
505+
}
506+
return true;
507+
}
508+
509+
if (expression is InitializerExpressionSyntax initializerExpressionSyntax)
510+
{
511+
foreach (var content in initializerExpressionSyntax.Expressions)
512+
{
513+
if (!IsStaticallyResolvable(content, semanticModel))
514+
{
515+
return false;
516+
}
517+
}
518+
return true;
519+
}
520+
521+
// Handle other expressions conservatively (e.g., method calls, instance references, etc.)
522+
return false;
523+
}
524+
421525
private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType)
422526
{
423527
if (marshalType == MarshalType.GodotObjectOrDerived)

0 commit comments

Comments
 (0)