summaryrefslogtreecommitdiff
path: root/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
blob: fac362479a54d3439969120431126546c4a02327 (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
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"
        };
    }

    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 NameWithTypeParameters(this INamedTypeSymbol symbol)
    {
        return symbol.IsGenericType ?
            string.Concat(symbol.Name, "<", string.Join(", ", symbol.TypeParameters), ">") :
            symbol.Name;
    }

    public static string FullQualifiedName(this INamespaceSymbol symbol)
        => symbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);

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