AsyncLambdaVariableCodeFix.cs 3.9 KB
Newer Older
J
jmarolf 已提交
1 2 3
// Copyright (c) Microsoft Open Technologies, Inc.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
4
using System.Collections.Immutable;
5
using System.Composition;
6
using System.Diagnostics;
7
using System.Linq;
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
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;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Simplification;

namespace AsyncPackage
{
    /// <summary>
    /// Codefix that changes the type of a variable to be Func of Task instead of a void-returning delegate type.
    /// </summary>
23
    [ExportCodeFixProvider(AsyncLambdaAnalyzer.AsyncLambdaId1, LanguageNames.CSharp), Shared]
24
    public class AsyncLambdaVariableCodeFix : CodeFixProvider
25
    {
26
        public sealed override ImmutableArray<string> GetFixableDiagnosticIds()
27
        {
28
            return ImmutableArray.Create(AsyncLambdaAnalyzer.AsyncLambdaId1);
29 30
        }

31
        public sealed override FixAllProvider GetFixAllProvider()
32 33 34 35
        {
            return null;
        }

36
        public sealed override async Task ComputeFixesAsync(CodeFixContext context)
37
        {
38
            var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
39

40 41
            var diagnostic = context.Diagnostics.First();
            var diagnosticSpan = diagnostic.Location.SourceSpan;
42 43 44 45 46 47 48 49

            Debug.Assert(root != null);
            var parent = root.FindToken(diagnosticSpan.Start).Parent;
            if (parent != null)
            {
                // Find the type declaration identified by the diagnostic.
                var variableDeclaration = parent.FirstAncestorOrSelf<VariableDeclarationSyntax>();

50 51 52 53 54
                // Register a code action that will invoke the fix.
                context.RegisterFix(
                    new AsyncLambdaVariableCodeAction("Async lambdas should not be stored in void-returning delegates",
                                                      c => ChangeToFunc(context.Document, variableDeclaration, c)),
                    diagnostic);
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
            }
        }

        private async Task<Document> ChangeToFunc(Document document, VariableDeclarationSyntax variableDeclaration, CancellationToken cancellationToken)
        {
            // Change the variable declaration
            var newDeclaration = variableDeclaration.WithType(SyntaxFactory.ParseTypeName("System.Func<System.Threading.Tasks.Task>").WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation)
                .WithLeadingTrivia(variableDeclaration.Type.GetLeadingTrivia()).WithTrailingTrivia(variableDeclaration.Type.GetTrailingTrivia()));

            var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var newRoot = oldRoot.ReplaceNode(variableDeclaration, newDeclaration);
            var newDocument = document.WithSyntaxRoot(newRoot);

            // Return document with transformed tree.
            return newDocument;
        }
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

        private class AsyncLambdaVariableCodeAction : CodeAction
        {
            private Func<CancellationToken, Task<Document>> generateDocument;
            private string title;

            public AsyncLambdaVariableCodeAction(string title, Func<CancellationToken, Task<Document>> generateDocument)
            {
                this.title = title;
                this.generateDocument = generateDocument;
            }

            public override string Title { get { return title; } }

            protected override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
            {
                return this.generateDocument(cancellationToken);
            }
        }
90 91
    }
}