提交 5cb370b1 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #13061 from CyrusNajmabadi/rewriteMakeAsync

Rewrite make async

Fixes #12578
......@@ -211,7 +211,7 @@
<Compile Include="Diagnostics\AddUsing\AddUsingTests.cs" />
<Compile Include="Diagnostics\AddUsing\AddUsingTests_ExtensionMethods.cs" />
<Compile Include="Diagnostics\AddUsing\AddUsingTests_Queries.cs" />
<Compile Include="Diagnostics\Async\AddAsyncTests.cs" />
<Compile Include="Diagnostics\MakeMethodAsynchronous\MakeMethodAsynchronousTests.cs" />
<Compile Include="Diagnostics\Async\AddAwaitTests.cs" />
<Compile Include="Diagnostics\Async\ChangeToAsyncTests.cs" />
<Compile Include="Diagnostics\DiagnosticAnalyzerDriver\DiagnosticAnalyzerDriverTests.cs" />
......
......@@ -497,7 +497,16 @@ public async Task TestAssignmentExpressionWithConversion()
public async Task TestAssignmentExpressionWithConversionInNonAsyncFunction()
{
await TestMissingAsync(
@"using System . Threading . Tasks ; class TestClass { private Task MyTestMethod1Async ( ) { long myInt = [|MyIntMethodAsync ( )|] ; } private Task < int > MyIntMethodAsync ( ) { return Task . FromResult ( result : 1 ) ; } } ");
@"using System . Threading . Tasks ;
class TestClass {
private Task MyTestMethod1Async ( ) {
long myInt = [|MyIntMethodAsync ( )|] ;
}
private Task < int > MyIntMethodAsync ( ) {
return Task . FromResult ( result : 1 ) ;
}
} ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAwait)]
......
// Copyright (c) Microsoft. 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.Async;
using Microsoft.CodeAnalysis.CSharp.MakeMethodAsynchronous;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Test.Utilities;
using System;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.Async
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.MakeMethodAsynchronous
{
public partial class AddAsyncTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
public partial class MakeMethodAsynchronousTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(
null, new CSharpMakeMethodAsynchronousCodeFixProvider());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)]
public async Task AwaitInVoidMethodWithModifiers()
{
......@@ -33,13 +39,41 @@ public static void Test()
class Program
{
public static async void Test()
public static async void TestAsync()
{
await Task.Delay(1);
}
}";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)]
public async Task AwaitInVoidMethodWithModifiers2()
{
var initial =
@"using System;
using System.Threading.Tasks;
class Program
{
public static void Test()
{
[|await Task.Delay(1);|]
}
}";
var expected =
@"using System;
using System.Threading.Tasks;
class Program
{
public static async Task TestAsync()
{
await Task.Delay(1);
}
}";
await TestAsync(initial, expected, index: 1);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)]
public async Task AwaitInTaskMethodNoModifiers()
......@@ -62,7 +96,7 @@ Task Test()
class Program
{
async Task Test()
async Task TestAsync()
{
await Task.Delay(1);
}
......@@ -91,7 +125,7 @@ class Program
class Program
{
public/*Comment*/static/*Comment*/async Task/*Comment*/Test()
public/*Comment*/static/*Comment*/async Task/*Comment*/TestAsync()
{
await Task.Delay(1);
}
......@@ -176,7 +210,7 @@ void Test()
@"using System.Threading.Tasks;
class Program
{
async void Test()
async void TestAsync()
{
await Task.Delay(1);
}
......@@ -201,7 +235,7 @@ Task Test()
@"using System.Threading.Tasks;
class Program
{
async Task Test()
async Task TestAsync()
{
await Task.Delay(1);
}
......@@ -226,7 +260,7 @@ Task<int> Test()
@"using System.Threading.Tasks;
class Program
{
async Task<int> Test()
async Task<int> TestAsync()
{
await Task.Delay(1);
}
......@@ -251,7 +285,7 @@ int Test()
@"using System.Threading.Tasks;
class Program
{
async Task<int> Test()
async Task<int> TestAsync()
{
await Task.Delay(1);
}
......@@ -274,7 +308,7 @@ void Test()
var expected =
@"class Program
{
async void Test()
async void TestAsync()
{
await Task.Delay(1);
}
......@@ -297,7 +331,7 @@ Task Test()
var expected =
@"class Program
{
async Task Test()
async Task TestAsync()
{
await Task.Delay(1);
}
......@@ -320,7 +354,7 @@ Task<int> Test()
var expected =
@"class Program
{
async Task<int> Test()
async Task<int> TestAsync()
{
await Task.Delay(1);
}
......@@ -343,7 +377,7 @@ int Test()
var expected =
@"class Program
{
async System.Threading.Tasks.Task<int> Test()
async System.Threading.Tasks.Task<int> TestAsync()
{
await Task.Delay(1);
}
......@@ -366,7 +400,7 @@ Program Test()
var expected =
@"class Program
{
async System.Threading.Tasks.Task<Program> Test()
async System.Threading.Tasks.Task<Program> TestAsync()
{
await Task.Delay(1);
}
......@@ -389,7 +423,7 @@ asdf Test()
var expected =
@"class Program
{
async System.Threading.Tasks.Task<asdf> Test()
async System.Threading.Tasks.Task<asdf> TestAsync()
{
await Task.Delay(1);
}
......@@ -456,10 +490,5 @@ static async void Main()
}
}");
}
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(null, new CSharpAddAsyncCodeFixProvider());
}
}
}
......@@ -167,7 +167,7 @@
<Compile Include="Diagnostics\AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest.vb" />
<Compile Include="Diagnostics\AddImport\AddImportTests.vb" />
<Compile Include="Diagnostics\Async\AddAwaitTests.vb" />
<Compile Include="Diagnostics\Async\AddAsyncTests.vb" />
<Compile Include="Diagnostics\MakeMethodAsynchronous\MakeMethodAsynchronousTests.vb" />
<Compile Include="Diagnostics\Async\ChangeToAsyncTests.vb" />
<Compile Include="Diagnostics\CorrectNextControlVariable\CorrectNextControlVariableTests.vb" />
<Compile Include="Diagnostics\DiagnosticAnalyzerDriver\DiagnosticAnalyzerDriverTests.vb" />
......@@ -618,4 +618,4 @@
<Import Project="..\..\..\build\Targets\VSL.Imports.targets" />
<Import Project="..\..\..\build\Targets\Roslyn.Toolsets.Xunit.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -2,17 +2,23 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async
Imports Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
Public Class AddAsyncTests
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.MakeMethodAsynchronous
Public Class MakeMethodAsynchronousTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)(
Nothing,
New VisualBasicMakeMethodAsynchronousCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)>
Public Async Function TestAwaitInSubNoModifiers() As Task
Await TestAsync(
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Sub Test() \n [|Await Task.Delay(1)|] \n End Sub \n End Module"),
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Async Sub Test() \n Await Task.Delay(1) \n End Sub \n End Module")
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Async Sub TestAsync() \n Await Task.Delay(1) \n End Sub \n End Module")
)
End Function
......@@ -20,7 +26,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
Public Async Function TestAwaitInSubWithModifiers() As Task
Await TestAsync(
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Sub Test() \n [|Await Task.Delay(1)|] \n End Sub \n End Module"),
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Async Sub Test() \n Await Task.Delay(1) \n End Sub \n End Module")
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Async Sub TestAsync() \n Await Task.Delay(1) \n End Sub \n End Module")
)
End Function
......@@ -28,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
Public Async Function TestAwaitInFunctionNoModifiers() As Task
Await TestAsync(
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Function Test() As Integer \n [|Await Task.Delay(1)|] \n Function Sub \n End Module"),
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Async Function Test() As Task(Of Integer) \n Await Task.Delay(1) \n Function Sub \n End Module")
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Async Function TestAsync() As Task(Of Integer) \n Await Task.Delay(1) \n Function Sub \n End Module")
)
End Function
......@@ -36,7 +42,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
Public Async Function TestAwaitInFunctionWithModifiers() As Task
Await TestAsync(
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Function Test() As Integer \n [|Await Task.Delay(1)|] \n Function Sub \n End Module"),
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Async Function Test() As Task(Of Integer) \n Await Task.Delay(1) \n Function Sub \n End Module")
NewLines("Imports System \n Imports System.Threading.Tasks \n Module Program \n Public Shared Async Function TestAsync() As Task(Of Integer) \n Await Task.Delay(1) \n Function Sub \n End Module")
)
End Function
......@@ -91,7 +97,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Function rtrt() As Task
Async Function rtrtAsync() As Task
Await Nothing
End Function
</ModuleDeclaration>
......@@ -108,13 +114,30 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Sub rtrt()
Async Sub rtrtAsync()
Await Nothing
End Sub
</ModuleDeclaration>
Await TestAsync(initial, expected)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)>
Public Async Function TestBadAwaitInNonAsyncVoidMethod1() As Task
Dim initial =
<ModuleDeclaration>
Sub rtrt()
[|Await Nothing|]
End Sub
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Function rtrtAsync() As Threading.Tasks.Task
Await Nothing
End Function
</ModuleDeclaration>
Await TestAsync(initial, expected, index:=1)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)>
Public Async Function TestBadAwaitInNonAsyncFunction() As Task
Dim initial =
......@@ -125,7 +148,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Function rtrt() As Task
Async Function rtrtAsync() As Task
Await Nothing
End Function
</ModuleDeclaration>
......@@ -142,7 +165,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Function rtrt() As Task(Of Integer)
Async Function rtrtAsync() As Task(Of Integer)
Await Nothing
End Function
</ModuleDeclaration>
......@@ -159,7 +182,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Async
</ModuleDeclaration>
Dim expected =
<ModuleDeclaration>
Async Function rtrt() As Threading.Tasks.Task(Of Integer)
Async Function rtrtAsync() As Threading.Tasks.Task(Of Integer)
Await Nothing
End Function
</ModuleDeclaration>
......@@ -179,7 +202,7 @@ End Class
Dim expected =
<File>
Class Program
Async Function rtrt() As Task
Async Function rtrtAsync() As Task
Await Nothing
End Function
End Class
......@@ -200,7 +223,7 @@ End Class
Dim expected =
<File>
Class Program
Async Function rtrt() As Task(Of Integer)
Async Function rtrtAsync() As Task(Of Integer)
Await Nothing
End Function
End Class
......@@ -221,7 +244,7 @@ End Class
Dim expected =
<File>
Class Program
Async Function rtrt() As System.Threading.Tasks.Task(Of Integer)
Async Function rtrtAsync() As System.Threading.Tasks.Task(Of Integer)
Await Nothing
End Function
End Class
......@@ -242,7 +265,7 @@ End Class
Dim expected =
<File>
Class Program
Async Function rtrt() As System.Threading.Tasks.Task(Of Program)
Async Function rtrtAsync() As System.Threading.Tasks.Task(Of Program)
Await Nothing
End Function
End Class
......@@ -263,7 +286,7 @@ End Class
Dim expected =
<File>
Class Program
Async Function rtrt() As System.Threading.Tasks.Task(Of asdf)
Async Function rtrtAsync() As System.Threading.Tasks.Task(Of asdf)
Await Nothing
End Function
End Class
......@@ -288,11 +311,5 @@ End Module
</File>
Await TestMissingAsync(initial)
End Function
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)(
Nothing,
New VisualBasicAddAsyncCodeFixProvider())
End Function
End Class
End Namespace
......@@ -66,7 +66,6 @@
<Compile Include="ChangeSignature\UnifiedArgumentSyntax.cs" />
<Compile Include="CodeFixes\AddImport\CSharpAddImportCodeFixProvider.cs" />
<Compile Include="CodeFixes\AddMissingReference\AddMissingReferenceCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\CSharpAddAsyncCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\CSharpAddAwaitCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\CSharpConvertToAsyncMethodCodeFixProvider.cs" />
<Compile Include="CodeFixes\FullyQualify\CSharpFullyQualifyCodeFixProvider.cs" />
......@@ -329,6 +328,7 @@
<Compile Include="LanguageServices\CSharpSymbolDisplayService.cs" />
<Compile Include="LanguageServices\CSharpSymbolDisplayService.SymbolDescriptionBuilder.cs" />
<Compile Include="LanguageServices\CSharpSymbolDisplayServiceFactory.cs" />
<Compile Include="MakeMethodAsynchronous\CSharpMakeMethodAsynchronousCodeFixProvider.cs" />
<Compile Include="MakeMethodSynchronous\CSharpMakeMethodSynchronousCodeFixProvider.cs" />
<Compile Include="MetadataAsSource\CSharpMetadataAsSourceService.cs" />
<Compile Include="MetadataAsSource\CSharpMetadataAsSourceServiceFactory.cs" />
......
......@@ -548,15 +548,6 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Make the containing scope &apos;async&apos;..
/// </summary>
internal static string Make_the_containing_scope_async {
get {
return ResourceManager.GetString("Make_the_containing_scope_async", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;member name&gt; = .
/// </summary>
......
......@@ -255,9 +255,6 @@
<data name="Insert_await" xml:space="preserve">
<value>Insert 'await'.</value>
</data>
<data name="Make_the_containing_scope_async" xml:space="preserve">
<value>Make the containing scope 'async'.</value>
</data>
<data name="Make_0_return_Task_instead_of_void" xml:space="preserve">
<value>Make {0} return Task instead of void.</value>
</data>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.Async;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.Async
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAsync), Shared]
internal class CSharpAddAsyncCodeFixProvider : AbstractAddAsyncCodeFixProvider
{
/// <summary>
/// The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
/// </summary>
private const string CS4032 = nameof(CS4032);
/// <summary>
/// The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
/// </summary>
private const string CS4033 = nameof(CS4033);
/// <summary>
/// The 'await' operator can only be used within an async lambda expression. Consider marking this method with the 'async' modifier.
/// </summary>
private const string CS4034 = nameof(CS4034);
public override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(CS4032, CS4033, CS4034); }
}
protected override string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
{
return CSharpFeaturesResources.Make_the_containing_scope_async;
}
protected override async Task<SyntaxNode> GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken)
{
var nodeToModify = GetContainingMember(oldNode);
if (nodeToModify == null)
{
return null;
}
var modifiedNode = await ConvertToAsync(nodeToModify, semanticModel, document, cancellationToken).ConfigureAwait(false);
if (modifiedNode != null)
{
return root.ReplaceNode(nodeToModify, modifiedNode);
}
return null;
}
private static SyntaxNode GetContainingMember(SyntaxNode oldNode)
{
foreach (var node in oldNode.Ancestors())
{
switch (node.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
if ((node as AnonymousFunctionExpressionSyntax)?.AsyncKeyword.Kind() != SyntaxKind.AsyncKeyword)
{
return node;
}
break;
case SyntaxKind.MethodDeclaration:
if ((node as MethodDeclarationSyntax)?.Modifiers.Any(SyntaxKind.AsyncKeyword) == false)
{
return node;
}
break;
default:
continue;
}
}
return null;
}
private Task<SyntaxNode> ConvertToAsync(SyntaxNode node, SemanticModel semanticModel, Document document, CancellationToken cancellationToken)
{
return node.TypeSwitch(
(MethodDeclarationSyntax methodNode) => ConvertMethodToAsync(document, semanticModel, methodNode, cancellationToken),
(ParenthesizedLambdaExpressionSyntax parenthesizedLambda) => Task.FromResult(ConvertParenthesizedLambdaToAsync(parenthesizedLambda)),
(SimpleLambdaExpressionSyntax simpleLambda) => Task.FromResult(ConvertSimpleLambdaToAsync(simpleLambda)),
(AnonymousMethodExpressionSyntax anonymousMethod) => Task.FromResult(ConvertAnonymousMethodToAsync(anonymousMethod)),
@default => Task.FromResult<SyntaxNode>(null));
}
private static SyntaxNode ConvertParenthesizedLambdaToAsync(ParenthesizedLambdaExpressionSyntax parenthesizedLambda)
{
return SyntaxFactory.ParenthesizedLambdaExpression(
SyntaxFactory.Token(SyntaxKind.AsyncKeyword),
parenthesizedLambda.ParameterList,
parenthesizedLambda.ArrowToken,
parenthesizedLambda.Body)
.WithTriviaFrom(parenthesizedLambda)
.WithAdditionalAnnotations(Formatter.Annotation);
}
private static SyntaxNode ConvertSimpleLambdaToAsync(SimpleLambdaExpressionSyntax simpleLambda)
{
return SyntaxFactory.SimpleLambdaExpression(
SyntaxFactory.Token(SyntaxKind.AsyncKeyword),
simpleLambda.Parameter,
simpleLambda.ArrowToken,
simpleLambda.Body)
.WithTriviaFrom(simpleLambda)
.WithAdditionalAnnotations(Formatter.Annotation);
}
private static SyntaxNode ConvertAnonymousMethodToAsync(AnonymousMethodExpressionSyntax anonymousMethod)
{
return SyntaxFactory.AnonymousMethodExpression(
SyntaxFactory.Token(SyntaxKind.AsyncKeyword),
anonymousMethod.DelegateKeyword,
anonymousMethod.ParameterList,
anonymousMethod.Block)
.WithTriviaFrom(anonymousMethod)
.WithAdditionalAnnotations(Formatter.Annotation);
}
protected override SyntaxNode AddAsyncKeyword(SyntaxNode node)
{
var methodNode = node as MethodDeclarationSyntax;
if (methodNode == null)
{
return null;
}
return methodNode
.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword))
.WithAdditionalAnnotations(Formatter.Annotation);
}
protected override SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode node, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol)
{
var methodNode = node as MethodDeclarationSyntax;
if (methodNode == null)
{
return null;
}
if (taskTypeSymbol == null)
{
return null;
}
var returnType = taskTypeSymbol.Construct(existingReturnType).GenerateTypeSyntax();
return AddAsyncKeyword(methodNode.WithReturnType(returnType));
}
protected override bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination)
{
return compilation.ClassifyConversion(source, destination).Exists;
}
}
}
// Copyright (c) Microsoft. 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.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
......@@ -17,7 +19,7 @@
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.Async
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAwait), Shared]
internal class CSharpAddAwaitCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvider
internal class CSharpAddAwaitCodeFixProvider : AbstractAddAwaitCodeFixProvider
{
/// <summary>
/// Because this call is not awaited, execution of the current method continues before the call is completed.
......@@ -36,10 +38,20 @@ internal class CSharpAddAwaitCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvi
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CS0029, CS4014, CS4016);
protected override string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) =>
CSharpFeaturesResources.Insert_await;
protected override async Task<DescriptionAndNode> GetDescriptionAndNodeAsync(
SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken)
{
var newRoot = await GetNewRootAsync(
root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false);
if (newRoot == null)
{
return default(DescriptionAndNode);
}
return new DescriptionAndNode(CSharpFeaturesResources.Insert_await, newRoot);
}
protected override Task<SyntaxNode> GetNewRoot(
private Task<SyntaxNode> GetNewRootAsync(
SyntaxNode root,
SyntaxNode oldNode,
SemanticModel semanticModel,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MakeMethodAsynchronous;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.MakeMethodAsynchronous
{
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
internal class CSharpMakeMethodAsynchronousCodeFixProvider : AbstractMakeMethodAsynchronousCodeFixProvider
{
private const string CS4032 = nameof(CS4032); // The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
private const string CS4033 = nameof(CS4033); // The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
private const string CS4034 = nameof(CS4034); // The 'await' operator can only be used within an async lambda expression. Consider marking this method with the 'async' modifier.
private static readonly SyntaxToken s_asyncToken = SyntaxFactory.Token(SyntaxKind.AsyncKeyword);
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(CS4032, CS4033, CS4034);
protected override bool IsMethodOrAnonymousFunction(SyntaxNode node)
{
return node.IsKind(SyntaxKind.MethodDeclaration) || node.IsAnyLambdaOrAnonymousMethod();
}
protected override SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType)
{
return node.TypeSwitch(
(MethodDeclarationSyntax method) => FixMethod(keepVoid, methodSymbolOpt, method, taskType, taskOfTType),
(AnonymousMethodExpressionSyntax method) => FixAnonymousMethod(method),
(ParenthesizedLambdaExpressionSyntax lambda) => FixParenthesizedLambda(lambda),
(SimpleLambdaExpressionSyntax lambda) => FixSimpleLambda(lambda),
_ => node);
}
private SyntaxNode FixMethod(
bool keepVoid, IMethodSymbol methodSymbol, MethodDeclarationSyntax method,
ITypeSymbol taskType, INamedTypeSymbol taskOfTType)
{
var newReturnType = method.ReturnType;
if (methodSymbol.ReturnsVoid)
{
if (!keepVoid)
{
newReturnType = taskType.GenerateTypeSyntax();
}
}
else
{
if (!IsTaskLike(methodSymbol.ReturnType, taskType, taskOfTType))
{
// If it's not already Task-like, then wrap the existing return type
// in Task<>.
newReturnType = taskOfTType.Construct(methodSymbol.ReturnType).GenerateTypeSyntax();
}
}
var newModifiers = method.Modifiers.Add(s_asyncToken);
return method.WithReturnType(newReturnType).WithModifiers(newModifiers);
}
private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda)
{
return lambda.WithoutLeadingTrivia()
.WithAsyncKeyword(s_asyncToken.WithPrependedLeadingTrivia(lambda.GetLeadingTrivia()));
}
private SyntaxNode FixSimpleLambda(SimpleLambdaExpressionSyntax lambda)
{
return lambda.WithoutLeadingTrivia()
.WithAsyncKeyword(s_asyncToken.WithPrependedLeadingTrivia(lambda.GetLeadingTrivia()));
}
private SyntaxNode FixAnonymousMethod(AnonymousMethodExpressionSyntax method)
{
return method.WithoutLeadingTrivia()
.WithAsyncKeyword(s_asyncToken.WithPrependedLeadingTrivia(method.GetLeadingTrivia()));
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
namespace Microsoft.CodeAnalysis.CodeFixes.Async
{
internal abstract partial class AbstractAddAsyncCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvider
{
protected const string SystemThreadingTasksTask = "System.Threading.Tasks.Task";
protected const string SystemThreadingTasksTaskT = "System.Threading.Tasks.Task`1";
protected abstract SyntaxNode AddAsyncKeyword(SyntaxNode methodNode);
protected abstract SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode methodNode, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol);
protected abstract bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination);
protected async Task<SyntaxNode> ConvertMethodToAsync(Document document, SemanticModel semanticModel, SyntaxNode methodNode, CancellationToken cancellationToken)
{
var methodSymbol = semanticModel.GetDeclaredSymbol(methodNode, cancellationToken) as IMethodSymbol;
if (methodSymbol.ReturnsVoid)
{
return AddAsyncKeyword(methodNode);
}
var returnType = methodSymbol.ReturnType;
var compilation = semanticModel.Compilation;
var taskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTask);
var genericTaskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTaskT);
if (taskSymbol == null)
{
return null;
}
if (returnType is IErrorTypeSymbol)
{
// The return type of the method will not bind. This could happen for a lot of reasons.
// The type may not actually exist or the user could just be missing a using/import statement.
// We're going to try and see if there are any known types that have the same name as
// our return type, and then check if those are convertible to Task. If they are then
// we assume the user just has a missing using. If they are not, we wrap the return
// type in a generic Task.
var typeName = returnType.Name;
var syntaxFacts = document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var results = await SymbolFinder.FindDeclarationsAsync(
document.Project, typeName, ignoreCase: syntaxFacts.IsCaseSensitive, filter: SymbolFilter.Type, cancellationToken: cancellationToken).ConfigureAwait(false);
if (results.OfType<ITypeSymbol>().Any(s => DoesConversionExist(compilation, s, taskSymbol)))
{
return AddAsyncKeyword(methodNode);
}
return AddAsyncKeywordAndTaskReturnType(methodNode, returnType, genericTaskSymbol);
}
if (DoesConversionExist(compilation, returnType, taskSymbol))
{
return AddAsyncKeyword(methodNode);
}
return AddAsyncKeywordAndTaskReturnType(methodNode, returnType, genericTaskSymbol);
}
}
}
// Copyright (c) Microsoft. 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.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
namespace Microsoft.CodeAnalysis.CodeFixes.Async
{
internal abstract partial class AbstractAddAsyncAwaitCodeFixProvider : AbstractAsyncCodeFix
internal abstract partial class AbstractAddAwaitCodeFixProvider : AbstractAsyncCodeFix
{
protected abstract string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
protected abstract Task<SyntaxNode> GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken);
protected abstract Task<DescriptionAndNode> GetDescriptionAndNodeAsync(
SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken);
protected override async Task<CodeAction> GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
protected override async Task<CodeAction> GetCodeActionAsync(
SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var newRoot = await this.GetNewRoot(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false);
if (newRoot != null)
var data = await this.GetDescriptionAndNodeAsync(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false);
if (data.Node == null)
{
return new MyCodeAction(
this.GetDescription(diagnostic, node, semanticModel, cancellationToken),
token => Task.FromResult(document.WithSyntaxRoot(newRoot)));
return null;
}
return null;
return new MyCodeAction(
data.Description,
c => Task.FromResult(document.WithSyntaxRoot(data.Node)));
}
protected static bool TryGetExpressionType(
......@@ -51,5 +53,17 @@ private class MyCodeAction : CodeAction.DocumentChangeAction
{
}
}
protected struct DescriptionAndNode
{
public readonly string Description;
public readonly SyntaxNode Node;
public DescriptionAndNode(string description, SyntaxNode node)
{
Description = description;
Node = node;
}
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -11,7 +12,8 @@ namespace Microsoft.CodeAnalysis.CodeFixes.Async
{
internal abstract partial class AbstractAsyncCodeFix : CodeFixProvider
{
protected abstract Task<CodeAction> GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken);
protected abstract Task<CodeAction> GetCodeActionAsync(
SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken);
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
......@@ -25,11 +27,11 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
var diagnostic = context.Diagnostics.FirstOrDefault();
var codeAction = await GetCodeFix(root, node, context.Document, diagnostic, context.CancellationToken).ConfigureAwait(false);
var codeAction = await GetCodeActionAsync(
root, node, context.Document, diagnostic, context.CancellationToken).ConfigureAwait(false);
if (codeAction != null)
{
context.RegisterCodeFix(codeAction, diagnostic);
context.RegisterCodeFix(codeAction, context.Diagnostics);
}
}
......@@ -46,4 +48,4 @@ private bool TryGetNode(SyntaxNode root, TextSpan span, out SyntaxNode node)
return node != null;
}
}
}
}
\ No newline at end of file
......@@ -12,22 +12,23 @@ internal abstract partial class AbstractChangeToAsyncCodeFixProvider : AbstractA
protected abstract Task<string> GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
protected abstract Task<Tuple<SyntaxTree, SyntaxNode>> GetRootInOtherSyntaxTree(SyntaxNode node, SemanticModel semanticModel, Diagnostic diagnostic, CancellationToken cancellationToken);
protected override async Task<CodeAction> GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
protected override async Task<CodeAction> GetCodeActionAsync(
SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var result = await GetRootInOtherSyntaxTree(node, semanticModel, diagnostic, cancellationToken).ConfigureAwait(false);
if (result != null)
if (result == null)
{
var syntaxTree = result.Item1;
var newRoot = result.Item2;
var otherDocument = document.Project.Solution.GetDocument(syntaxTree);
return new MyCodeAction(
await this.GetDescription(diagnostic, node, semanticModel, cancellationToken).ConfigureAwait(false),
token => Task.FromResult(otherDocument.WithSyntaxRoot(newRoot)));
return null;
}
return null;
var syntaxTree = result.Item1;
var newRoot = result.Item2;
var otherDocument = document.Project.Solution.GetDocument(syntaxTree);
return new MyCodeAction(
await this.GetDescription(diagnostic, node, semanticModel, cancellationToken).ConfigureAwait(false),
token => Task.FromResult(otherDocument.WithSyntaxRoot(newRoot)));
}
private class MyCodeAction : CodeAction.DocumentChangeAction
......
......@@ -24,10 +24,13 @@ internal static void RegisterFixes(this CodeFixContext context, IEnumerable<Code
/// </summary>
internal static void RegisterFixes(this CodeFixContext context, IEnumerable<CodeAction> actions, ImmutableArray<Diagnostic> diagnostics)
{
foreach (var action in actions)
if (actions != null)
{
context.RegisterCodeFix(action, diagnostics);
foreach (var action in actions)
{
context.RegisterCodeFix(action, diagnostics);
}
}
}
}
}
}
\ No newline at end of file
......@@ -110,8 +110,7 @@
<Compile Include="CodeFixes\FixAllOccurrences\FixMultipleCodeAction.cs" />
<Compile Include="CodeFixes\FixAllOccurrences\FixAllCodeAction.cs" />
<Compile Include="CodeFixes\AddImport\AbstractAddImportCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\AbstractAddAsyncAwaitCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\AbstractAddAsyncCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\AbstractAddAwaitCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\AbstractChangeToAsyncCodeFixProvider.cs" />
<Compile Include="CodeFixes\Async\AbstractAsyncCodeFix.cs" />
<Compile Include="CodeFixes\CodeFixCollection.cs" />
......@@ -242,6 +241,7 @@
<Compile Include="FindReferences\SourceReferenceItem.cs" />
<Compile Include="Diagnostics\EngineV2\InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs" />
<Compile Include="Diagnostics\EngineV2\ICodeAnalysisDiagnosticAnalyzerExecutor.cs" />
<Compile Include="MakeMethodAsynchronous\AbstractMakeMethodAsynchronousCodeFixProvider.cs" />
<Compile Include="ReplacePropertyWithMethods\AbstractReplacePropertyWithMethodsService.cs" />
<Compile Include="ReplacePropertyWithMethods\IReplacePropertyWithMethodsService.cs" />
<Compile Include="ReplacePropertyWithMethods\ReplacePropertyWithMethodsCodeRefactoringProvider.cs" />
......
......@@ -1620,6 +1620,42 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Make containing scope async.
/// </summary>
internal static string Make_containing_scope_async {
get {
return ResourceManager.GetString("Make_containing_scope_async", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make containing scope async (return Task).
/// </summary>
internal static string Make_containing_scope_async_return_Task {
get {
return ResourceManager.GetString("Make_containing_scope_async_return_Task", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make method async.
/// </summary>
internal static string Make_method_async {
get {
return ResourceManager.GetString("Make_method_async", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make method async (return Task).
/// </summary>
internal static string Make_method_async_return_Task {
get {
return ResourceManager.GetString("Make_method_async_return_Task", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make method synchronous..
/// </summary>
......
......@@ -1046,6 +1046,18 @@ This version used in: {2}</value>
<data name="Remove_tag" xml:space="preserve">
<value>Remove tag</value>
</data>
<data name="Make_containing_scope_async" xml:space="preserve">
<value>Make containing scope async</value>
</data>
<data name="Make_containing_scope_async_return_Task" xml:space="preserve">
<value>Make containing scope async (return Task)</value>
</data>
<data name="Make_method_async" xml:space="preserve">
<value>Make method async</value>
</data>
<data name="Make_method_async_return_Task" xml:space="preserve">
<value>Make method async (return Task)</value>
</data>
<data name="paren_Unknown_paren" xml:space="preserve">
<value>(Unknown)</value>
</data>
......
// Copyright (c) Microsoft. 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.MakeMethodAsyncOrSync
{
internal abstract class AbstractMakeMethodAsyncOrAsyncCodeFixProvider : CodeFixProvider
{
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var cancellationToken = context.CancellationToken;
var node = GetContainingFunction(diagnostic, cancellationToken);
if (node == null)
{
return;
}
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken) as IMethodSymbol;
// If it's a void returning method, offer to keep the void return type, or convert to
// a Task return type.
if (symbol?.MethodKind == MethodKind.Ordinary &&
symbol.ReturnsVoid)
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: true, cancellationToken: c)),
context.Diagnostics);
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async_return_Task, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
else
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
}
private const string AsyncSuffix = "Async";
private async Task<Solution> FixNodeAsync(
Document document, Diagnostic diagnostic,
bool keepVoid, CancellationToken cancellationToken)
{
var node = GetContainingFunction(diagnostic, cancellationToken);
// See if we're on an actual method declaration (otherwise we're on a lambda declaration).
// If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check
// if it has the 'Async' suffix, and remove that suffix if so.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
!methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
return await RenameThenAddAsyncTokenAsync(
keepVoid, document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false);
}
else
{
return await AddAsyncTokenAsync(
keepVoid, document, methodSymbolOpt, node, cancellationToken).ConfigureAwait(false);
}
}
private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
return node;
}
private async Task<Solution> RenameThenAddAsyncTokenAsync(
bool keepVoid, Document document, SyntaxNode node,
IMethodSymbol methodSymbol, CancellationToken cancellationToken)
{
var name = methodSymbol.Name;
var newName = name + AsyncSuffix;
var solution = document.Project.Solution;
// Store the path to this node. That way we can find it post rename.
var syntaxPath = new SyntaxPath(node);
// Rename the method to add the 'Async' suffix, then add the 'async' keyword.
var newSolution = await Renamer.RenameSymbolAsync(solution, methodSymbol, newName, solution.Options, cancellationToken).ConfigureAwait(false);
var newDocument = newSolution.GetDocument(document.Id);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newNode;
if (syntaxPath.TryResolve(newRoot, out newNode))
{
var semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var newMethod = (IMethodSymbol)semanticModel.GetDeclaredSymbol(newNode, cancellationToken);
return await AddAsyncTokenAsync(keepVoid, newDocument, newMethod, newNode, cancellationToken).ConfigureAwait(false);
}
return newSolution;
}
private async Task<Solution> AddAsyncTokenAsync(
bool keepVoid, Document document, IMethodSymbol methodSymbolOpt,
SyntaxNode node, CancellationToken cancellationToken)
{
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
var taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType)
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = root.ReplaceNode(node, newNode);
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument.Project.Solution;
}
private class MyCodeAction : CodeAction.SolutionChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Solution>> createChangedSolution)
: base(title, createChangedSolution, equivalenceKey: title)
{
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.MakeMethodAsynchronous
{
internal abstract class AbstractMakeMethodAsynchronousCodeFixProvider : CodeFixProvider
{
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var cancellationToken = context.CancellationToken;
var node = GetContainingFunction(diagnostic, cancellationToken);
if (node == null)
{
return;
}
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken) as IMethodSymbol;
// If it's a void returning method, offer to keep the void return type, or convert to
// a Task return type.
if (symbol?.MethodKind == MethodKind.Ordinary &&
symbol.ReturnsVoid)
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: true, cancellationToken: c)),
context.Diagnostics);
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async_return_Task, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
else
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
}
private const string AsyncSuffix = "Async";
private async Task<Solution> FixNodeAsync(
Document document, Diagnostic diagnostic,
bool keepVoid, CancellationToken cancellationToken)
{
var node = GetContainingFunction(diagnostic, cancellationToken);
// See if we're on an actual method declaration (otherwise we're on a lambda declaration).
// If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check
// if it has the 'Async' suffix, and remove that suffix if so.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
!methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
return await RenameThenAddAsyncTokenAsync(
keepVoid, document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false);
}
else
{
return await AddAsyncTokenAsync(
keepVoid, document, methodSymbolOpt, node, cancellationToken).ConfigureAwait(false);
}
}
private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
return node;
}
private async Task<Solution> RenameThenAddAsyncTokenAsync(
bool keepVoid, Document document, SyntaxNode node,
IMethodSymbol methodSymbol, CancellationToken cancellationToken)
{
var name = methodSymbol.Name;
var newName = name + AsyncSuffix;
var solution = document.Project.Solution;
// Store the path to this node. That way we can find it post rename.
var syntaxPath = new SyntaxPath(node);
// Rename the method to add the 'Async' suffix, then add the 'async' keyword.
var newSolution = await Renamer.RenameSymbolAsync(solution, methodSymbol, newName, solution.Options, cancellationToken).ConfigureAwait(false);
var newDocument = newSolution.GetDocument(document.Id);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newNode;
if (syntaxPath.TryResolve(newRoot, out newNode))
{
var semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var newMethod = (IMethodSymbol)semanticModel.GetDeclaredSymbol(newNode, cancellationToken);
return await AddAsyncTokenAsync(keepVoid, newDocument, newMethod, newNode, cancellationToken).ConfigureAwait(false);
}
return newSolution;
}
private async Task<Solution> AddAsyncTokenAsync(
bool keepVoid, Document document, IMethodSymbol methodSymbolOpt,
SyntaxNode node, CancellationToken cancellationToken)
{
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
var taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType)
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = root.ReplaceNode(node, newNode);
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument.Project.Solution;
}
private class MyCodeAction : CodeAction.SolutionChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Solution>> createChangedSolution)
: base(title, createChangedSolution, equivalenceKey: title)
{
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.MakeMethodSynchronous
{
internal abstract class AbstractMakeMethodSynchronousCodeFixProvider : CodeFixProvider
{
public static readonly string EquivalenceKey = FeaturesResources.Make_method_synchronous;
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixNodeAsync(context.Document, context.Diagnostics.First(), c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
private const string AsyncSuffix = "Async";
private async Task<Solution> FixNodeAsync(
Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
// See if we're on an actual method declaration (otherwise we're on a lambda declaration).
// If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check
// if it has the 'Async' suffix, and remove that suffix if so.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
methodSymbolOpt.Name.Length > AsyncSuffix.Length &&
methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
return await RenameThenRemoveAsyncTokenAsync(document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false);
}
else
{
return await RemoveAsyncTokenAsync(document, methodSymbolOpt, node, cancellationToken).ConfigureAwait(false);
}
}
private async Task<Solution> RenameThenRemoveAsyncTokenAsync(Document document, SyntaxNode node, IMethodSymbol methodSymbol, CancellationToken cancellationToken)
{
var name = methodSymbol.Name;
var newName = name.Substring(0, name.Length - AsyncSuffix.Length);
var solution = document.Project.Solution;
// Store the path to this node. That way we can find it post rename.
var syntaxPath = new SyntaxPath(node);
// Rename the method to remove the 'Async' suffix, then remove the 'async' keyword.
var newSolution = await Renamer.RenameSymbolAsync(solution, methodSymbol, newName, solution.Options, cancellationToken).ConfigureAwait(false);
var newDocument = newSolution.GetDocument(document.Id);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newNode;
if (syntaxPath.TryResolve<SyntaxNode>(newRoot, out newNode))
{
var semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var newMethod = (IMethodSymbol)semanticModel.GetDeclaredSymbol(newNode, cancellationToken);
return await RemoveAsyncTokenAsync(newDocument, newMethod, newNode, cancellationToken).ConfigureAwait(false);
}
return newSolution;
}
private async Task<Solution> RemoveAsyncTokenAsync(Document document, IMethodSymbol methodSymbolOpt, SyntaxNode node, CancellationToken cancellationToken)
{
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
var taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
var newNode = RemoveAsyncTokenAndFixReturnType(methodSymbolOpt, node, taskType, taskOfTType)
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = root.ReplaceNode(node, newNode);
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument.Project.Solution;
}
private class MyCodeAction : CodeAction.SolutionChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Solution>> createChangedSolution)
: base(FeaturesResources.Make_method_synchronous, createChangedSolution, AbstractMakeMethodSynchronousCodeFixProvider.EquivalenceKey)
{
}
}
}
}
// Copyright (c) Microsoft. 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.MakeMethodAsynchronous
{
internal abstract class AbstractMakeMethodAsynchronousCodeFixProvider : CodeFixProvider
{
protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node);
protected abstract SyntaxNode AddAsyncTokenAndFixReturnType(
bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node,
INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var cancellationToken = context.CancellationToken;
var node = GetContainingFunction(diagnostic, cancellationToken);
if (node == null)
{
return;
}
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbol = semanticModel.GetDeclaredSymbol(node, cancellationToken) as IMethodSymbol;
// If it's a void returning method, offer to keep the void return type, or convert to
// a Task return type.
if (symbol?.MethodKind == MethodKind.Ordinary &&
symbol.ReturnsVoid)
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: true, cancellationToken: c)),
context.Diagnostics);
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async_return_Task, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
else
{
context.RegisterCodeFix(
new MyCodeAction(FeaturesResources.Make_method_async, c => FixNodeAsync(
context.Document, diagnostic, keepVoid: false, cancellationToken: c)),
context.Diagnostics);
}
}
private const string AsyncSuffix = "Async";
private async Task<Solution> FixNodeAsync(
Document document, Diagnostic diagnostic,
bool keepVoid, CancellationToken cancellationToken)
{
var node = GetContainingFunction(diagnostic, cancellationToken);
// See if we're on an actual method declaration (otherwise we're on a lambda declaration).
// If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check
// if it has the 'Async' suffix, and remove that suffix if so.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol;
if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary &&
!methodSymbolOpt.Name.EndsWith(AsyncSuffix))
{
return await RenameThenAddAsyncTokenAsync(
keepVoid, document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false);
}
else
{
return await AddAsyncTokenAsync(
keepVoid, document, methodSymbolOpt, node, cancellationToken).ConfigureAwait(false);
}
}
private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToken cancellationToken)
{
var token = diagnostic.Location.FindToken(cancellationToken);
var node = token.GetAncestor(IsMethodOrAnonymousFunction);
return node;
}
private async Task<Solution> RenameThenAddAsyncTokenAsync(
bool keepVoid, Document document, SyntaxNode node,
IMethodSymbol methodSymbol, CancellationToken cancellationToken)
{
var name = methodSymbol.Name;
var newName = name + AsyncSuffix;
var solution = document.Project.Solution;
// Store the path to this node. That way we can find it post rename.
var syntaxPath = new SyntaxPath(node);
// Rename the method to add the 'Async' suffix, then add the 'async' keyword.
var newSolution = await Renamer.RenameSymbolAsync(solution, methodSymbol, newName, solution.Options, cancellationToken).ConfigureAwait(false);
var newDocument = newSolution.GetDocument(document.Id);
var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newNode;
if (syntaxPath.TryResolve(newRoot, out newNode))
{
var semanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var newMethod = (IMethodSymbol)semanticModel.GetDeclaredSymbol(newNode, cancellationToken);
return await AddAsyncTokenAsync(keepVoid, newDocument, newMethod, newNode, cancellationToken).ConfigureAwait(false);
}
return newSolution;
}
private async Task<Solution> AddAsyncTokenAsync(
bool keepVoid, Document document, IMethodSymbol methodSymbolOpt,
SyntaxNode node, CancellationToken cancellationToken)
{
var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var taskType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task");
var taskOfTType = compilation.GetTypeByMetadataName("System.Threading.Tasks.Task`1");
var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType)
.WithAdditionalAnnotations(Formatter.Annotation);
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newRoot = root.ReplaceNode(node, newNode);
var newDocument = document.WithSyntaxRoot(newRoot);
return newDocument.Project.Solution;
}
protected static bool IsTaskLike(
ITypeSymbol returnType, ITypeSymbol taskType, INamedTypeSymbol taskOfTType)
{
if (returnType.Equals(taskType))
{
return true;
}
if (returnType.OriginalDefinition.Equals(taskOfTType))
{
return true;
}
if (returnType.IsErrorType() &&
returnType.Name.Equals("Task"))
{
return true;
}
return false;
}
private class MyCodeAction : CodeAction.SolutionChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Solution>> createChangedSolution)
: base(title, createChangedSolution, equivalenceKey: title)
{
}
}
}
}
\ No newline at end of file
......@@ -91,7 +91,6 @@
<Compile Include="CodeFixes\InsertMissingCast\InsertMissingCastCodeFixProvider.CodeAction.vb" />
<Compile Include="CodeFixes\InsertMissingCast\InsertMissingCastCodeFixProvider.vb" />
<Compile Include="CodeFixes\AddMissingReference\AddMissingReferenceCodeFixProvider.vb" />
<Compile Include="CodeFixes\Async\VisualBasicAddAsyncCodeFixProvider.vb" />
<Compile Include="CodeFixes\Async\VisualBasicAddAwaitCodeFixProvider.vb" />
<Compile Include="CodeFixes\Async\VisualBasicConvertToAsyncFunctionCodeFixProvider.vb" />
<Compile Include="CodeFixes\Iterator\VisualBasicChangeToYieldCodeFixProvider.vb" />
......@@ -352,6 +351,7 @@
<Compile Include="LanguageServices\VisualBasicSymbolDisplayService.SymbolDescriptionBuilder.vb" />
<Compile Include="LanguageServices\VisualBasicSymbolDisplayService.vb" />
<Compile Include="LanguageServices\VisualBasicSymbolDisplayServiceFactory.vb" />
<Compile Include="MakeMethodAsynchronous\VisualBasicMakeMethodAsynchronousCodeFixProvider.vb" />
<Compile Include="MakeMethodSynchronous\VisualBasicMakeMethodSynchronousCodeFixProvider.vb" />
<Compile Include="MetadataAsSource\VisualBasicMetadataAsSourceService.vb" />
<Compile Include="MetadataAsSource\VisualBasicMetadataAsSourceServiceFactory.vb" />
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.CodeFixes.Async
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.AddAsync), [Shared]>
Friend Class VisualBasicAddAsyncCodeFixProvider
Inherits AbstractAddAsyncCodeFixProvider
Friend Const BC36937 As String = "BC36937" ' error BC36937: 'Await' can only be used when contained within a method or lambda expression marked with the 'Async' modifier.
Friend Const BC37057 As String = "BC37057" ' error BC37057: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
Friend Const BC37058 As String = "BC37058" ' error BC37058: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
Friend Const BC37059 As String = "BC37059" ' error BC37059: 'Await' can only be used within an Async lambda expression. Consider marking this expression with the 'Async' modifier and changing its return type to 'Task'.
Friend Shared ReadOnly Ids As ImmutableArray(Of String) = ImmutableArray.Create(BC36937, BC37057, BC37058, BC37059)
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get
Return Ids
End Get
End Property
Protected Overrides Function GetDescription(diagnostic As Diagnostic, node As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As String
Return VBFeaturesResources.Make_the_containing_scope_Async
End Function
Protected Overrides Async Function GetNewRoot(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Dim methodNode = GetContainingMember(oldNode)
If methodNode Is Nothing Then
Return Nothing
End If
Return root.ReplaceNode(methodNode, Await ConvertToAsync(methodNode, semanticModel, document, cancellationToken).ConfigureAwait(False))
End Function
Private Shared Function GetContainingMember(oldNode As SyntaxNode) As StatementSyntax
Dim lambda = oldNode.GetAncestor(Of LambdaExpressionSyntax)
If lambda IsNot Nothing Then
Return TryCast(lambda.ChildNodes().FirstOrDefault(Function(a) _
a.IsKind(SyntaxKind.FunctionLambdaHeader) Or
a.IsKind(SyntaxKind.SubLambdaHeader)),
StatementSyntax)
End If
Dim ancestor As MethodBlockBaseSyntax = oldNode.GetAncestor(Of MethodBlockBaseSyntax)
If ancestor IsNot Nothing Then
Return TryCast(ancestor.ChildNodes().FirstOrDefault(Function(a) _
a.IsKind(SyntaxKind.SubStatement) Or
a.IsKind(SyntaxKind.FunctionStatement)),
StatementSyntax)
End If
Return Nothing
End Function
Private Async Function ConvertToAsync(node As StatementSyntax, semanticModel As SemanticModel, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Dim methodNode = TryCast(node, MethodStatementSyntax)
If methodNode IsNot Nothing Then
Return Await ConvertMethodToAsync(document, semanticModel, methodNode, cancellationToken).ConfigureAwait(False)
End If
Dim lambdaNode = TryCast(node, LambdaHeaderSyntax)
If lambdaNode IsNot Nothing Then
Return lambdaNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)).WithAdditionalAnnotations(Formatter.Annotation)
End If
Return Nothing
End Function
Protected Overrides Function AddAsyncKeyword(node As SyntaxNode) As SyntaxNode
Dim methodNode = TryCast(node, MethodStatementSyntax)
If methodNode Is Nothing Then
Return Nothing
End If
' Visual Basic includes newlines in the trivia for MethodStatementSyntax nodes. If we are
' inserting into the beginning of method statement, we will need to move the trivia.
Dim token = methodNode.ChildTokens().FirstOrDefault()
Dim keyword = methodNode.DeclarationKeyword
If keyword = token Then
Dim trivia = token.LeadingTrivia
methodNode = methodNode.ReplaceToken(token, token.WithLeadingTrivia())
Return methodNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword).WithLeadingTrivia(trivia)).WithAdditionalAnnotations(Formatter.Annotation)
End If
Return methodNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)).WithAdditionalAnnotations(Formatter.Annotation)
End Function
Protected Overrides Function AddAsyncKeywordAndTaskReturnType(node As SyntaxNode, existingReturnType As ITypeSymbol, taskTypeSymbol As INamedTypeSymbol) As SyntaxNode
Dim methodNode = TryCast(node, MethodStatementSyntax)
If methodNode Is Nothing Then
Return Nothing
End If
If taskTypeSymbol Is Nothing Then
Return Nothing
End If
Dim returnType = taskTypeSymbol.Construct(existingReturnType).GenerateTypeSyntax()
Return AddAsyncKeyword(methodNode.WithAsClause(methodNode.AsClause.WithType(returnType)))
End Function
Protected Overrides Function DoesConversionExist(compilation As Compilation, source As ITypeSymbol, destination As ITypeSymbol) As Boolean
Return compilation.ClassifyConversion(source, destination).Exists
End Function
End Class
End Namespace
......@@ -15,7 +15,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.AddAwait), [Shared]>
Friend Class VisualBasicAddAwaitCodeFixProvider
Inherits AbstractAddAsyncAwaitCodeFixProvider
Inherits AbstractAddAwaitCodeFixProvider
Friend Const BC30311 As String = "BC30311" ' error BC30311: Value of type 'X' cannot be converted to 'Y'.
Friend Const BC37055 As String = "BC37055" ' error BC37055: Since this is an async method, the return expression must be of type 'blah' rather than 'baz'
......@@ -29,11 +29,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async
End Get
End Property
Protected Overrides Function GetDescription(diagnostic As Diagnostic, node As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As String
Return VBFeaturesResources.Insert_Await
Protected Overrides Async Function GetDescriptionAndNodeAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of DescriptionAndNode)
Dim newRoot = Await GetNewRootAsync(
root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(False)
If newRoot Is Nothing Then
Return Nothing
End If
Return New DescriptionAndNode(VBFeaturesResources.Insert_Await, newRoot)
End Function
Protected Overrides Function GetNewRoot(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Private Function GetNewRootAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Dim expression = TryCast(oldNode, ExpressionSyntax)
If expression Is Nothing Then
Return SpecializedTasks.Default(Of SyntaxNode)()
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.MakeMethodAsynchronous
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous
<ExportCodeFixProvider(LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicMakeMethodAsynchronousCodeFixProvider
Inherits AbstractMakeMethodAsynchronousCodeFixProvider
Friend Const BC36937 As String = "BC36937" ' error BC36937: 'Await' can only be used when contained within a method or lambda expression marked with the 'Async' modifier.
Friend Const BC37057 As String = "BC37057" ' error BC37057: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
Friend Const BC37058 As String = "BC37058" ' error BC37058: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
Friend Const BC37059 As String = "BC37059" ' error BC37059: 'Await' can only be used within an Async lambda expression. Consider marking this expression with the 'Async' modifier and changing its return type to 'Task'.
Private Shared ReadOnly s_diagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(
BC36937, BC37057, BC37058, BC37059)
Private Shared ReadOnly s_asyncToken As SyntaxToken = SyntaxFactory.Token(SyntaxKind.AsyncKeyword)
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get
Return s_diagnosticIds
End Get
End Property
Protected Overrides Function IsMethodOrAnonymousFunction(node As SyntaxNode) As Boolean
Return node.IsKind(SyntaxKind.FunctionBlock) OrElse
node.IsKind(SyntaxKind.SubBlock) OrElse
node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineSubLambdaExpression)
End Function
Protected Overrides Function AddAsyncTokenAndFixReturnType(
keepVoid As Boolean, methodSymbolOpt As IMethodSymbol, node As SyntaxNode,
taskType As INamedTypeSymbol, taskOfTType As INamedTypeSymbol) As SyntaxNode
If node.IsKind(SyntaxKind.SingleLineSubLambdaExpression) OrElse
node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) Then
Return FixSingleLineLambdaExpression(DirectCast(node, SingleLineLambdaExpressionSyntax))
ElseIf node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse
node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) Then
Return FixMultiLineLambdaExpression(DirectCast(node, MultiLineLambdaExpressionSyntax))
ElseIf node.IsKind(SyntaxKind.SubBlock) Then
Return FixSubBlock(keepVoid, DirectCast(node, MethodBlockSyntax), taskType)
Else
Return FixFunctionBlock(methodSymbolOpt, DirectCast(node, MethodBlockSyntax), taskType, taskOfTType)
End If
End Function
Private Function FixFunctionBlock(methodSymbol As IMethodSymbol, node As MethodBlockSyntax,
taskType As INamedTypeSymbol, taskOfTType As INamedTypeSymbol) As SyntaxNode
Dim functionStatement = node.SubOrFunctionStatement
Dim newFunctionStatement = AddAsyncKeyword(functionStatement)
If Not IsTaskLike(methodSymbol.ReturnType, taskType, taskOfTType) Then
' if the current return type is not already task-list, then wrap it in Task(of ...)
Dim returnType = taskOfTType.Construct(methodSymbol.ReturnType).GenerateTypeSyntax()
newFunctionStatement = newFunctionStatement.WithAsClause(
newFunctionStatement.AsClause.WithType(returnType))
End If
Return node.WithSubOrFunctionStatement(newFunctionStatement)
End Function
Private Function FixSubBlock(
keepVoid As Boolean, node As MethodBlockSyntax, taskType As INamedTypeSymbol) As SyntaxNode
If keepVoid Then
' User wants to keep this a void method, so keep this as a sub.
Dim newSubStatement = AddAsyncKeyword(node.SubOrFunctionStatement)
Return node.WithSubOrFunctionStatement(newSubStatement)
End If
' Have to convert this sub into a func.
Dim asClause = SyntaxFactory.SimpleAsClause(taskType.GenerateTypeSyntax())
Dim subStatement = node.SubOrFunctionStatement
Dim functionStatement = SyntaxFactory.FunctionStatement(
subStatement.AttributeLists,
subStatement.Modifiers.Add(s_asyncToken),
SyntaxFactory.Token(SyntaxKind.FunctionKeyword).WithTriviaFrom(subStatement.SubOrFunctionKeyword),
subStatement.Identifier,
subStatement.TypeParameterList,
subStatement.ParameterList,
asClause,
subStatement.HandlesClause,
subStatement.ImplementsClause)
Dim endFunctionStatement = SyntaxFactory.EndFunctionStatement(
node.EndSubOrFunctionStatement.EndKeyword,
SyntaxFactory.Token(SyntaxKind.FunctionKeyword).WithTriviaFrom(node.EndSubOrFunctionStatement.BlockKeyword))
Dim block = SyntaxFactory.FunctionBlock(
functionStatement,
node.Statements,
endFunctionStatement)
Return block
End Function
Private Shared Function AddAsyncKeyword(subOrFunctionStatement As MethodStatementSyntax) As MethodStatementSyntax
Dim modifiers = subOrFunctionStatement.Modifiers
Dim newModifiers = modifiers.Add(s_asyncToken)
Return subOrFunctionStatement.WithModifiers(newModifiers)
End Function
Private Function FixMultiLineLambdaExpression(node As MultiLineLambdaExpressionSyntax) As SyntaxNode
Dim header As LambdaHeaderSyntax = GetNewHeader(node)
Return node.WithSubOrFunctionHeader(header).WithLeadingTrivia(node.GetLeadingTrivia())
End Function
Private Function FixSingleLineLambdaExpression(node As SingleLineLambdaExpressionSyntax) As SingleLineLambdaExpressionSyntax
Dim header As LambdaHeaderSyntax = GetNewHeader(node)
Return node.WithSubOrFunctionHeader(header).WithLeadingTrivia(node.GetLeadingTrivia())
End Function
Private Shared Function GetNewHeader(node As LambdaExpressionSyntax) As LambdaHeaderSyntax
Dim header = DirectCast(node.SubOrFunctionHeader, LambdaHeaderSyntax)
Dim newModifiers = header.Modifiers.Add(s_asyncToken)
Dim newHeader = header.WithModifiers(newModifiers)
Return newHeader
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -1520,15 +1520,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Make the containing scope &apos;Async&apos;..
'''</summary>
Friend ReadOnly Property Make_the_containing_scope_Async() As String
Get
Return ResourceManager.GetString("Make_the_containing_scope_Async", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Mid statement.
'''</summary>
......
......@@ -979,9 +979,6 @@ Sub(&lt;parameterList&gt;) &lt;statement&gt;</value>
<data name="Insert_Await" xml:space="preserve">
<value>Insert 'Await'.</value>
</data>
<data name="Make_the_containing_scope_Async" xml:space="preserve">
<value>Make the containing scope 'Async'.</value>
</data>
<data name="Make_0_an_Async_Function" xml:space="preserve">
<value>Make {0} an Async Function.</value>
</data>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册