C#: Add initial implementation of source generator for script members

This replaces the way we invoke methods and set/get properties.
This first iteration rids us of runtime type checking in those
cases, as it's now done at compile time.
Later it will also stop needing the use of reflection. After that,
we will only depend on reflection for generic Godot Array and
Dictionary. We're stuck with reflection in generic collections
for now as C# doesn't support generic/template specialization.

This is only the initial implementation. Further iterations are
coming, specially once we switch to the native extension system
which completely changes the way members are accessed/invoked.
For example, with the native extension system we will likely need
to create `UnmanagedCallersOnly` invoke wrapper methods and return
function pointers to the engine.

Other kind of members, like event signals will be receiving the
same treatment in the future.
This commit is contained in:
Ignacio Roldán Etcheverry
2021-12-28 23:25:16 +01:00
parent e5e7a795b1
commit 4d710bf659
20 changed files with 908 additions and 189 deletions
@@ -14,9 +14,8 @@ namespace Godot.SourceGenerators
"Missing partial modifier on declaration of type '" +
$"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
string description = $"{message}. Subclasses of '{GodotClasses.Object}' must be " +
"declared with the partial modifier or annotated with the " +
$"attribute '{GodotClasses.DisableGodotGeneratorsAttr}'.";
string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
"must be declared with the partial modifier.";
context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(id: "GODOT-G0001",
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -13,6 +14,11 @@ namespace Godot.SourceGenerators
) => context.AnalyzerConfigOptions.GlobalOptions
.TryGetValue("build_property." + property, out value);
public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context)
=> context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) &&
toggle != null &&
toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase);
private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
{
if (symbol == null)
@@ -72,15 +78,11 @@ namespace Godot.SourceGenerators
public static bool IsPartial(this ClassDeclarationSyntax cds)
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
=> symbol.GetAttributes().Any(attr =>
attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
public static string FullQualifiedName(this INamedTypeSymbol symbol)
public static string FullQualifiedName(this ITypeSymbol symbol)
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
@@ -2,6 +2,6 @@
<ItemGroup>
<!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
<CompilerVisibleProperty Include="GodotProjectDir" />
<CompilerVisibleProperty Include="GodotScriptPathAttributeGenerator" />
<CompilerVisibleProperty Include="EnableGodotGenerators" />
</ItemGroup>
</Project>
@@ -3,7 +3,6 @@ namespace Godot.SourceGenerators
public static class GodotClasses
{
public const string Object = "Godot.Object";
public const string DisableGodotGeneratorsAttr = "Godot.DisableGodotGeneratorsAttribute";
public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
}
}
@@ -0,0 +1,78 @@
using System.Diagnostics.CodeAnalysis;
namespace Godot.SourceGenerators
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum MarshalType
{
Boolean,
Char,
SByte,
Int16,
Int32,
Int64,
Byte,
UInt16,
UInt32,
UInt64,
Single,
Double,
String,
// Godot structs
Vector2,
Vector2i,
Rect2,
Rect2i,
Transform2D,
Vector3,
Vector3i,
Basis,
Quaternion,
Transform3D,
AABB,
Color,
Plane,
Callable,
SignalInfo,
// Enums
Enum,
// Arrays
ByteArray,
Int32Array,
Int64Array,
SingleArray,
DoubleArray,
StringArray,
Vector2Array,
Vector3Array,
ColorArray,
GodotObjectOrDerivedArray,
SystemObjectArray,
// Generics
GodotGenericDictionary,
GodotGenericArray,
SystemGenericDictionary,
SystemGenericList,
GenericIDictionary,
GenericICollection,
GenericIEnumerable,
// Variant
SystemObject,
// Classes
GodotObjectOrDerived,
StringName,
NodePath,
RID,
GodotDictionary,
GodotArray,
IDictionary,
ICollection,
IEnumerable,
}
}
@@ -0,0 +1,224 @@
using System;
using Microsoft.CodeAnalysis;
namespace Godot.SourceGenerators
{
public static class MarshalUtils
{
public class TypeCache
{
public INamedTypeSymbol GodotObjectType { get; }
public INamedTypeSymbol GodotGenericDictionary { get; }
public INamedTypeSymbol GodotGenericArray { get; }
public INamedTypeSymbol IDictionary { get; }
public INamedTypeSymbol ICollection { get; }
public INamedTypeSymbol GenericIDictionary { get; }
public INamedTypeSymbol SystemGenericDictionary { get; }
public INamedTypeSymbol SystemGenericList { get; }
public TypeCache(GeneratorExecutionContext context)
{
INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
{
return context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
}
GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object");
GodotGenericDictionary = GetTypeByMetadataNameOrThrow("Godot.Collections.Dictionary`2");
GodotGenericArray = GetTypeByMetadataNameOrThrow("Godot.Collections.Array`1");
IDictionary = GetTypeByMetadataNameOrThrow("System.Collections.IDictionary");
ICollection = GetTypeByMetadataNameOrThrow("System.Collections.ICollection");
GenericIDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.IDictionary`2");
SystemGenericDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.Dictionary`2");
SystemGenericList = GetTypeByMetadataNameOrThrow("System.Collections.Generic.List`1");
}
}
public static MarshalType? ConvertManagedTypeToVariantType(ITypeSymbol type, TypeCache typeCache)
{
var specialType = type.SpecialType;
switch (specialType)
{
case SpecialType.System_Boolean:
return MarshalType.Boolean;
case SpecialType.System_Char:
return MarshalType.Char;
case SpecialType.System_SByte:
return MarshalType.SByte;
case SpecialType.System_Int16:
return MarshalType.Int16;
case SpecialType.System_Int32:
return MarshalType.Int32;
case SpecialType.System_Int64:
return MarshalType.Int64;
case SpecialType.System_Byte:
return MarshalType.Byte;
case SpecialType.System_UInt16:
return MarshalType.UInt16;
case SpecialType.System_UInt32:
return MarshalType.UInt32;
case SpecialType.System_UInt64:
return MarshalType.UInt64;
case SpecialType.System_Single:
return MarshalType.Single;
case SpecialType.System_Double:
return MarshalType.Double;
case SpecialType.System_String:
return MarshalType.String;
case SpecialType.System_Object:
return MarshalType.SystemObject;
case SpecialType.System_ValueType:
{
if (type.ContainingAssembly.Name == "GodotSharp" &&
type.ContainingNamespace.Name == "Godot")
{
return type switch
{
{ Name: "Vector2" } => MarshalType.Vector2,
{ Name: "Vector2i" } => MarshalType.Vector2i,
{ Name: "Rect2" } => MarshalType.Rect2,
{ Name: "Rect2i" } => MarshalType.Rect2i,
{ Name: "Transform2D" } => MarshalType.Transform2D,
{ Name: "Vector3" } => MarshalType.Vector3,
{ Name: "Vector3i" } => MarshalType.Vector3i,
{ Name: "Basis" } => MarshalType.Basis,
{ Name: "Quaternion" } => MarshalType.Quaternion,
{ Name: "Transform3D" } => MarshalType.Transform3D,
{ Name: "AABB" } => MarshalType.AABB,
{ Name: "Color" } => MarshalType.Color,
{ Name: "Plane" } => MarshalType.Plane,
{ Name: "RID" } => MarshalType.RID,
{ Name: "Callable" } => MarshalType.Callable,
{ Name: "SignalInfo" } => MarshalType.SignalInfo,
{ TypeKind: TypeKind.Enum } => MarshalType.Enum,
_ => null
};
}
return null;
}
default:
{
if (type.TypeKind == TypeKind.Array)
{
var arrayType = (IArrayTypeSymbol)type;
var elementType = arrayType.ElementType;
switch (elementType.SpecialType)
{
case SpecialType.System_Byte:
return MarshalType.ByteArray;
case SpecialType.System_Int32:
return MarshalType.Int32Array;
case SpecialType.System_Int64:
return MarshalType.Int64Array;
case SpecialType.System_Single:
return MarshalType.SingleArray;
case SpecialType.System_Double:
return MarshalType.DoubleArray;
case SpecialType.System_String:
return MarshalType.StringArray;
case SpecialType.System_Object:
return MarshalType.SystemObjectArray;
}
if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
return MarshalType.GodotObjectOrDerivedArray;
if (type.ContainingAssembly.Name == "GodotSharp" &&
type.ContainingNamespace.Name == "Godot")
{
return elementType switch
{
{ Name: "Vector2" } => MarshalType.Vector2Array,
{ Name: "Vector3" } => MarshalType.Vector3Array,
{ Name: "Color" } => MarshalType.ColorArray,
_ => null
};
}
}
else if (type is INamedTypeSymbol { IsGenericType: true } genericType)
{
var genericTypeDef = genericType.ConstructedFrom;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericDictionary))
return MarshalType.GodotGenericDictionary;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericArray))
return MarshalType.GodotGenericArray;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericDictionary))
return MarshalType.SystemGenericDictionary;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericList))
return MarshalType.SystemGenericList;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GenericIDictionary))
return MarshalType.GenericIDictionary;
return genericTypeDef.SpecialType switch
{
SpecialType.System_Collections_Generic_ICollection_T => MarshalType.GenericICollection,
SpecialType.System_Collections_Generic_IEnumerable_T => MarshalType.GenericIEnumerable,
_ => null
};
}
else
{
if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
return MarshalType.GodotObjectOrDerived;
if (SymbolEqualityComparer.Default.Equals(type, typeCache.IDictionary))
return MarshalType.IDictionary;
if (SymbolEqualityComparer.Default.Equals(type, typeCache.ICollection))
return MarshalType.ICollection;
if (specialType == SpecialType.System_Collections_IEnumerable)
return MarshalType.IEnumerable;
if (type.ContainingAssembly.Name == "GodotSharp")
{
switch (type.ContainingNamespace.Name)
{
case "Godot":
return type switch
{
{ Name: "StringName" } => MarshalType.StringName,
{ Name: "NodePath" } => MarshalType.NodePath,
_ => null
};
case "Godot.Collections" when !(type is INamedTypeSymbol { IsGenericType: true }):
return type switch
{
{ Name: "Dictionary" } => MarshalType.GodotDictionary,
{ Name: "Array" } => MarshalType.GodotArray,
_ => null
};
}
}
}
break;
}
}
return null;
}
private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
{
while (type != null)
{
if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
return true;
type = type.BaseType;
}
return false;
}
}
}
@@ -0,0 +1,423 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Godot.SourceGenerators
{
[Generator]
public class ScriptBoilerplateGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
if (context.AreGodotSourceGeneratorsDisabled())
return;
// False positive for RS1024. We're already using `SymbolEqualityComparer.Default`...
#pragma warning disable RS1024
INamedTypeSymbol[] godotClasses = context
.Compilation.SyntaxTrees
.SelectMany(tree =>
tree.GetRoot().DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.SelectGodotScriptClasses(context.Compilation)
// Report and skip non-partial classes
.Where(x =>
{
if (x.cds.IsPartial())
return true;
Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
return false;
})
.Select(x => x.symbol)
)
.Distinct<INamedTypeSymbol>(SymbolEqualityComparer.Default)
.ToArray();
#pragma warning restore RS1024
if (godotClasses.Length > 0)
{
var typeCache = new MarshalUtils.TypeCache(context);
foreach (var godotClass in godotClasses)
{
VisitGodotScriptClass(context, typeCache, godotClass);
}
}
}
private static void VisitGodotScriptClass(
GeneratorExecutionContext context,
MarshalUtils.TypeCache typeCache,
INamedTypeSymbol symbol
)
{
string className = symbol.Name;
INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
namespaceSymbol.FullQualifiedName() :
string.Empty;
bool hasNamespace = classNs.Length != 0;
string uniqueName = hasNamespace ?
classNs + "." + className + "_ScriptBoilerplate_Generated" :
className + "_ScriptBoilerplate_Generated";
var source = new StringBuilder();
source.Append("using Godot;\n");
source.Append("using Godot.NativeInterop;\n");
source.Append("\n");
if (hasNamespace)
{
source.Append("namespace ");
source.Append(classNs);
source.Append(" {\n\n");
}
source.Append("partial class ");
source.Append(className);
source.Append("\n{\n");
var members = symbol.GetMembers();
// TODO: Static static marshaling (no reflection, no runtime type checks)
var methodSymbols = members
.Where(s => s.Kind == SymbolKind.Method)
.Cast<IMethodSymbol>()
.Where(m => m.MethodKind == MethodKind.Ordinary && !m.IsImplicitlyDeclared);
var propertySymbols = members
.Where(s => s.Kind == SymbolKind.Property)
.Cast<IPropertySymbol>();
var fieldSymbols = members
.Where(s => s.Kind == SymbolKind.Field)
.Cast<IFieldSymbol>()
.Where(p => !p.IsImplicitlyDeclared);
var methods = WhereHasCompatibleGodotType(methodSymbols, typeCache).ToArray();
var properties = WhereIsCompatibleGodotType(propertySymbols, typeCache).ToArray();
var fields = WhereIsCompatibleGodotType(fieldSymbols, typeCache).ToArray();
source.Append(" private class GodotInternal {\n");
// Generate cached StringNames for methods and properties, for fast lookup
foreach (var method in methods)
{
string methodName = method.Method.Name;
source.Append(" public static readonly StringName MethodName_");
source.Append(methodName);
source.Append(" = \"");
source.Append(methodName);
source.Append("\";\n");
}
foreach (var property in properties)
{
string propertyName = property.Property.Name;
source.Append(" public static readonly StringName PropName_");
source.Append(propertyName);
source.Append(" = \"");
source.Append(propertyName);
source.Append("\";\n");
}
foreach (var field in fields)
{
string fieldName = field.Field.Name;
source.Append(" public static readonly StringName PropName_");
source.Append(fieldName);
source.Append(" = \"");
source.Append(fieldName);
source.Append("\";\n");
}
source.Append(" }\n");
if (methods.Length > 0)
{
source.Append(" protected override bool InvokeGodotClassMethod(in godot_string_name method, ");
source.Append("NativeVariantPtrArgs args, int argCount, out godot_variant ret)\n {\n");
foreach (var method in methods)
{
GenerateMethodInvoker(method, source);
}
source.Append(" return base.InvokeGodotClassMethod(method, args, argCount, out ret);\n");
source.Append(" }\n");
}
if (properties.Length > 0 || fields.Length > 0)
{
// Setters
source.Append(" protected override bool SetGodotClassPropertyValue(in godot_string_name name, ");
source.Append("in godot_variant value)\n {\n");
foreach (var property in properties)
{
GeneratePropertySetter(property.Property.Name,
property.Property.Type.FullQualifiedName(), source);
}
foreach (var field in fields)
{
GeneratePropertySetter(field.Field.Name,
field.Field.Type.FullQualifiedName(), source);
}
source.Append(" return base.SetGodotClassPropertyValue(name, value);\n");
source.Append(" }\n");
// Getters
source.Append(" protected override bool GetGodotClassPropertyValue(in godot_string_name name, ");
source.Append("out godot_variant value)\n {\n");
foreach (var property in properties)
{
GeneratePropertyGetter(property.Property.Name, source);
}
foreach (var field in fields)
{
GeneratePropertyGetter(field.Field.Name, source);
}
source.Append(" return base.GetGodotClassPropertyValue(name, out value);\n");
source.Append(" }\n");
}
source.Append("}\n");
if (hasNamespace)
{
source.Append("\n}\n");
}
context.AddSource(uniqueName, SourceText.From(source.ToString(), Encoding.UTF8));
}
private static void GenerateMethodInvoker(
GodotMethodInfo method,
StringBuilder source
)
{
string methodName = method.Method.Name;
source.Append(" if (method == GodotInternal.MethodName_");
source.Append(methodName);
source.Append(" && argCount == ");
source.Append(method.ParamTypes.Length);
source.Append(") {\n");
if (method.RetType != null)
source.Append(" object retBoxed = ");
else
source.Append(" ");
source.Append(methodName);
source.Append("(");
for (int i = 0; i < method.ParamTypes.Length; i++)
{
if (i != 0)
source.Append(", ");
// TODO: static marshaling (no reflection, no runtime type checks)
string paramTypeQualifiedName = method.ParamTypeSymbols[i].FullQualifiedName();
source.Append("(");
source.Append(paramTypeQualifiedName);
source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(args[");
source.Append(i);
source.Append("], typeof(");
source.Append(paramTypeQualifiedName);
source.Append("))");
}
source.Append(");\n");
if (method.RetType != null)
{
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append(" ret = Marshaling.ConvertManagedObjectToVariant(retBoxed);\n");
source.Append(" return true;\n");
}
else
{
source.Append(" ret = default;\n");
source.Append(" return true;\n");
}
source.Append(" }\n");
}
private static void GeneratePropertySetter(
string propertyMemberName,
string propertyTypeQualifiedName,
StringBuilder source
)
{
source.Append(" if (name == GodotInternal.PropName_");
source.Append(propertyMemberName);
source.Append(") {\n");
source.Append(" ");
source.Append(propertyMemberName);
source.Append(" = ");
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append("(");
source.Append(propertyTypeQualifiedName);
source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(value, typeof(");
source.Append(propertyTypeQualifiedName);
source.Append("));\n");
source.Append(" return true;\n");
source.Append(" }\n");
}
private static void GeneratePropertyGetter(
string propertyMemberName,
StringBuilder source
)
{
source.Append(" if (name == GodotInternal.PropName_");
source.Append(propertyMemberName);
source.Append(") {\n");
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append(" value = Marshaling.ConvertManagedObjectToVariant(");
source.Append(propertyMemberName);
source.Append(");\n");
source.Append(" return true;\n");
source.Append(" }\n");
}
public void Initialize(GeneratorInitializationContext context)
{
}
private struct GodotMethodInfo
{
public GodotMethodInfo(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType)
{
Method = method;
ParamTypes = paramTypes;
ParamTypeSymbols = paramTypeSymbols;
RetType = retType;
}
public IMethodSymbol Method { get; }
public ImmutableArray<MarshalType> ParamTypes { get; }
public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; }
public MarshalType? RetType { get; }
}
private struct GodotPropertyInfo
{
public GodotPropertyInfo(IPropertySymbol property, MarshalType type)
{
Property = property;
Type = type;
}
public IPropertySymbol Property { get; }
public MarshalType Type { get; }
}
private struct GodotFieldInfo
{
public GodotFieldInfo(IFieldSymbol field, MarshalType type)
{
Field = field;
Type = type;
}
public IFieldSymbol Field { get; }
public MarshalType Type { get; }
}
private static IEnumerable<GodotMethodInfo> WhereHasCompatibleGodotType(
IEnumerable<IMethodSymbol> methods,
MarshalUtils.TypeCache typeCache
)
{
foreach (var method in methods)
{
if (method.IsGenericMethod)
continue;
var retType = method.ReturnsVoid ?
null :
MarshalUtils.ConvertManagedTypeToVariantType(method.ReturnType, typeCache);
if (retType == null && !method.ReturnsVoid)
continue;
var parameters = method.Parameters;
var paramTypes = parameters.Select(p =>
MarshalUtils.ConvertManagedTypeToVariantType(p.Type, typeCache))
.Where(t => t != null).Cast<MarshalType>().ToImmutableArray();
if (parameters.Length > paramTypes.Length)
continue; // Some param types weren't compatible
yield return new GodotMethodInfo(method, paramTypes, parameters
.Select(p => p.Type).ToImmutableArray(), retType);
}
}
private static IEnumerable<GodotPropertyInfo> WhereIsCompatibleGodotType(
IEnumerable<IPropertySymbol> properties,
MarshalUtils.TypeCache typeCache
)
{
foreach (var property in properties)
{
var marshalType = MarshalUtils.ConvertManagedTypeToVariantType(property.Type, typeCache);
if (marshalType == null)
continue;
yield return new GodotPropertyInfo(property, marshalType.Value);
}
}
private static IEnumerable<GodotFieldInfo> WhereIsCompatibleGodotType(
IEnumerable<IFieldSymbol> fields,
MarshalUtils.TypeCache typeCache
)
{
foreach (var field in fields)
{
var marshalType = MarshalUtils.ConvertManagedTypeToVariantType(field.Type, typeCache);
if (marshalType == null)
continue;
yield return new GodotFieldInfo(field, marshalType.Value);
}
}
}
}
@@ -14,13 +14,10 @@ namespace Godot.SourceGenerators
{
public void Execute(GeneratorExecutionContext context)
{
if (context.TryGetGlobalAnalyzerProperty("GodotScriptPathAttributeGenerator", out string? toggle)
&& toggle == "disabled")
{
if (context.AreGodotSourceGeneratorsDisabled())
return;
}
// NOTE: IsNullOrEmpty doesn't work well with nullable checks
// NOTE: NotNullWhen diagnostics don't work on projects targeting .NET Standard 2.0
// ReSharper disable once ReplaceWithStringIsNullOrEmpty
if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
|| godotProjectDir!.Length == 0)
@@ -28,7 +25,8 @@ namespace Godot.SourceGenerators
throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
}
var godotClasses = context.Compilation.SyntaxTrees
Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses = context
.Compilation.SyntaxTrees
.SelectMany(tree =>
tree.GetRoot().DescendantNodes()
.OfType<ClassDeclarationSyntax>()
@@ -38,7 +36,7 @@ namespace Godot.SourceGenerators
// Report and skip non-partial classes
.Where(x =>
{
if (x.cds.IsPartial() || x.symbol.HasDisableGeneratorsAttribute())
if (x.cds.IsPartial())
return true;
Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
return false;
@@ -0,0 +1,19 @@
using Microsoft.CodeAnalysis;
namespace Godot.SourceGenerators
{
// Placeholder. Once we switch to native extensions this will act as the registrar for all
// user Godot classes in the assembly. Think of it as something similar to `register_types`.
public class ScriptRegistrarGenerator : ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
throw new System.NotImplementedException();
}
public void Execute(GeneratorExecutionContext context)
{
throw new System.NotImplementedException();
}
}
}