You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-06 12:20:30 +00:00
[.NET] Disallow [ExportToolButton] on members thay may store the Callable
Ensures the user doesn't store the Callable so the .NET assembly can be reloaded.
This commit is contained in:
@@ -6,3 +6,4 @@ GD0003 | Usage | Error | ScriptPathAttributeGenerator, [Documentation](ht
|
||||
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)
|
||||
|
||||
@@ -137,6 +137,16 @@ namespace Godot.SourceGenerators
|
||||
"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'",
|
||||
|
||||
@@ -4,6 +4,7 @@ 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";
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
|
||||
@@ -465,6 +466,94 @@ namespace Godot.SourceGenerators
|
||||
return null;
|
||||
}
|
||||
|
||||
if (exportToolButtonAttr != null && propertySymbol != null)
|
||||
{
|
||||
if (!PropertyIsExpressionBodiedAndReturnsNewCallable(context.Compilation, propertySymbol))
|
||||
{
|
||||
context.ReportDiagnostic(Diagnostic.Create(
|
||||
Common.ExportToolButtonMustBeExpressionBodiedProperty,
|
||||
propertySymbol.Locations.FirstLocationWithSourceTreeOrDefault(),
|
||||
propertySymbol.ToDisplayString()
|
||||
));
|
||||
return null;
|
||||
}
|
||||
|
||||
static bool PropertyIsExpressionBodiedAndReturnsNewCallable(Compilation compilation, IPropertySymbol? propertySymbol)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var memberType = propertySymbol?.Type ?? fieldSymbol!.Type;
|
||||
|
||||
var memberVariantType = MarshalUtils.ConvertMarshalTypeToVariantType(marshalType)!.Value;
|
||||
|
||||
Reference in New Issue
Block a user