// 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; using System.Collections.Immutable; using System.Composition; using System.Diagnostics; using System.Linq; 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 { /// /// Codefix that changes the type of a variable to be Func of Task instead of a void-returning delegate type. /// [ExportCodeFixProvider(AsyncLambdaAnalyzer.AsyncLambdaId1, LanguageNames.CSharp), Shared] public class AsyncLambdaVariableCodeFix : CodeFixProvider { public sealed override ImmutableArray GetFixableDiagnosticIds() { return ImmutableArray.Create(AsyncLambdaAnalyzer.AsyncLambdaId1); } public sealed override FixAllProvider GetFixAllProvider() { return null; } public sealed override async Task ComputeFixesAsync(CodeFixContext context) { var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var diagnostic = context.Diagnostics.First(); var diagnosticSpan = diagnostic.Location.SourceSpan; 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(); // 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); } } private async Task ChangeToFunc(Document document, VariableDeclarationSyntax variableDeclaration, CancellationToken cancellationToken) { // Change the variable declaration var newDeclaration = variableDeclaration.WithType(SyntaxFactory.ParseTypeName("System.Func").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; } private class AsyncLambdaVariableCodeAction : CodeAction { private Func> generateDocument; private string title; public AsyncLambdaVariableCodeAction(string title, Func> generateDocument) { this.title = title; this.generateDocument = generateDocument; } public override string Title { get { return title; } } protected override Task GetChangedDocumentAsync(CancellationToken cancellationToken) { return this.generateDocument(cancellationToken); } } } }