You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-08 12:40:44 +00:00
Add MustBeVariant attribute and analyzer
- MustBeVariant attribute can be used to enforce that generic types must be a marshable from/to Variant. - Also renames all diagnostic ids to be valid unicode identifiers.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
namespace Godot.SourceGenerators
|
namespace Godot.SourceGenerators
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ namespace Godot.SourceGenerators
|
|||||||
"must be declared with the partial modifier.";
|
"must be declared with the partial modifier.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0001",
|
new DiagnosticDescriptor(id: "GD0001",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -51,7 +52,7 @@ namespace Godot.SourceGenerators
|
|||||||
"containing types must be declared with the partial modifier.";
|
"containing types must be declared with the partial modifier.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0002",
|
new DiagnosticDescriptor(id: "GD0002",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -78,7 +79,7 @@ namespace Godot.SourceGenerators
|
|||||||
" Remove the 'static' modifier or the '[Export]' attribute.";
|
" Remove the 'static' modifier or the '[Export]' attribute.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0101",
|
new DiagnosticDescriptor(id: "GD0101",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -104,7 +105,7 @@ namespace Godot.SourceGenerators
|
|||||||
string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
|
string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0102",
|
new DiagnosticDescriptor(id: "GD0102",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -132,7 +133,7 @@ namespace Godot.SourceGenerators
|
|||||||
$"{message}. Exported properties must be writable.";
|
$"{message}. Exported properties must be writable.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0103",
|
new DiagnosticDescriptor(id: "GD0103",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -156,7 +157,7 @@ namespace Godot.SourceGenerators
|
|||||||
string description = $"{message}. Exported properties must be readable.";
|
string description = $"{message}. Exported properties must be readable.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0104",
|
new DiagnosticDescriptor(id: "GD0104",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -181,7 +182,7 @@ namespace Godot.SourceGenerators
|
|||||||
string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
|
string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0201",
|
new DiagnosticDescriptor(id: "GD0201",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -205,7 +206,7 @@ namespace Godot.SourceGenerators
|
|||||||
string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
|
string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0202",
|
new DiagnosticDescriptor(id: "GD0202",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -229,7 +230,7 @@ namespace Godot.SourceGenerators
|
|||||||
string description = $"{message}. Return void or remove the '[Signal]' attribute.";
|
string description = $"{message}. Return void or remove the '[Signal]' attribute.";
|
||||||
|
|
||||||
context.ReportDiagnostic(Diagnostic.Create(
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
new DiagnosticDescriptor(id: "GODOT-G0203",
|
new DiagnosticDescriptor(id: "GD0203",
|
||||||
title: message,
|
title: message,
|
||||||
messageFormat: message,
|
messageFormat: message,
|
||||||
category: "Usage",
|
category: "Usage",
|
||||||
@@ -239,5 +240,97 @@ namespace Godot.SourceGenerators
|
|||||||
location,
|
location,
|
||||||
location?.SourceTree?.FilePath));
|
location?.SourceTree?.FilePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
|
||||||
|
new DiagnosticDescriptor(id: "GD0301",
|
||||||
|
title: "The generic type argument must be a Variant compatible type",
|
||||||
|
messageFormat: "The generic type argument must be a Variant compatible type: {0}",
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
"The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
|
||||||
|
|
||||||
|
public static void ReportGenericTypeArgumentMustBeVariant(
|
||||||
|
SyntaxNodeAnalysisContext context,
|
||||||
|
SyntaxNode typeArgumentSyntax,
|
||||||
|
ISymbol typeArgumentSymbol)
|
||||||
|
{
|
||||||
|
string message = "The generic type argument " +
|
||||||
|
$"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
|
||||||
|
|
||||||
|
string description = $"{message}. Use a Variant compatible type as the generic type argument.";
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(id: "GD0301",
|
||||||
|
title: message,
|
||||||
|
messageFormat: message,
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description),
|
||||||
|
typeArgumentSyntax.GetLocation(),
|
||||||
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
|
||||||
|
new DiagnosticDescriptor(id: "GD0302",
|
||||||
|
title: "The generic type parameter must be annotated with the MustBeVariant attribute",
|
||||||
|
messageFormat: "The generic type argument must be a Variant type: {0}",
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
"The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
|
||||||
|
|
||||||
|
public static void ReportGenericTypeParameterMustBeVariantAnnotated(
|
||||||
|
SyntaxNodeAnalysisContext context,
|
||||||
|
SyntaxNode typeArgumentSyntax,
|
||||||
|
ISymbol typeArgumentSymbol)
|
||||||
|
{
|
||||||
|
string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
|
||||||
|
|
||||||
|
string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(id: "GD0302",
|
||||||
|
title: message,
|
||||||
|
messageFormat: message,
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description),
|
||||||
|
typeArgumentSyntax.GetLocation(),
|
||||||
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
|
||||||
|
new DiagnosticDescriptor(id: "GD0303",
|
||||||
|
title: "The generic type parameter must be annotated with the MustBeVariant attribute",
|
||||||
|
messageFormat: "The generic type argument must be a Variant type: {0}",
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
"The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
|
||||||
|
|
||||||
|
public static void ReportTypeArgumentParentSymbolUnhandled(
|
||||||
|
SyntaxNodeAnalysisContext context,
|
||||||
|
SyntaxNode typeArgumentSyntax,
|
||||||
|
ISymbol parentSymbol)
|
||||||
|
{
|
||||||
|
string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
|
||||||
|
"that must be Variant compatible was not handled.";
|
||||||
|
|
||||||
|
string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(
|
||||||
|
new DiagnosticDescriptor(id: "GD0303",
|
||||||
|
title: message,
|
||||||
|
messageFormat: message,
|
||||||
|
category: "Usage",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description),
|
||||||
|
typeArgumentSyntax.GetLocation(),
|
||||||
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,6 +177,9 @@ namespace Godot.SourceGenerators
|
|||||||
public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
|
public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
|
||||||
=> symbol.ToString() == GodotClasses.SignalAttr;
|
=> symbol.ToString() == GodotClasses.SignalAttr;
|
||||||
|
|
||||||
|
public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
|
||||||
|
=> symbol.ToString() == GodotClasses.MustBeVariantAttr;
|
||||||
|
|
||||||
public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
|
public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
|
||||||
=> symbol.ToString() == GodotClasses.GodotClassNameAttr;
|
=> symbol.ToString() == GodotClasses.GodotClassNameAttr;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Godot.SourceGenerators
|
|||||||
public const string ExportGroupAttr = "Godot.ExportGroupAttribute";
|
public const string ExportGroupAttr = "Godot.ExportGroupAttribute";
|
||||||
public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute";
|
public const string ExportSubgroupAttr = "Godot.ExportSubgroupAttribute";
|
||||||
public const string SignalAttr = "Godot.SignalAttribute";
|
public const string SignalAttr = "Godot.SignalAttribute";
|
||||||
|
public const string MustBeVariantAttr = "Godot.MustBeVariantAttribute";
|
||||||
public const string GodotClassNameAttr = "Godot.GodotClassName";
|
public const string GodotClassNameAttr = "Godot.GodotClassName";
|
||||||
public const string SystemFlagsAttr = "System.FlagsAttribute";
|
public const string SystemFlagsAttr = "System.FlagsAttribute";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ namespace Godot.SourceGenerators
|
|||||||
{
|
{
|
||||||
public INamedTypeSymbol GodotObjectType { get; }
|
public INamedTypeSymbol GodotObjectType { get; }
|
||||||
|
|
||||||
public TypeCache(GeneratorExecutionContext context)
|
public TypeCache(Compilation compilation)
|
||||||
{
|
{
|
||||||
INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
|
INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
|
||||||
{
|
{
|
||||||
return context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
|
return compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
|
||||||
throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
|
throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
|
||||||
|
namespace Godot.SourceGenerators
|
||||||
|
{
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class MustBeVariantAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
|
||||||
|
=> ImmutableArray.Create(
|
||||||
|
Common.GenericTypeArgumentMustBeVariantRule,
|
||||||
|
Common.GenericTypeParameterMustBeVariantAnnotatedRule,
|
||||||
|
Common.TypeArgumentParentSymbolUnhandledRule);
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.TypeArgumentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
|
||||||
|
{
|
||||||
|
var typeArgListSyntax = (TypeArgumentListSyntax)context.Node;
|
||||||
|
|
||||||
|
// Method invocation or variable declaration that contained the type arguments
|
||||||
|
var parentSyntax = context.Node.Parent;
|
||||||
|
Debug.Assert(parentSyntax != null);
|
||||||
|
|
||||||
|
var sm = context.SemanticModel;
|
||||||
|
|
||||||
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
|
for (int i = 0; i < typeArgListSyntax.Arguments.Count; i++)
|
||||||
|
{
|
||||||
|
var typeSyntax = typeArgListSyntax.Arguments[i];
|
||||||
|
var typeSymbol = sm.GetSymbolInfo(typeSyntax).Symbol as ITypeSymbol;
|
||||||
|
Debug.Assert(typeSymbol != null);
|
||||||
|
|
||||||
|
var parentSymbol = sm.GetSymbolInfo(parentSyntax).Symbol;
|
||||||
|
|
||||||
|
if (!ShouldCheckTypeArgument(context, parentSyntax, parentSymbol, typeSyntax, typeSymbol, i))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeSymbol is ITypeParameterSymbol typeParamSymbol)
|
||||||
|
{
|
||||||
|
if (!typeParamSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false))
|
||||||
|
{
|
||||||
|
Common.ReportGenericTypeParameterMustBeVariantAnnotated(context, typeSyntax, typeSymbol);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(typeSymbol, typeCache);
|
||||||
|
|
||||||
|
if (marshalType == null)
|
||||||
|
{
|
||||||
|
Common.ReportGenericTypeArgumentMustBeVariant(context, typeSyntax, typeSymbol);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the given type argument is being used in a type parameter that contains
|
||||||
|
/// the <c>MustBeVariantAttribute</c>; otherwise, we ignore the attribute.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">Context for a syntax node action.</param>
|
||||||
|
/// <param name="parentSyntax">The parent node syntax that contains the type node syntax.</param>
|
||||||
|
/// <param name="parentSymbol">The symbol retrieved for the parent node syntax.</param>
|
||||||
|
/// <param name="typeArgumentSyntax">The type node syntax of the argument type to check.</param>
|
||||||
|
/// <param name="typeArgumentSymbol">The symbol retrieved for the type node syntax.</param>
|
||||||
|
/// <returns><see langword="true"/> if the type must be variant and must be analyzed.</returns>
|
||||||
|
private bool ShouldCheckTypeArgument(SyntaxNodeAnalysisContext context, SyntaxNode parentSyntax, ISymbol parentSymbol, TypeSyntax typeArgumentSyntax, ITypeSymbol typeArgumentSymbol, int typeArgumentIndex)
|
||||||
|
{
|
||||||
|
var typeParamSymbol = parentSymbol switch
|
||||||
|
{
|
||||||
|
IMethodSymbol methodSymbol => methodSymbol.TypeParameters[typeArgumentIndex],
|
||||||
|
INamedTypeSymbol typeSymbol => typeSymbol.TypeParameters[typeArgumentIndex],
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeParamSymbol == null)
|
||||||
|
{
|
||||||
|
Common.ReportTypeArgumentParentSymbolUnhandled(context, typeArgumentSyntax, parentSymbol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeParamSymbol.GetAttributes()
|
||||||
|
.Any(a => a.AttributeClass?.IsGodotMustBeVariantAttribute() ?? false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ namespace Godot.SourceGenerators
|
|||||||
|
|
||||||
if (godotClasses.Length > 0)
|
if (godotClasses.Length > 0)
|
||||||
{
|
{
|
||||||
var typeCache = new MarshalUtils.TypeCache(context);
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
foreach (var godotClass in godotClasses)
|
foreach (var godotClass in godotClasses)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Godot.SourceGenerators
|
|||||||
|
|
||||||
if (godotClasses.Length > 0)
|
if (godotClasses.Length > 0)
|
||||||
{
|
{
|
||||||
var typeCache = new MarshalUtils.TypeCache(context);
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
foreach (var godotClass in godotClasses)
|
foreach (var godotClass in godotClasses)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Godot.SourceGenerators
|
|||||||
|
|
||||||
if (godotClasses.Length > 0)
|
if (godotClasses.Length > 0)
|
||||||
{
|
{
|
||||||
var typeCache = new MarshalUtils.TypeCache(context);
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
foreach (var godotClass in godotClasses)
|
foreach (var godotClass in godotClasses)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace Godot.SourceGenerators
|
|||||||
|
|
||||||
if (godotClasses.Length > 0)
|
if (godotClasses.Length > 0)
|
||||||
{
|
{
|
||||||
var typeCache = new MarshalUtils.TypeCache(context);
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
foreach (var godotClass in godotClasses)
|
foreach (var godotClass in godotClasses)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ namespace Godot.SourceGenerators
|
|||||||
|
|
||||||
if (godotClasses.Length > 0)
|
if (godotClasses.Length > 0)
|
||||||
{
|
{
|
||||||
var typeCache = new MarshalUtils.TypeCache(context);
|
var typeCache = new MarshalUtils.TypeCache(context.Compilation);
|
||||||
|
|
||||||
foreach (var godotClass in godotClasses)
|
foreach (var godotClass in godotClasses)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ namespace Godot.Collections
|
|||||||
/// <typeparam name="T">The type of the array.</typeparam>
|
/// <typeparam name="T">The type of the array.</typeparam>
|
||||||
[SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
|
[SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
|
||||||
[SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
|
[SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
|
||||||
public sealed class Array<T> :
|
public sealed class Array<[MustBeVariant] T> :
|
||||||
IList<T>,
|
IList<T>,
|
||||||
IReadOnlyList<T>,
|
IReadOnlyList<T>,
|
||||||
ICollection<T>,
|
ICollection<T>,
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Godot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute that restricts generic type parameters to be only types
|
||||||
|
/// that can be marshaled from/to a <see cref="Variant"/>.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.GenericParameter)]
|
||||||
|
public class MustBeVariantAttribute : Attribute { }
|
||||||
|
}
|
||||||
@@ -352,7 +352,7 @@ namespace Godot.Collections
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
|
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
|
||||||
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
|
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
|
||||||
public class Dictionary<TKey, TValue> :
|
public class Dictionary<[MustBeVariant] TKey, [MustBeVariant] TValue> :
|
||||||
IDictionary<TKey, TValue>,
|
IDictionary<TKey, TValue>,
|
||||||
IReadOnlyDictionary<TKey, TValue>
|
IReadOnlyDictionary<TKey, TValue>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
<Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
|
<Compile Include="Core\Attributes\ExportCategoryAttribute.cs" />
|
||||||
<Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
|
<Compile Include="Core\Attributes\ExportGroupAttribute.cs" />
|
||||||
<Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
|
<Compile Include="Core\Attributes\ExportSubgroupAttribute.cs" />
|
||||||
|
<Compile Include="Core\Attributes\MustBeVariantAttribute.cs" />
|
||||||
<Compile Include="Core\Attributes\RPCAttribute.cs" />
|
<Compile Include="Core\Attributes\RPCAttribute.cs" />
|
||||||
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
|
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
|
||||||
<Compile Include="Core\Attributes\SignalAttribute.cs" />
|
<Compile Include="Core\Attributes\SignalAttribute.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user