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