diff options
author | Raul Santos <raulsntos@gmail.com> | 2024-02-15 18:18:33 +0100 |
---|---|---|
committer | Raul Santos <raulsntos@gmail.com> | 2024-02-19 06:33:13 +0100 |
commit | fe280ef9ae65d38cbccbdc5fe197cf029a0ca397 (patch) | |
tree | abafda3dfdfece36a2ed60c4602cb53dca8ff5ed /modules/mono/editor | |
parent | ae51db75e7a4b4d111cf5dcbf596bc2c8c8a3222 (diff) | |
download | redot-engine-fe280ef9ae65d38cbccbdc5fe197cf029a0ca397.tar.gz |
C#: Various fixes to generic scripts
- Report a diagnostic when there are multiple classes that match the script file name in the same script since that will result in a duplicate path key in the bimap and it's not allowed.
- Fix InspectorPlugin to handle empty paths in case the project was built with a previous version of Godot that used empty paths for generic scripts.
- Add tests for the new diagnostic GD0003.
Diffstat (limited to 'modules/mono/editor')
12 files changed, 126 insertions, 28 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs index 2cd1a08c0e..457d8daa8e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs @@ -2,17 +2,6 @@ namespace Godot.SourceGenerators.Sample { - partial class Generic<T> : GodotObject - { - private int _field; - } - - // Generic again but different generic parameters - partial class Generic<T, R> : GodotObject - { - private int _field; - } - // Generic again but without generic parameters partial class Generic : GodotObject { diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs new file mode 100644 index 0000000000..9c4f8ee1e1 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic1T.cs @@ -0,0 +1,9 @@ +#pragma warning disable CS0169 + +namespace Godot.SourceGenerators.Sample +{ + partial class Generic1T<T> : GodotObject + { + private int _field; + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs new file mode 100644 index 0000000000..80551a0b42 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic2T.cs @@ -0,0 +1,10 @@ +#pragma warning disable CS0169 + +namespace Godot.SourceGenerators.Sample +{ + // Generic again but different generic parameters + partial class Generic2T<T, R> : GodotObject + { + private int _field; + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs index b7ad4217aa..4f6b50cf02 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/ScriptPathAttributeGeneratorTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -47,9 +48,33 @@ public class ScriptPathAttributeGeneratorTests { var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( new string[] { "Generic.cs" }, - new string[] { "Generic_ScriptPath.generated.cs" } + new string[] { "Generic(Of T)_ScriptPath.generated.cs" } ); - verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic" })); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>" })); + await verifier.RunAsync(); + } + + [Fact] + public async void GenericMultipleClassesSameName() + { + var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( + Array.Empty<string>(), + new string[] { "Generic(Of T)_ScriptPath.generated.cs" } + ); + verifier.TestState.Sources.Add(("Generic.cs", File.ReadAllText(Path.Combine(Constants.SourceFolderPath, "Generic.GD0003.cs")))); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::Generic<>", "global::Generic<,>", "global::Generic" })); + await verifier.RunAsync(); + } + + [Fact] + public async void NamespaceMultipleClassesSameName() + { + var verifier = CSharpSourceGeneratorVerifier<ScriptPathAttributeGenerator>.MakeVerifier( + Array.Empty<string>(), + new string[] { "NamespaceA.SameName_ScriptPath.generated.cs" } + ); + verifier.TestState.Sources.Add(("SameName.cs", File.ReadAllText(Path.Combine(Constants.SourceFolderPath, "SameName.GD0003.cs")))); + verifier.TestState.GeneratedSources.Add(MakeAssemblyScriptTypesGeneratedSource(new string[] { "global::NamespaceA.SameName", "global::NamespaceB.SameName" })); await verifier.RunAsync(); } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic(Of T)_ScriptPath.generated.cs index 72c48595a2..355b01f753 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic_ScriptPath.generated.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/Generic(Of T)_ScriptPath.generated.cs @@ -1,5 +1,5 @@ using Godot; [ScriptPathAttribute("res://Generic.cs")] -partial class Generic +partial class Generic<T> { } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs new file mode 100644 index 0000000000..cad9f2a46b --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/NamespaceA.SameName_ScriptPath.generated.cs @@ -0,0 +1,9 @@ +using Godot; +namespace NamespaceA { + +[ScriptPathAttribute("res://SameName.cs")] +partial class SameName +{ +} + +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs new file mode 100644 index 0000000000..15c1e03801 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.GD0003.cs @@ -0,0 +1,18 @@ +using Godot; + +partial class Generic<T> : GodotObject +{ + private int _field; +} + +// Generic again but different generic parameters +partial class {|GD0003:Generic|}<T, R> : GodotObject +{ + private int _field; +} + +// Generic again but without generic parameters +partial class {|GD0003:Generic|} : GodotObject +{ + private int _field; +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs index 84d1ede065..5a83e21e96 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/Generic.cs @@ -4,15 +4,3 @@ partial class Generic<T> : GodotObject { private int _field; } - -// Generic again but different generic parameters -partial class Generic<T, R> : GodotObject -{ - private int _field; -} - -// Generic again but without generic parameters -partial class Generic : GodotObject -{ - private int _field; -} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs new file mode 100644 index 0000000000..3f4f79fc49 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/SameName.GD0003.cs @@ -0,0 +1,18 @@ +using Godot; + +namespace NamespaceA +{ + partial class SameName : GodotObject + { + private int _field; + } +} + +// SameName again but different namespace +namespace NamespaceB +{ + partial class {|GD0003:SameName|} : GodotObject + { + private int _field; + } +} 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 a47e2e170f..6cd5ddb42f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs @@ -65,6 +65,16 @@ namespace Godot.SourceGenerators outerTypeDeclSyntax.SyntaxTree.FilePath)); } + public static readonly DiagnosticDescriptor MultipleClassesInGodotScriptRule = + new DiagnosticDescriptor(id: "GD0003", + title: "Found multiple classes with the same name in the same script file", + messageFormat: "Found multiple classes with the name '{0}' in the same script file", + category: "Usage", + DiagnosticSeverity.Error, + isEnabledByDefault: true, + "Found multiple classes with the same name in the same script file. A script file must only contain one class with a name that matches the file name.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0003")); + public static readonly DiagnosticDescriptor ExportedMemberIsStaticRule = new DiagnosticDescriptor(id: "GD0101", title: "The exported member is static", diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs index 625a6f9921..6dc541cc59 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs @@ -58,9 +58,10 @@ namespace Godot.SourceGenerators .GroupBy<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol), INamedTypeSymbol>(x => x.symbol, SymbolEqualityComparer.Default) .ToDictionary<IGrouping<INamedTypeSymbol, (ClassDeclarationSyntax cds, INamedTypeSymbol symbol)>, INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>>(g => g.Key, g => g.Select(x => x.cds), SymbolEqualityComparer.Default); + var usedPaths = new HashSet<string>(); foreach (var godotClass in godotClasses) { - VisitGodotScriptClass(context, godotProjectDir, + VisitGodotScriptClass(context, godotProjectDir, usedPaths, symbol: godotClass.Key, classDeclarations: godotClass.Value); } @@ -74,6 +75,7 @@ namespace Godot.SourceGenerators private static void VisitGodotScriptClass( GeneratorExecutionContext context, string godotProjectDir, + HashSet<string> usedPaths, INamedTypeSymbol symbol, IEnumerable<ClassDeclarationSyntax> classDeclarations ) @@ -93,8 +95,19 @@ namespace Godot.SourceGenerators if (attributes.Length != 0) attributes.Append("\n"); + string scriptPath = RelativeToDir(cds.SyntaxTree.FilePath, godotProjectDir); + if (!usedPaths.Add(scriptPath)) + { + context.ReportDiagnostic(Diagnostic.Create( + Common.MultipleClassesInGodotScriptRule, + cds.Identifier.GetLocation(), + symbol.Name + )); + return; + } + attributes.Append(@"[ScriptPathAttribute(""res://"); - attributes.Append(RelativeToDir(cds.SyntaxTree.FilePath, godotProjectDir)); + attributes.Append(scriptPath); attributes.Append(@""")]"); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs index b86a2b8b24..8aeb19e08b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Inspector/InspectorPlugin.cs @@ -28,6 +28,15 @@ namespace GodotTools.Inspector continue; string scriptPath = script.ResourcePath; + + if (string.IsNullOrEmpty(scriptPath)) + { + // Generic types used empty paths in older versions of Godot + // so we assume your project is out of sync. + AddCustomControl(new InspectorOutOfSyncWarning()); + break; + } + if (scriptPath.StartsWith("csharp://")) { // This is a virtual path used by generic types, extract the real path. |