|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Immutable;
using System.Composition;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.AspNetCore.Components.Analyzers;
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(JSInvokableCodeFixProvider)), Shared]
public class JSInvokableCodeFixProvider : CodeFixProvider
{
private static readonly LocalizableString _title = new LocalizableResourceString(nameof(Resources.JSInvokableMethodShouldBePublic_FixTitle), Resources.ResourceManager, typeof(Resources));
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(DiagnosticDescriptors.JSInvokableMethodShouldBePublic.Id);
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var title = _title.ToString(CultureInfo.InvariantCulture);
foreach (var diagnostic in context.Diagnostics)
{
context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: ct => GetTransformedDocumentAsync(diagnostic, context.Document, ct),
equivalenceKey: title),
diagnostic);
}
return Task.CompletedTask;
}
private static async Task<Document> GetTransformedDocumentAsync(
Diagnostic diagnostic,
Document document,
CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
if (root is null)
{
return document;
}
var declarationNode = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf<MethodDeclarationSyntax>();
if (declarationNode is null)
{
return document;
}
var updatedDeclarationNode = HandleMethodDeclaration(declarationNode);
var newSyntaxRoot = root.ReplaceNode(declarationNode, updatedDeclarationNode);
return document.WithSyntaxRoot(newSyntaxRoot);
}
private static SyntaxNode HandleMethodDeclaration(MethodDeclarationSyntax node)
{
var newModifiers = SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PublicKeyword)
);
foreach (var modifier in node.Modifiers)
{
// Skip all existing access modifiers, including 'public' just in case the fix is ran on invalid code with multiple access modifiers (e.g. private public)
if (modifier.IsKind(SyntaxKind.PrivateKeyword)
|| modifier.IsKind(SyntaxKind.ProtectedKeyword)
|| modifier.IsKind(SyntaxKind.InternalKeyword)
|| modifier.IsKind(SyntaxKind.PublicKeyword))
{
continue;
}
newModifiers = newModifiers.Add(modifier);
}
node = node.WithModifiers(newModifiers);
return node;
}
}
|