mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
* 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>
78 lines
2.7 KiB
C#
78 lines
2.7 KiB
C#
#nullable enable
|
|
using System.Collections.Immutable;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CodeActions;
|
|
using Microsoft.CodeAnalysis.CodeFixes;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using static Robust.Roslyn.Shared.Diagnostics;
|
|
|
|
namespace Robust.Analyzers;
|
|
|
|
[ExportCodeFixProvider(LanguageNames.CSharp)]
|
|
public sealed class PrototypeFixer : CodeFixProvider
|
|
{
|
|
public override ImmutableArray<string> FixableDiagnosticIds => [IdPrototypeRedundantType];
|
|
|
|
public override FixAllProvider GetFixAllProvider()
|
|
{
|
|
return WellKnownFixAllProviders.BatchFixer;
|
|
}
|
|
|
|
public override Task RegisterCodeFixesAsync(CodeFixContext context)
|
|
{
|
|
foreach (var diagnostic in context.Diagnostics)
|
|
{
|
|
switch (diagnostic.Id)
|
|
{
|
|
case IdPrototypeRedundantType:
|
|
return RegisterRemoveType(context, diagnostic);
|
|
}
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private static async Task RegisterRemoveType(CodeFixContext context, Diagnostic diagnostic)
|
|
{
|
|
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken);
|
|
var span = diagnostic.Location.SourceSpan;
|
|
var token = root?.FindToken(span.Start).Parent?.AncestorsAndSelf().OfType<AttributeArgumentSyntax>().First();
|
|
|
|
if (token == null)
|
|
return;
|
|
|
|
context.RegisterCodeFix(CodeAction.Create(
|
|
"Remove explicitly set type",
|
|
c => RemoveType(context.Document, token, c),
|
|
"Remove explicitly set type"
|
|
), diagnostic);
|
|
}
|
|
|
|
private static async Task<Document> RemoveType(Document document, AttributeArgumentSyntax syntax, CancellationToken cancellation)
|
|
{
|
|
var root = (CompilationUnitSyntax?) await document.GetSyntaxRootAsync(cancellation);
|
|
|
|
if (syntax.Parent is not AttributeArgumentListSyntax argListSyntax)
|
|
return document;
|
|
|
|
if (argListSyntax.Arguments.Count == 1)
|
|
{
|
|
// If this is the only argument, remove the whole argument list so we don't leave empty parentheses
|
|
if (argListSyntax.Parent is not AttributeSyntax attributeSyntax)
|
|
return document;
|
|
|
|
var newAttributeSyntax = attributeSyntax.RemoveNode(argListSyntax, SyntaxRemoveOptions.KeepNoTrivia);
|
|
root = root!.ReplaceNode(attributeSyntax, newAttributeSyntax!);
|
|
}
|
|
else
|
|
{
|
|
// Otherwise just remove the argument
|
|
var newArgListSyntax = argListSyntax.WithArguments(argListSyntax.Arguments.Remove(syntax));
|
|
root = root!.ReplaceNode(argListSyntax, newArgListSyntax);
|
|
}
|
|
|
|
|
|
return document.WithSyntaxRoot(root);
|
|
}
|
|
}
|