summaryrefslogtreecommitdiff
path: root/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
blob: 37f7005d01594dceab50f8edac81429fd8786d2e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Godot.SourceGenerators.Internal;

internal static class ExtensionMethods
{
    public static AttributeData? GetGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
        => symbol.GetAttributes()
            .FirstOrDefault(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false);

    private static bool HasGenerateUnmanagedCallbacksAttribute(
        this ClassDeclarationSyntax cds, Compilation compilation,
        out INamedTypeSymbol? symbol
    )
    {
        var sm = compilation.GetSemanticModel(cds.SyntaxTree);

        var classTypeSymbol = sm.GetDeclaredSymbol(cds);
        if (classTypeSymbol == null)
        {
            symbol = null;
            return false;
        }

        if (!classTypeSymbol.GetAttributes()
                .Any(a => a.AttributeClass?.IsGenerateUnmanagedCallbacksAttribute() ?? false))
        {
            symbol = null;
            return false;
        }

        symbol = classTypeSymbol;
        return true;
    }

    private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
        => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;

    public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
        this IEnumerable<ClassDeclarationSyntax> source,
        Compilation compilation
    )
    {
        foreach (var cds in source)
        {
            if (cds.HasGenerateUnmanagedCallbacksAttribute(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<TypeDeclarationSyntax>().FirstOrDefault()?
            .Keyword.Text;

        return keyword ?? namedTypeSymbol.TypeKind switch
        {
            TypeKind.Interface => "interface",
            TypeKind.Struct => "struct",
            _ => "class"
        };
    }

    public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
    {
        return symbol.IsGenericType ?
            string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
            symbol.Name;
    }

    private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
        SymbolDisplayFormat.FullyQualifiedFormat
            .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);

    private static SymbolDisplayFormat FullyQualifiedFormatIncludeGlobal { get; } =
        SymbolDisplayFormat.FullyQualifiedFormat
            .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Included);

    public static string FullQualifiedNameOmitGlobal(this ITypeSymbol symbol)
        => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);

    public static string FullQualifiedNameOmitGlobal(this INamespaceSymbol namespaceSymbol)
        => namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);

    public static string FullQualifiedNameIncludeGlobal(this ITypeSymbol symbol)
        => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatIncludeGlobal);

    public static string FullQualifiedNameIncludeGlobal(this INamespaceSymbol namespaceSymbol)
        => namespaceSymbol.ToDisplayString(FullyQualifiedFormatIncludeGlobal);

    public static string SanitizeQualifiedNameForUniqueHint(this string qualifiedName)
        => qualifiedName
            // AddSource() doesn't support angle brackets
            .Replace("<", "(Of ")
            .Replace(">", ")");
}