136 lines
4.6 KiB
C#
136 lines
4.6 KiB
C#
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<ClassDeclarationSyntax> 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<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 FullQualifiedName(this INamespaceSymbol namespaceSymbol)
|
|
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
|
|
}
|
|
}
|