C# 代码修复:显示代码修复的最低要求

C# CodeFixes: Minimum Requirement that codefix is displayed

我最近开始使用 CodeAnalyzers 和 CodeFixes。

sdk()自带的模板当然可以。但是当我继续时,codefix 不再出现在调试中。

当遍历时,诊断会按预期创建。 但是:代码修复 (Strg+.) 不再显示。我处理了 Diagnostic-Id、equivalence-key,...但无法弄清楚,为什么我不再显示我的代码修复。那么要为诊断显示的代码修复的最小 "requirements" 是多少?


分析器代码如下:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class VirtualMemberAnalyzer : DiagnosticAnalyzer
{
    public const string PublicVirtualMethod_DiagnosticId = "PublicVirtualMethod";

    private const string Category = "Naming";

    private static readonly DiagnosticDescriptor PublicMethodVirtualRule = new DiagnosticDescriptor(
        PublicVirtualMethod_DiagnosticId,
        "public methode not virtual",
        "every public methode must be virtual",
        Category,
        DiagnosticSeverity.Error,
        true);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
        ImmutableArray.Create(PublicMethodVirtualRule);

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
    }

    private static void AnalyzeSymbol(SymbolAnalysisContext context)
    {
        var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;

        foreach (var methodSymbol in namedTypeSymbol.GetMembers().OfType<IMethodSymbol>())
        {
            if (methodSymbol.MethodKind == MethodKind.Ordinary && !methodSymbol.IsStatic && !methodSymbol.IsVirtual && methodSymbol.DeclaredAccessibility == Accessibility.Public)
            {
                var diagnostic = Diagnostic.Create(
                    PublicMethodVirtualRule,
                    methodSymbol.Locations[0],
                    methodSymbol.Name);
                context.ReportDiagnostic(diagnostic);
            }
        }
    }
}

以及 Codefix-Provider 的:

[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(VirtualMemberAnalyzersCodeFixProvider)), Shared]
public class VirtualMemberAnalyzersCodeFixProvider : CodeFixProvider {
    private const string title_property = "Make property virtual";
    private const string title_method = "Make method virtual";

    public sealed override ImmutableArray<string> FixableDiagnosticIds {
        get {
            return ImmutableArray.Create(VirtualMemberAnalyzer.PublicVirtualMethod_DiagnosticId);
        }
    }

    public sealed override FixAllProvider GetFixAllProvider() {
        return WellKnownFixAllProviders.BatchFixer;
    }

    public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
        var root = await context.Document.GetSyntaxRootAsync();

        var diagnostic = context.Diagnostics.First();
        var diagnosticSpan = diagnostic.Location.SourceSpan;

        var methodDeclarations = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>().FirstOrDefault();

        if (methodDeclarations != null) {
            CodeAction codeAction = CodeAction.Create(title_method, c => MakeVirtual(context.Document, methodDeclarations, c), equivalenceKey: title_method);
            context.RegisterCodeFix(codeAction, diagnostic);
        }
    }

    private async Task<Document> MakeVirtual(Document document, MethodDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
    {
        SyntaxTokenList memberDeclarationModifiers = memberDeclaration.Modifiers;
        memberDeclarationModifiers.Add(SyntaxFactory.Token(SyntaxKind.VirtualKeyword));
        MethodDeclarationSyntax methodDeclarationSyntax = memberDeclaration.WithModifiers(memberDeclarationModifiers);

        var oldRoot = await document.GetSyntaxRootAsync(cancellationToken);
        var newRoot = oldRoot.ReplaceNode(memberDeclaration, methodDeclarationSyntax);

        return document.WithSyntaxRoot(newRoot);
    }
}

memberDeclarationModifiers.Add(SyntaxFactory.Token(SyntaxKind.VirtualKeyword));returns一个新的SyntaxTokenList。您没有使用新创建的列表。请记住,语法树在 Roslyn 中是不可变的。树中的每个更改都会创建一棵新树。这是您的 roslyn 代码修复的代码修复:)

private async Task<Document> MakeVirtual(Document document, MethodDeclarationSyntax memberDeclaration, CancellationToken cancellationToken)
{
    var methodDeclarationSyntax =
        memberDeclaration.WithModifiers(
            memberDeclaration.Modifiers.Add( SyntaxFactory.Token( SyntaxKind.VirtualKeyword ) ) );

    var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait( false );
    var newRoot = oldRoot.ReplaceNode(memberDeclaration, methodDeclarationSyntax);

    return document.WithSyntaxRoot(newRoot);
}