summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author398utubzyt <398utubzyt@gmail.com>2023-07-03 19:35:54 -0700
committer398utubzyt <398utubzyt@gmail.com>2023-07-07 16:37:16 -0700
commit8e56c807cc604a3042be4575e2533d9676c36f8e (patch)
tree0166d4a8469594cdfffc993edc9d2ce06d74ffb8
parentcdd2313ba27d0a2600a18e849b4c5d1fd6a6e351 (diff)
downloadredot-engine-8e56c807cc604a3042be4575e2533d9676c36f8e.tar.gz
C#: Add a Roslyn analyzer for global classes
Co-Authored-By: Raul Santos <raulsntos@gmail.com>
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs60
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs42
3 files changed, 104 insertions, 2 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 8be1151142..72614dd7e0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -384,5 +384,65 @@ namespace Godot.SourceGenerators
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
+
+ public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule =
+ new DiagnosticDescriptor(id: "GD0401",
+ title: "The class must derive from GodotObject or a derived class",
+ messageFormat: "The class '{0}' must derive from GodotObject or a derived class.",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustDeriveFromGodotObject(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must derive from GodotObject or a derived class";
+
+ string description = $"{message}. Change the base class or remove the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0401",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GlobalClassMustNotBeGenericRule =
+ new DiagnosticDescriptor(id: "GD0402",
+ title: "The class must not contain generic arguments",
+ messageFormat: "The class '{0}' must not contain generic arguments",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustNotBeGeneric(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must not contain generic arguments";
+
+ string description = $"{message}. Remove the generic arguments or the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0402",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index 38af1cbade..c652040950 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -81,7 +81,7 @@ namespace Godot.SourceGenerators
return godotClassName ?? nativeType.Name;
}
- private static bool IsGodotScriptClass(
+ private static bool TryGetGodotScriptClass(
this ClassDeclarationSyntax cds, Compilation compilation,
out INamedTypeSymbol? symbol
)
@@ -108,7 +108,7 @@ namespace Godot.SourceGenerators
{
foreach (var cds in source)
{
- if (cds.IsGodotScriptClass(compilation, out var symbol))
+ if (cds.TryGetGodotScriptClass(compilation, out var symbol))
yield return (cds, symbol!);
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
new file mode 100644
index 0000000000..bcb35dae8a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
@@ -0,0 +1,42 @@
+using System.Collections.Immutable;
+using System.Linq;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class GlobalClassAnalyzer : DiagnosticAnalyzer
+ {
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
+ => ImmutableArray.Create(
+ Common.GlobalClassMustDeriveFromGodotObjectRule,
+ Common.GlobalClassMustNotBeGenericRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var typeClassDecl = (ClassDeclarationSyntax)context.Node;
+
+ // Return if not a type symbol or the type is not a global class.
+ if (context.ContainingSymbol is not INamedTypeSymbol typeSymbol ||
+ !typeSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false))
+ return;
+
+ if (typeSymbol.IsGenericType)
+ Common.ReportGlobalClassMustNotBeGeneric(context, typeClassDecl, typeSymbol);
+
+ if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject))
+ Common.ReportGlobalClassMustDeriveFromGodotObject(context, typeClassDecl, typeSymbol);
+ }
+ }
+}