提交 dddf7182 编写于 作者: J Julien Couvreur

Add MakeStatementAsynchronous fixer

上级 bd738fc4
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeStatementAsynchronous;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.MakeStatementAsynchronous
{
[Trait(Traits.Feature, Traits.Features.CodeActionsMakeStatementAsynchronous)]
public class CSharpMakeStatementAsynchronousCodeFixTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpMakeStatementAsynchronousCodeFixProvider());
private static readonly TestParameters s_asyncStreamsFeature = new TestParameters(parseOptions: new CSharpParseOptions(LanguageVersion.CSharp8));
private readonly string AsyncStreams = @"
namespace System.Collections.Generic
{
public interface IAsyncEnumerable<out T>
{
IAsyncEnumerator<T> GetAsyncEnumerator();
}
public interface IAsyncEnumerator<out T> : System.IAsyncDisposable
{
System.Threading.Tasks.ValueTask<bool> MoveNextAsync();
T Current { get; }
}
}
namespace System
{
public interface IAsyncDisposable
{
System.Threading.Tasks.ValueTask DisposeAsync();
}
}
";
[Fact]
public async Task FixAll()
{
await TestInRegularAndScript1Async(
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<int> collection)
{
foreach (var i in {|FixAllInDocument:collection|}) { }
foreach (var j in collection) { }
}
}",
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<int> collection)
{
await foreach (var i in collection) { }
await foreach (var j in collection) { }
}
}", parameters: s_asyncStreamsFeature);
}
[Fact]
public async Task FixForeach()
{
await TestInRegularAndScript1Async(
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<int> collection)
{
foreach (var i in [|collection|])
{
}
}
}",
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<int> collection)
{
await foreach (var i in collection)
{
}
}
}", parameters: s_asyncStreamsFeature);
}
[Fact]
public async Task FixForeachDeconstruction()
{
await TestInRegularAndScript1Async(
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<(int, int)> collection)
{
foreach (var (i, j) in collection[||])
{
}
}
}",
AsyncStreams + @"
class Program
{
void M(System.Collections.Generic.IAsyncEnumerable<(int, int)> collection)
{
await foreach (var (i, j) in collection)
{
}
}
}", parameters: s_asyncStreamsFeature);
}
[Fact]
public async Task FixUsingStatement()
{
await TestInRegularAndScript1Async(
AsyncStreams + @"
class Program
{
void M(System.IAsyncDisposable disposable)
{
using (var i = disposable[||])
{
}
}
}",
AsyncStreams + @"
class Program
{
void M(System.IAsyncDisposable disposable)
{
await using (var i = disposable)
{
}
}
}", parameters: s_asyncStreamsFeature);
}
[Fact]
public async Task FixUsingDeclaration()
{
await TestInRegularAndScript1Async(
AsyncStreams + @"
class Program
{
void M(System.IAsyncDisposable disposable)
{
using var i = disposable[||];
}
}",
AsyncStreams + @"
class Program
{
void M(System.IAsyncDisposable disposable)
{
await using var i = disposable;
}
}", parameters: s_asyncStreamsFeature);
}
}
}
......@@ -1296,6 +1296,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Use asynchronous statement.
/// </summary>
internal static string Use_asynchronous_statement {
get {
return ResourceManager.GetString("Use_asynchronous_statement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use explicit type.
/// </summary>
......
......@@ -168,6 +168,9 @@
<data name="Declare_as_nullable" xml:space="preserve">
<value>Declare as nullable</value>
</data>
<data name="Use_asynchronous_statement" xml:space="preserve">
<value>Use asynchronous statement</value>
</data>
<data name="Simplify_name_0" xml:space="preserve">
<value>Simplify name '{0}'</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.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeStatementAsynchronous
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.MakeStatementAsynchronous), Shared]
internal class CSharpMakeStatementAsynchronousCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
// error CS8414: foreach statement cannot operate on variables of type 'IAsyncEnumerable<int>' because 'IAsyncEnumerable<int>' does not contain a public instance definition for 'GetEnumerator'. Did you mean 'await foreach'?
// error CS8418: 'IAsyncDisposable': type used in a using statement must be implicitly convertible to 'System.IDisposable'. Did you mean 'await using' rather than 'using'?
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create("CS8414", "CS8418");
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var constructToFix = TryGetStatementToFix(node);
if (constructToFix == null)
{
return;
}
context.RegisterCodeFix(new MyCodeAction(
c => FixAsync(context.Document, diagnostic, c)),
context.Diagnostics);
}
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = editor.OriginalRoot;
foreach (var diagnostic in diagnostics)
{
var node = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken);
var statementToFix = TryGetStatementToFix(node);
if (statementToFix != null)
{
MakeStatementAsynchronous(document, editor, statementToFix);
}
}
return Task.CompletedTask;
}
private static void MakeStatementAsynchronous(Document document, SyntaxEditor editor, SyntaxNode statementToFix)
{
SyntaxNode newStatement;
switch (statementToFix)
{
case ForEachStatementSyntax forEach:
newStatement = forEach.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));
break;
case ForEachVariableStatementSyntax forEachDeconstruction:
newStatement = forEachDeconstruction.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));
break;
case UsingStatementSyntax usingStatement:
newStatement = usingStatement.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));
break;
case LocalDeclarationStatementSyntax localDeclaration:
newStatement = localDeclaration.WithAwaitKeyword(SyntaxFactory.Token(SyntaxKind.AwaitKeyword));
break;
default:
return;
}
editor.ReplaceNode(statementToFix, newStatement);
}
private static SyntaxNode TryGetStatementToFix(SyntaxNode node)
{
if (node.IsParentKind(SyntaxKind.ForEachStatement, SyntaxKind.ForEachVariableStatement, SyntaxKind.UsingStatement))
{
return node.Parent;
}
if (node is LocalDeclarationStatementSyntax localDeclaration && localDeclaration.UsingKeyword != default)
{
return node;
}
return null;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) :
base(CSharpFeaturesResources.Use_asynchronous_statement,
createChangedDocument,
CSharpFeaturesResources.Use_asynchronous_statement)
{
}
}
}
}
......@@ -122,6 +122,11 @@
<target state="translated">Seřadit modifikátory dostupnosti</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">Příkaz if lze zjednodušit.</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Zugriffsmodifizierer sortieren</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">Die If-Anweisung kann vereinfacht werden.</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Ordenar modificadores de accesibilidad</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">La instrucción "if" se puede simplificar</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Trier les modificateurs d'accessibilité</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">L'instruction 'if' peut être simplifiée</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Ordina i modificatori di accessibilità</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">L'istruzione 'If' può essere semplificata</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">アクセシビリティ修飾子を並べ替える</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">'if' ステートメントは簡素化できます</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">접근성 한정자 정렬</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">'if' 문을 간단하게 줄일 수 있습니다.</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Sortuj modyfikatory dostępności</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">Instrukcja „if” może zostać uproszczona</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Classificar modificadores de acessibilidade</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">A instrução 'if' pode ser simplificada</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Сортировать модификаторы доступности</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">Оператор if можно упростить</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">Erişilebilirlik değiştiricilerini sırala</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">'If' deyimi basitleştirilebilir</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">对可访问性修饰符排序</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">可简化“If”语句</target>
......
......@@ -122,6 +122,11 @@
<target state="translated">排序協助工具修飾詞</target>
<note />
</trans-unit>
<trans-unit id="Use_asynchronous_statement">
<source>Use asynchronous statement</source>
<target state="new">Use asynchronous statement</target>
<note />
</trans-unit>
<trans-unit id="if_statement_can_be_simplified">
<source>'if' statement can be simplified</source>
<target state="translated">'if' 陳述式可簡化</target>
......
......@@ -37,6 +37,7 @@ internal static class PredefinedCodeFixProviderNames
public const string ImplementInterface = nameof(ImplementInterface);
public const string InsertMissingCast = nameof(InsertMissingCast);
public const string MakeFieldReadonly = nameof(MakeFieldReadonly);
public const string MakeStatementAsynchronous = nameof(MakeStatementAsynchronous);
public const string MakeMethodSynchronous = nameof(MakeMethodSynchronous);
public const string MoveToTopOfFile = nameof(MoveToTopOfFile);
public const string PopulateSwitch = nameof(PopulateSwitch);
......
......@@ -101,6 +101,7 @@ public static class Features
public const string CodeActionsInvokeDelegateWithConditionalAccess = "CodeActions.InvokeDelegateWithConditionalAccess";
public const string CodeActionsLambdaSimplifier = "CodeActions.LambdaSimplifier";
public const string CodeActionsMakeFieldReadonly = "CodeActions.MakeFieldReadonly";
public const string CodeActionsMakeStatementAsynchronous = "CodeActions.MakeStatementAsynchronous";
public const string CodeActionsMakeLocalFunctionStatic = "CodeActions.MakeLocalFunctionStatic";
public const string CodeActionsMakeMethodAsynchronous = "CodeActions.MakeMethodAsynchronous";
public const string CodeActionsMakeMethodSynchronous = "CodeActions.MakeMethodSynchronous";
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册