feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -3,3 +3,7 @@
|
|||
Rule ID | Category | Severity | Notes
|
||||
--------|----------|----------|--------------------
|
||||
GD0003 | Usage | Error | ScriptPathAttributeGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0003.html)
|
||||
GD0108 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0108.html)
|
||||
GD0109 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0109.html)
|
||||
GD0110 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0110.html)
|
||||
GD0111 | Usage | Error | ScriptPropertiesGenerator, [Documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/c_sharp/diagnostics/GD0111.html)
|
||||
|
|
|
|||
|
|
@ -107,6 +107,46 @@ namespace Godot.SourceGenerators
|
|||
"Types not derived from Node should not export Node members. Node export is only supported in Node-derived classes.",
|
||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0107"));
|
||||
|
||||
public static readonly DiagnosticDescriptor OnlyToolClassesShouldUseExportToolButtonRule =
|
||||
new DiagnosticDescriptor(id: "GD0108",
|
||||
title: "The exported tool button is not in a tool class",
|
||||
messageFormat: "The exported tool button '{0}' is not in a tool class",
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
"The exported tool button is not in a tool class. Annotate the class with the '[Tool]' attribute, or remove the '[ExportToolButton]' attribute.",
|
||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0108"));
|
||||
|
||||
public static readonly DiagnosticDescriptor ExportToolButtonShouldNotBeUsedWithExportRule =
|
||||
new DiagnosticDescriptor(id: "GD0109",
|
||||
title: "The '[ExportToolButton]' attribute cannot be used with another '[Export]' attribute",
|
||||
messageFormat: "The '[ExportToolButton]' attribute cannot be used with another '[Export]' attribute on '{0}'",
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
"The '[ExportToolButton]' attribute cannot be used with the '[Export]' attribute. Remove one of the attributes.",
|
||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0109"));
|
||||
|
||||
public static readonly DiagnosticDescriptor ExportToolButtonIsNotCallableRule =
|
||||
new DiagnosticDescriptor(id: "GD0110",
|
||||
title: "The exported tool button is not a Callable",
|
||||
messageFormat: "The exported tool button '{0}' is not a Callable",
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
"The exported tool button is not a Callable. The '[ExportToolButton]' attribute is only supported on members of type Callable.",
|
||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0110"));
|
||||
|
||||
public static readonly DiagnosticDescriptor ExportToolButtonMustBeExpressionBodiedProperty =
|
||||
new DiagnosticDescriptor(id: "GD0111",
|
||||
title: "The exported tool button must be an expression-bodied property",
|
||||
messageFormat: "The exported tool button '{0}' must be an expression-bodied property",
|
||||
category: "Usage",
|
||||
DiagnosticSeverity.Error,
|
||||
isEnabledByDefault: true,
|
||||
"The exported tool button must be an expression-bodied property. The '[ExportToolButton]' attribute is only supported on expression-bodied properties with a 'new Callable(...)' or 'Callable.From(...)' expression.",
|
||||
helpLinkUri: string.Format(_helpLinkFormat, "GD0111"));
|
||||
|
||||
public static readonly DiagnosticDescriptor SignalDelegateMissingSuffixRule =
|
||||
new DiagnosticDescriptor(id: "GD0201",
|
||||
title: "The name of the delegate must end with 'EventHandler'",
|
||||
|
|
|
|||
|
|
@ -155,6 +155,32 @@ namespace Godot.SourceGenerators
|
|||
};
|
||||
}
|
||||
|
||||
public static string GetAccessibilityKeyword(this INamedTypeSymbol namedTypeSymbol)
|
||||
{
|
||||
if (namedTypeSymbol.DeclaredAccessibility == Accessibility.NotApplicable)
|
||||
{
|
||||
// Accessibility not specified. Get the default accessibility.
|
||||
return namedTypeSymbol.ContainingSymbol switch
|
||||
{
|
||||
null or INamespaceSymbol => "internal",
|
||||
ITypeSymbol { TypeKind: TypeKind.Class or TypeKind.Struct } => "private",
|
||||
ITypeSymbol { TypeKind: TypeKind.Interface } => "public",
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
||||
return namedTypeSymbol.DeclaredAccessibility switch
|
||||
{
|
||||
Accessibility.Private => "private",
|
||||
Accessibility.Protected => "protected",
|
||||
Accessibility.Internal => "internal",
|
||||
Accessibility.ProtectedAndInternal => "private",
|
||||
Accessibility.ProtectedOrInternal => "private",
|
||||
Accessibility.Public => "public",
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
||||
public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
|
||||
{
|
||||
return symbol.IsGenericType ?
|
||||
|
|
@ -261,6 +287,12 @@ namespace Godot.SourceGenerators
|
|||
public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GlobalClassAttr;
|
||||
|
||||
public static bool IsGodotExportToolButtonAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.ExportToolButtonAttr;
|
||||
|
||||
public static bool IsGodotToolAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.ToolAttr;
|
||||
|
||||
public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
|
||||
=> symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SystemFlagsAttr;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Description>Core C# source generator for Godot projects.</Description>
|
||||
<Authors>Godot Engine contributors</Authors>
|
||||
|
||||
<PackageId>Godot.SourceGenerators</PackageId>
|
||||
<Version>4.3.0</Version>
|
||||
<Version>4.4.0</Version>
|
||||
<PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion>
|
||||
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl>
|
||||
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,18 @@ namespace Godot.SourceGenerators
|
|||
{
|
||||
public const string GodotObject = "Godot.GodotObject";
|
||||
public const string Node = "Godot.Node";
|
||||
public const string Callable = "Godot.Callable";
|
||||
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
|
||||
public const string ExportAttr = "Godot.ExportAttribute";
|
||||
public const string ExportCategoryAttr = "Godot.ExportCategoryAttribute";
|
||||
public const string ExportGroupAttr = "Godot.ExportGroupAttribute";
|
||||
public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute";
|
||||
public const string ExportToolButtonAttr = "Godot.ExportToolButtonAttribute";
|
||||
public const string SignalAttr = "Godot.SignalAttribute";
|
||||
public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
|
||||
public const string GodotClassNameAttr = "Godot.GodotClassNameAttribute";
|
||||
public const string GlobalClassAttr = "Godot.GlobalClassAttribute";
|
||||
public const string ToolAttr = "Godot.ToolAttribute";
|
||||
public const string SystemFlagsAttr = "System.FlagsAttribute";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,9 @@ namespace Godot.SourceGenerators
|
|||
HideQuaternionEdit = 35,
|
||||
Password = 36,
|
||||
LayersAvoidance = 37,
|
||||
Max = 38
|
||||
DictionaryType = 38,
|
||||
ToolButton = 39,
|
||||
Max = 40
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
|
|
|||
|
|
@ -274,6 +274,14 @@ namespace Godot.SourceGenerators
|
|||
return null;
|
||||
}
|
||||
|
||||
public static ITypeSymbol[]? GetGenericElementTypes(ITypeSymbol typeSymbol)
|
||||
{
|
||||
if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
|
||||
return genericType.TypeArguments.ToArray();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static StringBuilder Append(this StringBuilder source, string a, string b)
|
||||
=> source.Append(a).Append(b);
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Godot.SourceGenerators
|
|||
{
|
||||
ITypeParameterSymbol? typeParamSymbol = parentSymbol switch
|
||||
{
|
||||
IMethodSymbol methodSymbol when parentSyntax.Parent is AttributeSyntax &&
|
||||
IMethodSymbol methodSymbol when parentSyntax.Ancestors().Any(s => s is AttributeSyntax) &&
|
||||
methodSymbol.ContainingType.TypeParameters.Length > 0
|
||||
=> methodSymbol.ContainingType.TypeParameters[typeArgumentIndex],
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
|
|
@ -68,6 +70,7 @@ namespace Godot.SourceGenerators
|
|||
bool hasNamespace = classNs.Length != 0;
|
||||
|
||||
bool isInnerClass = symbol.ContainingType != null;
|
||||
bool isToolClass = symbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotToolAttribute() ?? false);
|
||||
|
||||
string uniqueHint = symbol.FullQualifiedNameOmitGlobal().SanitizeQualifiedNameForUniqueHint()
|
||||
+ "_ScriptProperties.generated";
|
||||
|
|
@ -276,6 +279,16 @@ namespace Godot.SourceGenerators
|
|||
if (propertyInfo == null)
|
||||
continue;
|
||||
|
||||
if (propertyInfo.Value.Hint == PropertyHint.ToolButton && !isToolClass)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.OnlyToolClassesShouldUseExportToolButtonRule,
|
||||
member.Symbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
member.Symbol.ToDisplayString()
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
AppendPropertyInfo(source, propertyInfo.Value);
|
||||
}
|
||||
|
||||
|
|
@ -417,31 +430,127 @@ namespace Godot.SourceGenerators
|
|||
var exportAttr = memberSymbol.GetAttributes()
|
||||
.FirstOrDefault(a => a.AttributeClass?.IsGodotExportAttribute() ?? false);
|
||||
|
||||
var exportToolButtonAttr = memberSymbol.GetAttributes()
|
||||
.FirstOrDefault(a => a.AttributeClass?.IsGodotExportToolButtonAttribute() ?? false);
|
||||
|
||||
if (exportAttr != null && exportToolButtonAttr != null)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportToolButtonShouldNotBeUsedWithExportRule,
|
||||
memberSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
memberSymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
var propertySymbol = memberSymbol as IPropertySymbol;
|
||||
var fieldSymbol = memberSymbol as IFieldSymbol;
|
||||
|
||||
if (exportAttr != null && propertySymbol != null)
|
||||
{
|
||||
if (propertySymbol.GetMethod == null)
|
||||
if (propertySymbol.GetMethod == null || propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
|
||||
{
|
||||
// Exports can be neither read-only nor write-only but the diagnostic errors for properties are already
|
||||
// reported by ScriptPropertyDefValGenerator.cs so just quit early here.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (exportToolButtonAttr != null && propertySymbol != null && propertySymbol.GetMethod == null)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportedPropertyIsWriteOnlyRule,
|
||||
propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
propertySymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exportToolButtonAttr != null && propertySymbol != null)
|
||||
{
|
||||
if (!PropertyIsExpressionBodiedAndReturnsNewCallable(context.Compilation, propertySymbol))
|
||||
{
|
||||
// This should never happen, as we filtered WriteOnly properties, but just in case.
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportedPropertyIsWriteOnlyRule,
|
||||
Common.ExportToolButtonMustBeExpressionBodiedProperty,
|
||||
propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
propertySymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (propertySymbol.SetMethod == null || propertySymbol.SetMethod.IsInitOnly)
|
||||
static bool PropertyIsExpressionBodiedAndReturnsNewCallable(Compilation compilation, IPropertySymbol? propertySymbol)
|
||||
{
|
||||
// This should never happen, as we filtered ReadOnly properties, but just in case.
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportedMemberIsReadOnlyRule,
|
||||
propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
propertySymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
if (propertySymbol == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var propertyDeclarationSyntax = propertySymbol.DeclaringSyntaxReferences
|
||||
.Select(r => r.GetSyntax() as PropertyDeclarationSyntax).FirstOrDefault();
|
||||
if (propertyDeclarationSyntax == null || propertyDeclarationSyntax.Initializer != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (propertyDeclarationSyntax.AccessorList != null)
|
||||
{
|
||||
var accessors = propertyDeclarationSyntax.AccessorList.Accessors;
|
||||
foreach (var accessor in accessors)
|
||||
{
|
||||
if (!accessor.IsKind(SyntaxKind.GetAccessorDeclaration))
|
||||
{
|
||||
// Only getters are allowed.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ExpressionBodyReturnsNewCallable(compilation, accessor.ExpressionBody))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!ExpressionBodyReturnsNewCallable(compilation, propertyDeclarationSyntax.ExpressionBody))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ExpressionBodyReturnsNewCallable(Compilation compilation, ArrowExpressionClauseSyntax? expressionSyntax)
|
||||
{
|
||||
if (expressionSyntax == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var semanticModel = compilation.GetSemanticModel(expressionSyntax.SyntaxTree);
|
||||
|
||||
switch (expressionSyntax.Expression)
|
||||
{
|
||||
case ImplicitObjectCreationExpressionSyntax creationExpression:
|
||||
// We already validate that the property type must be 'Callable'
|
||||
// so we can assume this constructor is valid.
|
||||
return true;
|
||||
|
||||
case ObjectCreationExpressionSyntax creationExpression:
|
||||
var typeSymbol = semanticModel.GetSymbolInfo(creationExpression.Type).Symbol as ITypeSymbol;
|
||||
if (typeSymbol != null)
|
||||
{
|
||||
return typeSymbol.FullQualifiedNameOmitGlobal() == GodotClasses.Callable;
|
||||
}
|
||||
break;
|
||||
|
||||
case InvocationExpressionSyntax invocationExpression:
|
||||
var methodSymbol = semanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol;
|
||||
if (methodSymbol != null && methodSymbol.Name == "From")
|
||||
{
|
||||
return methodSymbol.ContainingType.FullQualifiedNameOmitGlobal() == GodotClasses.Callable;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -450,14 +559,41 @@ namespace Godot.SourceGenerators
|
|||
var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
|
||||
string memberName = memberSymbol.Name;
|
||||
|
||||
string? hintString = null;
|
||||
|
||||
if (exportToolButtonAttr != null)
|
||||
{
|
||||
if (memberVariantType != VariantType.Callable)
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportToolButtonIsNotCallableRule,
|
||||
memberSymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
memberSymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
hintString = exportToolButtonAttr.ConstructorArguments[0].Value?.ToString() ?? "";
|
||||
foreach (var namedArgument in exportToolButtonAttr.NamedArguments)
|
||||
{
|
||||
if (namedArgument is { Key: "Icon", Value.Value: string { Length: > 0 } })
|
||||
{
|
||||
hintString += $",{namedArgument.Value.Value}";
|
||||
}
|
||||
}
|
||||
|
||||
return new PropertyInfo(memberVariantType, memberName, PropertyHint.ToolButton,
|
||||
hintString: hintString, PropertyUsageFlags.Editor, exported: true);
|
||||
}
|
||||
|
||||
if (exportAttr == null)
|
||||
{
|
||||
return new PropertyInfo(memberVariantType, memberName, PropertyHint.None,
|
||||
hintString: null, PropertyUsageFlags.ScriptVariable, exported: false);
|
||||
hintString: hintString, PropertyUsageFlags.ScriptVariable, exported: false);
|
||||
}
|
||||
|
||||
if (!TryGetMemberExportHint(typeCache, memberType, exportAttr, memberVariantType,
|
||||
isTypeArgument: false, out var hint, out var hintString))
|
||||
isTypeArgument: false, out var hint, out hintString))
|
||||
{
|
||||
var constructorArguments = exportAttr.ConstructorArguments;
|
||||
|
||||
|
|
@ -728,8 +864,81 @@ namespace Godot.SourceGenerators
|
|||
|
||||
if (!isTypeArgument && variantType == VariantType.Dictionary)
|
||||
{
|
||||
// TODO: Dictionaries are not supported in the inspector
|
||||
return false;
|
||||
var elementTypes = MarshalUtils.GetGenericElementTypes(type);
|
||||
|
||||
if (elementTypes == null)
|
||||
return false; // Non-generic Dictionary, so there's no hint to add
|
||||
Debug.Assert(elementTypes.Length == 2);
|
||||
|
||||
var keyElementMarshalType = MarshalUtils.ConvertManagedTypeToMarshalType(elementTypes[0], typeCache);
|
||||
var valueElementMarshalType = MarshalUtils.ConvertManagedTypeToMarshalType(elementTypes[1], typeCache);
|
||||
|
||||
if (keyElementMarshalType == null || valueElementMarshalType == null)
|
||||
{
|
||||
// To maintain compatibility with previous versions of Godot before 4.4,
|
||||
// we must preserve the old behavior for generic dictionaries with non-marshallable
|
||||
// generic type arguments.
|
||||
return false;
|
||||
}
|
||||
|
||||
var keyElementVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(keyElementMarshalType.Value)!.Value;
|
||||
var keyIsPresetHint = false;
|
||||
var keyHintString = (string?)null;
|
||||
|
||||
if (keyElementVariantType == VariantType.String || keyElementVariantType == VariantType.StringName)
|
||||
keyIsPresetHint = GetStringArrayEnumHint(keyElementVariantType, exportAttr, out keyHintString);
|
||||
|
||||
if (!keyIsPresetHint)
|
||||
{
|
||||
bool hintRes = TryGetMemberExportHint(typeCache, elementTypes[0],
|
||||
exportAttr, keyElementVariantType, isTypeArgument: true,
|
||||
out var keyElementHint, out var keyElementHintString);
|
||||
|
||||
// Format: type/hint:hint_string
|
||||
if (hintRes)
|
||||
{
|
||||
keyHintString = (int)keyElementVariantType + "/" + (int)keyElementHint + ":";
|
||||
|
||||
if (keyElementHintString != null)
|
||||
keyHintString += keyElementHintString;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyHintString = (int)keyElementVariantType + "/" + (int)PropertyHint.None + ":";
|
||||
}
|
||||
}
|
||||
|
||||
var valueElementVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(valueElementMarshalType.Value)!.Value;
|
||||
var valueIsPresetHint = false;
|
||||
var valueHintString = (string?)null;
|
||||
|
||||
if (valueElementVariantType == VariantType.String || valueElementVariantType == VariantType.StringName)
|
||||
valueIsPresetHint = GetStringArrayEnumHint(valueElementVariantType, exportAttr, out valueHintString);
|
||||
|
||||
if (!valueIsPresetHint)
|
||||
{
|
||||
bool hintRes = TryGetMemberExportHint(typeCache, elementTypes[1],
|
||||
exportAttr, valueElementVariantType, isTypeArgument: true,
|
||||
out var valueElementHint, out var valueElementHintString);
|
||||
|
||||
// Format: type/hint:hint_string
|
||||
if (hintRes)
|
||||
{
|
||||
valueHintString = (int)valueElementVariantType + "/" + (int)valueElementHint + ":";
|
||||
|
||||
if (valueElementHintString != null)
|
||||
valueHintString += valueElementHintString;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueHintString = (int)valueElementVariantType + "/" + (int)PropertyHint.None + ":";
|
||||
}
|
||||
}
|
||||
|
||||
hint = PropertyHint.TypeString;
|
||||
|
||||
hintString = keyHintString != null && valueHintString != null ? $"{keyHintString};{valueHintString}" : null;
|
||||
return hintString != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -196,16 +196,13 @@ namespace Godot.SourceGenerators
|
|||
continue;
|
||||
}
|
||||
|
||||
if (marshalType == MarshalType.GodotObjectOrDerived)
|
||||
if (!isNode && MemberHasNodeType(propertyType, marshalType.Value))
|
||||
{
|
||||
if (!isNode && propertyType.InheritsFrom("GodotSharp", GodotClasses.Node))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.OnlyNodesShouldExportNodesRule,
|
||||
property.Locations.FirstLocationWithSourceTreeOrDefault()
|
||||
));
|
||||
continue;
|
||||
}
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.OnlyNodesShouldExportNodesRule,
|
||||
property.Locations.FirstLocationWithSourceTreeOrDefault()
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
var propertyDeclarationSyntax = property.DeclaringSyntaxReferences
|
||||
|
|
@ -315,16 +312,13 @@ namespace Godot.SourceGenerators
|
|||
continue;
|
||||
}
|
||||
|
||||
if (marshalType == MarshalType.GodotObjectOrDerived)
|
||||
if (!isNode && MemberHasNodeType(fieldType, marshalType.Value))
|
||||
{
|
||||
if (!isNode && fieldType.InheritsFrom("GodotSharp", GodotClasses.Node))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.OnlyNodesShouldExportNodesRule,
|
||||
field.Locations.FirstLocationWithSourceTreeOrDefault()
|
||||
));
|
||||
continue;
|
||||
}
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.OnlyNodesShouldExportNodesRule,
|
||||
field.Locations.FirstLocationWithSourceTreeOrDefault()
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
||||
EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences
|
||||
|
|
@ -424,6 +418,27 @@ namespace Godot.SourceGenerators
|
|||
context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
|
||||
}
|
||||
|
||||
private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType)
|
||||
{
|
||||
if (marshalType == MarshalType.GodotObjectOrDerived)
|
||||
{
|
||||
return memberType.InheritsFrom("GodotSharp", GodotClasses.Node);
|
||||
}
|
||||
if (marshalType == MarshalType.GodotObjectOrDerivedArray)
|
||||
{
|
||||
var elementType = ((IArrayTypeSymbol)memberType).ElementType;
|
||||
return elementType.InheritsFrom("GodotSharp", GodotClasses.Node);
|
||||
}
|
||||
if (memberType is INamedTypeSymbol { IsGenericType: true } genericType)
|
||||
{
|
||||
return genericType.TypeArguments
|
||||
.Any(static typeArgument
|
||||
=> typeArgument.InheritsFrom("GodotSharp", GodotClasses.Node));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private struct ExportedPropertyMetadata
|
||||
{
|
||||
public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value)
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@ using Microsoft.CodeAnalysis;
|
|||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
// TODO:
|
||||
// Determine a proper way to emit the signal.
|
||||
// 'Emit(nameof(TheEvent))' creates a StringName every time and has the overhead of string marshaling.
|
||||
// I haven't decided on the best option yet. Some possibilities:
|
||||
// - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
|
||||
// - Generate a 'EmitSignalName' method for each event signal.
|
||||
|
||||
namespace Godot.SourceGenerators
|
||||
{
|
||||
[Generator]
|
||||
|
|
@ -276,7 +269,7 @@ namespace Godot.SourceGenerators
|
|||
source.Append(
|
||||
$" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
|
||||
|
||||
source.Append(" public event ")
|
||||
source.Append($" {signalDelegate.DelegateSymbol.GetAccessibilityKeyword()} event ")
|
||||
.Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
|
||||
.Append(" @")
|
||||
.Append(signalName)
|
||||
|
|
@ -288,6 +281,43 @@ namespace Godot.SourceGenerators
|
|||
.Append(signalName)
|
||||
.Append(" -= value;\n")
|
||||
.Append("}\n");
|
||||
|
||||
// Generate EmitSignal{EventName} method to raise the event
|
||||
|
||||
var invokeMethodSymbol = signalDelegate.InvokeMethodData.Method;
|
||||
int paramCount = invokeMethodSymbol.Parameters.Length;
|
||||
|
||||
string raiseMethodModifiers = signalDelegate.DelegateSymbol.ContainingType.IsSealed ?
|
||||
"private" :
|
||||
"protected";
|
||||
|
||||
source.Append($" {raiseMethodModifiers} void EmitSignal{signalName}(");
|
||||
for (int i = 0; i < paramCount; i++)
|
||||
{
|
||||
var paramSymbol = invokeMethodSymbol.Parameters[i];
|
||||
source.Append($"{paramSymbol.Type.FullQualifiedNameIncludeGlobal()} @{paramSymbol.Name}");
|
||||
if (i < paramCount - 1)
|
||||
{
|
||||
source.Append(", ");
|
||||
}
|
||||
}
|
||||
source.Append(")\n");
|
||||
source.Append(" {\n");
|
||||
source.Append($" EmitSignal(SignalName.{signalName}");
|
||||
foreach (var paramSymbol in invokeMethodSymbol.Parameters)
|
||||
{
|
||||
// Enums must be converted to the underlying type before they can be implicitly converted to Variant
|
||||
if (paramSymbol.Type.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
var underlyingType = ((INamedTypeSymbol)paramSymbol.Type).EnumUnderlyingType!;
|
||||
source.Append($", ({underlyingType.FullQualifiedNameIncludeGlobal()})@{paramSymbol.Name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
source.Append($", @{paramSymbol.Name}");
|
||||
}
|
||||
source.Append(");\n");
|
||||
source.Append(" }\n");
|
||||
}
|
||||
|
||||
// Generate RaiseGodotClassSignalCallbacks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue