godot/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
Ignacio Etcheverry ee8e5146a4 C#: Fix ScriptPathAttribute generator with none or nested namespaces
The following two bugs were fixed:
- For classes without namespace we were still generating `namespace {`
without a namespace identifier, causing a syntax error.
- For classes with nested namespaces we were generating only the innermost
part of the namespace was being generated, e.g.: for `Foo.Bar` we were
generating `namespace Bar {` instead of `namespace Foo.Bar {`.
This wasn't causing any build error, but because of the wrong namespace
Godot wasn't able to find the class associated with the script.
2021-03-13 01:04:59 +01:00

90 lines
3 KiB
C#

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);
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 IsPartial(this ClassDeclarationSyntax cds)
=> cds.Modifiers.Any(SyntaxKind.PartialKeyword);
public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
=> symbol.GetAttributes().Any(attr =>
attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
public static string FullQualifiedName(this INamedTypeSymbol symbol)
=> symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
=> namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
}
}