761e2b1a65
Ignore property indexers since they are unsupported and report a diagnostic if an user tries to export it.
362 lines
16 KiB
C#
362 lines
16 KiB
C#
using System.Linq;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Diagnostics;
|
|
|
|
namespace Godot.SourceGenerators
|
|
{
|
|
public static class Common
|
|
{
|
|
public static void ReportNonPartialGodotScriptClass(
|
|
GeneratorExecutionContext context,
|
|
ClassDeclarationSyntax cds, INamedTypeSymbol symbol
|
|
)
|
|
{
|
|
string message =
|
|
"Missing partial modifier on declaration of type '" +
|
|
$"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
|
|
|
|
string description = $"{message}. Subclasses of '{GodotClasses.Object}' " +
|
|
"must be declared with the partial modifier.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0001",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
cds.GetLocation(),
|
|
cds.SyntaxTree.FilePath));
|
|
}
|
|
|
|
public static void ReportNonPartialGodotScriptOuterClass(
|
|
GeneratorExecutionContext context,
|
|
TypeDeclarationSyntax outerTypeDeclSyntax
|
|
)
|
|
{
|
|
var outerSymbol = context.Compilation
|
|
.GetSemanticModel(outerTypeDeclSyntax.SyntaxTree)
|
|
.GetDeclaredSymbol(outerTypeDeclSyntax);
|
|
|
|
string fullQualifiedName = outerSymbol is INamedTypeSymbol namedTypeSymbol ?
|
|
namedTypeSymbol.FullQualifiedName() :
|
|
"type not found";
|
|
|
|
string message =
|
|
$"Missing partial modifier on declaration of type '{fullQualifiedName}', " +
|
|
$"which contains one or more subclasses of '{GodotClasses.Object}'";
|
|
|
|
string description = $"{message}. Subclasses of '{GodotClasses.Object}' and their " +
|
|
"containing types must be declared with the partial modifier.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0002",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
outerTypeDeclSyntax.GetLocation(),
|
|
outerTypeDeclSyntax.SyntaxTree.FilePath));
|
|
}
|
|
|
|
public static void ReportExportedMemberIsStatic(
|
|
GeneratorExecutionContext context,
|
|
ISymbol exportedMemberSymbol
|
|
)
|
|
{
|
|
var locations = exportedMemberSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
bool isField = exportedMemberSymbol is IFieldSymbol;
|
|
|
|
string message = $"Attempted to export static {(isField ? "field" : "property")}: " +
|
|
$"'{exportedMemberSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Only instance fields and properties can be exported." +
|
|
" Remove the 'static' modifier or the '[Export]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0101",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportExportedMemberTypeNotSupported(
|
|
GeneratorExecutionContext context,
|
|
ISymbol exportedMemberSymbol
|
|
)
|
|
{
|
|
var locations = exportedMemberSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
bool isField = exportedMemberSymbol is IFieldSymbol;
|
|
|
|
string message = $"The type of the exported {(isField ? "field" : "property")} " +
|
|
$"is not supported: '{exportedMemberSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Use a supported type or remove the '[Export]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0102",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportExportedMemberIsReadOnly(
|
|
GeneratorExecutionContext context,
|
|
ISymbol exportedMemberSymbol
|
|
)
|
|
{
|
|
var locations = exportedMemberSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
bool isField = exportedMemberSymbol is IFieldSymbol;
|
|
|
|
string message = $"The exported {(isField ? "field" : "property")} " +
|
|
$"is read-only: '{exportedMemberSymbol.ToDisplayString()}'";
|
|
|
|
string description = isField ?
|
|
$"{message}. Exported fields cannot be read-only." :
|
|
$"{message}. Exported properties must be writable.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0103",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportExportedMemberIsWriteOnly(
|
|
GeneratorExecutionContext context,
|
|
ISymbol exportedMemberSymbol
|
|
)
|
|
{
|
|
var locations = exportedMemberSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
|
|
string message = $"The exported property is write-only: '{exportedMemberSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Exported properties must be readable.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0104",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportExportedMemberIsIndexer(
|
|
GeneratorExecutionContext context,
|
|
ISymbol exportedMemberSymbol
|
|
)
|
|
{
|
|
var locations = exportedMemberSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
|
|
string message = $"Attempted to export indexer property: " +
|
|
$"'{exportedMemberSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Indexer properties can't be exported." +
|
|
" Remove the '[Export]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0105",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportSignalDelegateMissingSuffix(
|
|
GeneratorExecutionContext context,
|
|
INamedTypeSymbol delegateSymbol)
|
|
{
|
|
var locations = delegateSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
|
|
string message = "The name of the delegate must end with 'EventHandler': " +
|
|
delegateSymbol.ToDisplayString() +
|
|
$". Did you mean '{delegateSymbol.Name}EventHandler'?";
|
|
|
|
string description = $"{message}. Rename the delegate accordingly or remove the '[Signal]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0201",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportSignalParameterTypeNotSupported(
|
|
GeneratorExecutionContext context,
|
|
IParameterSymbol parameterSymbol)
|
|
{
|
|
var locations = parameterSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
|
|
string message = "The parameter of the delegate signature of the signal " +
|
|
$"is not supported: '{parameterSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Use supported types only or remove the '[Signal]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0202",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static void ReportSignalDelegateSignatureMustReturnVoid(
|
|
GeneratorExecutionContext context,
|
|
INamedTypeSymbol delegateSymbol)
|
|
{
|
|
var locations = delegateSymbol.Locations;
|
|
var location = locations.FirstOrDefault(l => l.SourceTree != null) ?? locations.FirstOrDefault();
|
|
|
|
string message = "The delegate signature of the signal " +
|
|
$"must return void: '{delegateSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Return void or remove the '[Signal]' attribute.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0203",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
location,
|
|
location?.SourceTree?.FilePath));
|
|
}
|
|
|
|
public static readonly DiagnosticDescriptor GenericTypeArgumentMustBeVariantRule =
|
|
new DiagnosticDescriptor(id: "GD0301",
|
|
title: "The generic type argument must be a Variant compatible type",
|
|
messageFormat: "The generic type argument must be a Variant compatible type: {0}",
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
"The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.");
|
|
|
|
public static void ReportGenericTypeArgumentMustBeVariant(
|
|
SyntaxNodeAnalysisContext context,
|
|
SyntaxNode typeArgumentSyntax,
|
|
ISymbol typeArgumentSymbol)
|
|
{
|
|
string message = "The generic type argument " +
|
|
$"must be a Variant compatible type: '{typeArgumentSymbol.ToDisplayString()}'";
|
|
|
|
string description = $"{message}. Use a Variant compatible type as the generic type argument.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0301",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
typeArgumentSyntax.GetLocation(),
|
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
|
}
|
|
|
|
public static readonly DiagnosticDescriptor GenericTypeParameterMustBeVariantAnnotatedRule =
|
|
new DiagnosticDescriptor(id: "GD0302",
|
|
title: "The generic type parameter must be annotated with the MustBeVariant attribute",
|
|
messageFormat: "The generic type argument must be a Variant type: {0}",
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
"The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
|
|
|
|
public static void ReportGenericTypeParameterMustBeVariantAnnotated(
|
|
SyntaxNodeAnalysisContext context,
|
|
SyntaxNode typeArgumentSyntax,
|
|
ISymbol typeArgumentSymbol)
|
|
{
|
|
string message = "The generic type parameter must be annotated with the MustBeVariant attribute";
|
|
|
|
string description = $"{message}. Add the MustBeVariant attribute to the generic type parameter.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0302",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
typeArgumentSyntax.GetLocation(),
|
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
|
}
|
|
|
|
public static readonly DiagnosticDescriptor TypeArgumentParentSymbolUnhandledRule =
|
|
new DiagnosticDescriptor(id: "GD0303",
|
|
title: "The generic type parameter must be annotated with the MustBeVariant attribute",
|
|
messageFormat: "The generic type argument must be a Variant type: {0}",
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
"The generic type argument must be a Variant type. Use a Variant type as the generic type argument.");
|
|
|
|
public static void ReportTypeArgumentParentSymbolUnhandled(
|
|
SyntaxNodeAnalysisContext context,
|
|
SyntaxNode typeArgumentSyntax,
|
|
ISymbol parentSymbol)
|
|
{
|
|
string message = $"Symbol '{parentSymbol.ToDisplayString()}' parent of a type argument " +
|
|
"that must be Variant compatible was not handled.";
|
|
|
|
string description = $"{message}. Handle type arguments that are children of the unhandled symbol type.";
|
|
|
|
context.ReportDiagnostic(Diagnostic.Create(
|
|
new DiagnosticDescriptor(id: "GD0303",
|
|
title: message,
|
|
messageFormat: message,
|
|
category: "Usage",
|
|
DiagnosticSeverity.Error,
|
|
isEnabledByDefault: true,
|
|
description),
|
|
typeArgumentSyntax.GetLocation(),
|
|
typeArgumentSyntax.SyntaxTree.FilePath));
|
|
}
|
|
}
|
|
}
|