Files
RobustToolbox/Robust.Analyzers.Tests/PrototypeFixerTest.cs
Tayrtahn c1737a540f Analyzer & Fixer for redundant Prototype type strings (#5718)
* Add Prototype analyzer

* Add Prototype fixer

* Early return after finding prototype attribute

* Add PrototypeEndsWithPrototypeRule diagnostic

* Oops. Uncomment parallelizable.

* Rework to ignore redundancy for non-literal string values

* Allow redundancy when removal would expose class name not ending in "Prototype"

* Promote PrototypeEndsWithPrototypeRule from warning to error, since it causes a runtime error.

* No need to get the symbol to get the class identifier

* Minor cleanup

* A little more cleanup

* More specific location for redundant name

* Refactor redundant name fixer so argument order is no longer important

* Add failing test

* Use symbol analysis to fix alias handling

* Oops! We have to go back to the previous syntax-based approach.

Now it's a hybrid.

Also fixed tests to not copy the prototype definitions.

---------

Co-authored-by: PJB3005 <pieterjan.briers+git@gmail.com>
2025-12-17 18:15:32 +01:00

96 lines
3.0 KiB
C#

using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using NUnit.Framework;
using VerifyCS =
Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier<Robust.Analyzers.PrototypeAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;
namespace Robust.Analyzers.Tests;
public sealed class PrototypeFixerTest
{
private static Task Verifier(string code, string fixedCode, params DiagnosticResult[] expected)
{
var test = new CSharpCodeFixTest<PrototypeAnalyzer, PrototypeFixer, DefaultVerifier>()
{
TestState =
{
Sources = { code },
},
FixedState =
{
Sources = { fixedCode },
}
};
test.TestState.Sources.Add(("PrototypeAttribute.cs", PrototypeAttributeDef));
test.FixedState.Sources.Add(("PrototypeAttribute.cs", PrototypeAttributeDef));
test.TestState.ExpectedDiagnostics.AddRange(expected);
return test.RunAsync();
}
private const string PrototypeAttributeDef = """
using System;
namespace Robust.Shared.Prototypes
{
public class PrototypeAttribute : Attribute
{
public string? Type { get; internal set; }
public readonly int LoadPriority = 1;
public PrototypeAttribute(string? type = null, int loadPriority = 1)
{
Type = type;
LoadPriority = loadPriority;
}
public PrototypeAttribute(int loadPriority)
{
Type = null;
LoadPriority = loadPriority;
}
}
public interface IPrototype;
}
""";
[Test]
public async Task Test()
{
const string code = """
using Robust.Shared.Prototypes;
[Prototype]
public sealed partial class GoodAutoPrototype : IPrototype;
[Prototype("someOtherName")]
public sealed partial class GoodUnmatchedPrototype : IPrototype;
[Prototype("badMatched")]
public sealed partial class BadMatchedPrototype : IPrototype;
""";
const string fixedCode = """
using Robust.Shared.Prototypes;
[Prototype]
public sealed partial class GoodAutoPrototype : IPrototype;
[Prototype("someOtherName")]
public sealed partial class GoodUnmatchedPrototype : IPrototype;
[Prototype]
public sealed partial class BadMatchedPrototype : IPrototype;
""";
await Verifier(code, fixedCode,
// /0/Test0.cs(9,2): warning RA0033: Prototype BadMatchedPrototype has explicitly set type "badMatched" that matches autogenerated value
VerifyCS.Diagnostic(PrototypeAnalyzer.PrototypeRedundantTypeRule).WithSpan(9, 12, 9, 24).WithArguments("BadMatchedPrototype", "badMatched")
);
}
}