C#CodeFixes:显示codefix的最低要求

时间:2017-11-20 13:47:39

标签: c# roslyn-code-analysis codefixprovider

我最近开始使用CodeAnalyzers和CodeFixes。

当然,sdk()附带的模板有效。但是当我继续前进时,codefix不会再出现在调试中。

在完成诊断时,按照预期创建诊断。 但是:codefix(Strg +。)不再显示了。我负责Diagnostic-Id,等价键,但是无法搞清楚,为什么我不再显示我的codefix。那么为诊断显示代码的最低“要求”是什么?

以下是分析仪的代码:

[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);
    }
}

1 个答案:

答案 0 :(得分:0)

memberDeclarationModifiers.Add(SyntaxFactory.Token(SyntaxKind.VirtualKeyword));会返回新的SyntaxTokenList。您没有使用新创建的列表。请记住,SyntaxTrees在Roslyn中是不可变的。树中的每个更改都将创建一个新树。这是你的roslyn codefix的codefix:)

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);
}