using System; using System.Collections.Generic; 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); public static bool AreGodotSourceGeneratorsDisabled(this GeneratorExecutionContext context) => context.TryGetGlobalAnalyzerProperty("GodotSourceGenerators", out string? toggle) && toggle != null && toggle.Equals("disabled", StringComparison.OrdinalIgnoreCase); public static bool IsGodotToolsProject(this GeneratorExecutionContext context) => context.TryGetGlobalAnalyzerProperty("IsGodotToolsProject", out string? toggle) && toggle != null && toggle.Equals("true", StringComparison.OrdinalIgnoreCase); private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName) { if (symbol == null) return false; while (true) { if (symbol.ToString() == baseName) { return true; } if (symbol.BaseType != null) { symbol = symbol.BaseType; continue; } break; } return false; } 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 || !classTypeSymbol.BaseType.InheritsFrom(GodotClasses.Object)) { symbol = null; return false; } symbol = classTypeSymbol; return true; } public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectGodotScriptClasses( this IEnumerable source, Compilation compilation ) { foreach (var cds in source) { if (cds.IsGodotScriptClass(compilation, out var symbol)) yield return (cds, symbol!); } } public static bool IsNested(this TypeDeclarationSyntax cds) => cds.Parent is TypeDeclarationSyntax; public static bool IsPartial(this TypeDeclarationSyntax cds) => cds.Modifiers.Any(SyntaxKind.PartialKeyword); 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().FirstOrDefault()? .Keyword.Text; return keyword ?? namedTypeSymbol.TypeKind switch { TypeKind.Interface => "interface", TypeKind.Struct => "struct", _ => "class" }; } private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } = SymbolDisplayFormat.FullyQualifiedFormat .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted); public static string FullQualifiedName(this ITypeSymbol symbol) => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal); public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol) => namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal); } }