| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  | using System; | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  | using System.Collections.Generic; | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  | using System.Collections.Immutable; | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  | using System.Linq; | 
					
						
							|  |  |  | using Microsoft.CodeAnalysis; | 
					
						
							|  |  |  | using Microsoft.CodeAnalysis.CSharp; | 
					
						
							|  |  |  | using Microsoft.CodeAnalysis.CSharp.Syntax; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Godot.SourceGenerators | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static class ExtensionMethods | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         public static bool TryGetGlobalAnalyzerProperty( | 
					
						
							|  |  |  |             this GeneratorExecutionContext context, string property, out string? value | 
					
						
							|  |  |  |         ) => context.AnalyzerConfigOptions.GlobalOptions | 
					
						
							|  |  |  |             .TryGetValue("build_property." + property, out value); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  |         public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context) | 
					
						
							|  |  |  |             => context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) && | 
					
						
							|  |  |  |                toggle != null && | 
					
						
							|  |  |  |                toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  |         public static bool IsGodotToolsProject(this GeneratorExecutionContext context) | 
					
						
							|  |  |  |             => context.TryGetGlobalAnalyzerProperty("IsGodotToolsProject", out string? toggle) && | 
					
						
							|  |  |  |                toggle != null && | 
					
						
							|  |  |  |                toggle.Equals("true", StringComparison.OrdinalIgnoreCase); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |         public static bool InheritsFrom(this INamedTypeSymbol? symbol, string assemblyName, string typeFullName) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |             while (symbol != null) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |                 if (symbol.ContainingAssembly.Name == assemblyName && | 
					
						
							|  |  |  |                     symbol.ToString() == typeFullName) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |                 { | 
					
						
							|  |  |  |                     return true; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |                 symbol = symbol.BaseType; | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |         public static INamedTypeSymbol? GetGodotScriptNativeClass(this INamedTypeSymbol classTypeSymbol) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var symbol = classTypeSymbol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             while (symbol != null) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (symbol.ContainingAssembly.Name == "GodotSharp") | 
					
						
							|  |  |  |                     return symbol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 symbol = symbol.BaseType; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static string? GetGodotScriptNativeClassName(this INamedTypeSymbol classTypeSymbol) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var nativeType = classTypeSymbol.GetGodotScriptNativeClass(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (nativeType == null) | 
					
						
							|  |  |  |                 return null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var godotClassNameAttr = nativeType.GetAttributes() | 
					
						
							|  |  |  |                 .FirstOrDefault(a => a.AttributeClass?.IsGodotClassNameAttribute() ?? false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             string? godotClassName = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (godotClassNameAttr is { ConstructorArguments: { Length: > 0 } }) | 
					
						
							|  |  |  |                 godotClassName = godotClassNameAttr.ConstructorArguments[0].Value?.ToString(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return godotClassName ?? nativeType.Name; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |         private static bool IsGodotScriptClass( | 
					
						
							|  |  |  |             this ClassDeclarationSyntax cds, Compilation compilation, | 
					
						
							|  |  |  |             out INamedTypeSymbol? symbol | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             var sm = compilation.GetSemanticModel(cds.SyntaxTree); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             var classTypeSymbol = sm.GetDeclaredSymbol(cds); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (classTypeSymbol?.BaseType == null | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |                 || !classTypeSymbol.BaseType.InheritsFrom("GodotSharp", GodotClasses.Object)) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |             { | 
					
						
							|  |  |  |                 symbol = null; | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             symbol = classTypeSymbol; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectGodotScriptClasses( | 
					
						
							|  |  |  |             this IEnumerable<ClassDeclarationSyntax> source, | 
					
						
							|  |  |  |             Compilation compilation | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             foreach (var cds in source) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (cds.IsGodotScriptClass(compilation, out var symbol)) | 
					
						
							|  |  |  |                     yield return (cds, symbol!); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  |         public static bool IsNested(this TypeDeclarationSyntax cds) | 
					
						
							|  |  |  |             => cds.Parent is TypeDeclarationSyntax; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static bool IsPartial(this TypeDeclarationSyntax cds) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |             => cds.Modifiers.Any(SyntaxKind.PartialKeyword); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  |         public static bool AreAllOuterTypesPartial( | 
					
						
							|  |  |  |             this TypeDeclarationSyntax cds, | 
					
						
							|  |  |  |             out TypeDeclarationSyntax? typeMissingPartial | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             SyntaxNode? outerSyntaxNode = cds.Parent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             while (outerSyntaxNode is TypeDeclarationSyntax outerTypeDeclSyntax) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (!outerTypeDeclSyntax.IsPartial()) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                     typeMissingPartial = outerTypeDeclSyntax; | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 outerSyntaxNode = outerSyntaxNode.Parent; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             typeMissingPartial = null; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static string GetDeclarationKeyword(this INamedTypeSymbol namedTypeSymbol) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             string? keyword = namedTypeSymbol.DeclaringSyntaxReferences | 
					
						
							|  |  |  |                 .OfType<TypeDeclarationSyntax>().FirstOrDefault()? | 
					
						
							|  |  |  |                 .Keyword.Text; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return keyword ?? namedTypeSymbol.TypeKind switch | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 TypeKind.Interface => "interface", | 
					
						
							|  |  |  |                 TypeKind.Struct => "struct", | 
					
						
							|  |  |  |                 _ => "class" | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |         private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } = | 
					
						
							|  |  |  |             SymbolDisplayFormat.FullyQualifiedFormat | 
					
						
							|  |  |  |                 .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:25:16 +01:00
										 |  |  |         public static string FullQualifiedName(this ITypeSymbol symbol) | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |             => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal); | 
					
						
							| 
									
										
										
										
											2021-03-13 01:04:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |         public static string NameWithTypeParameters(this INamedTypeSymbol symbol) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return symbol.IsGenericType ? | 
					
						
							|  |  |  |                 string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") : | 
					
						
							|  |  |  |                 symbol.Name; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-13 01:04:55 +01:00
										 |  |  |         public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol) | 
					
						
							|  |  |  |             => namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal); | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName) | 
					
						
							|  |  |  |             => qualifiedName | 
					
						
							|  |  |  |                 // AddSource() doesn't support angle brackets | 
					
						
							|  |  |  |                 .Replace("<", "(Of ") | 
					
						
							|  |  |  |                 .Replace(">", ")"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol) | 
					
						
							|  |  |  |             => symbol.ToString() == GodotClasses.ExportAttr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol) | 
					
						
							|  |  |  |             => symbol.ToString() == GodotClasses.GodotClassNameAttr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol) | 
					
						
							|  |  |  |             => symbol.ToString() == GodotClasses.SystemFlagsAttr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature( | 
					
						
							|  |  |  |             this IEnumerable<IMethodSymbol> methods, | 
					
						
							|  |  |  |             MarshalUtils.TypeCache typeCache | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             foreach (var method in methods) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 if (method.IsGenericMethod) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:46 +01:00
										 |  |  |                 var retSymbol = method.ReturnType; | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |                 var retType = method.ReturnsVoid ? | 
					
						
							|  |  |  |                     null : | 
					
						
							|  |  |  |                     MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (retType == null && !method.ReturnsVoid) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var parameters = method.Parameters; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var paramTypes = parameters | 
					
						
							|  |  |  |                     // Currently we don't support `ref`, `out`, `in`, `ref readonly` parameters (and we never may) | 
					
						
							|  |  |  |                     .Where(p => p.RefKind == RefKind.None) | 
					
						
							|  |  |  |                     // Attempt to determine the variant type | 
					
						
							|  |  |  |                     .Select(p => MarshalUtils.ConvertManagedTypeToMarshalType(p.Type, typeCache)) | 
					
						
							|  |  |  |                     // Discard parameter types that couldn't be determined (null entries) | 
					
						
							|  |  |  |                     .Where(t => t != null).Cast<MarshalType>().ToImmutableArray(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // If any parameter type was incompatible, it was discarded so the length won't match | 
					
						
							|  |  |  |                 if (parameters.Length > paramTypes.Length) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 yield return new GodotMethodData(method, paramTypes, parameters | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:46 +01:00
										 |  |  |                     .Select(p => p.Type).ToImmutableArray(), retType, retSymbol); | 
					
						
							| 
									
										
										
										
											2022-02-27 21:57:30 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static IEnumerable<GodotPropertyData> WhereIsGodotCompatibleType( | 
					
						
							|  |  |  |             this IEnumerable<IPropertySymbol> properties, | 
					
						
							|  |  |  |             MarshalUtils.TypeCache typeCache | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             foreach (var property in properties) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 // Ignore properties without a getter. Godot properties must be readable. | 
					
						
							|  |  |  |                 if (property.IsWriteOnly) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(property.Type, typeCache); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (marshalType == null) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 yield return new GodotPropertyData(property, marshalType.Value); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         public static IEnumerable<GodotFieldData> WhereIsGodotCompatibleType( | 
					
						
							|  |  |  |             this IEnumerable<IFieldSymbol> fields, | 
					
						
							|  |  |  |             MarshalUtils.TypeCache typeCache | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             foreach (var field in fields) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |                 var marshalType = MarshalUtils.ConvertManagedTypeToMarshalType(field.Type, typeCache); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (marshalType == null) | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 yield return new GodotFieldData(field, marshalType.Value); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-03-06 00:12:42 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } |