diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs index 1709e2800..516df0ca7 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs @@ -923,6 +923,94 @@ internal static void Foo() { } this.VerifyCSharpFix(test, withFix); } + [Fact] + public void AsyncAlternative_CodeFixRespectsTrivia() + { + var test = @" +using System; +using System.Threading.Tasks; + +class Test { + void Foo() { } + Task FooAsync() => Task.CompletedTask; + + async Task DoWorkAsync() + { + await Task.Yield(); + Console.WriteLine(""Foo""); + + // Some comment + Foo(/*argcomment*/); // another comment + } +} +"; + var withFix = @" +using System; +using System.Threading.Tasks; + +class Test { + void Foo() { } + Task FooAsync() => Task.CompletedTask; + + async Task DoWorkAsync() + { + await Task.Yield(); + Console.WriteLine(""Foo""); + + // Some comment + await FooAsync(/*argcomment*/); // another comment + } +} +"; + this.expect.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 15, 9, 15, 12) }; + this.VerifyCSharpDiagnostic(test, this.expect); + this.VerifyCSharpFix(test, withFix); + } + + [Fact] + public void AwaitRatherThanWait_CodeFixRespectsTrivia() + { + var test = @" +using System; +using System.Threading.Tasks; + +class Test { + void Foo() { } + Task FooAsync() => Task.CompletedTask; + + async Task DoWorkAsync() + { + await Task.Yield(); + Console.WriteLine(""Foo""); + + // Some comment + FooAsync(/*argcomment*/).Wait(); // another comment + } +} +"; + var withFix = @" +using System; +using System.Threading.Tasks; + +class Test { + void Foo() { } + Task FooAsync() => Task.CompletedTask; + + async Task DoWorkAsync() + { + await Task.Yield(); + Console.WriteLine(""Foo""); + + // Some comment + await FooAsync(/*argcomment*/); // another comment + } +} +"; + this.expect.Locations = new[] { new DiagnosticResultLocation("Test0.cs", 15, 34, 15, 38) }; + this.VerifyCSharpDiagnostic(test, this.expect); + this.VerifyCSharpFix(test, withFix); + } + [Fact] public void XunitThrowAsyncNotSuggestedInAsyncTestMethod() { diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD103UseAsyncOptionCodeFix.cs b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD103UseAsyncOptionCodeFix.cs index f3b5c5687..0fadad67e 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD103UseAsyncOptionCodeFix.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers/VSTHRD103UseAsyncOptionCodeFix.cs @@ -135,8 +135,11 @@ protected override async Task GetChangedSolutionAsync(CancellationToke if (this.AlternativeAsyncMethod != string.Empty) { // Replace the member being called and await the invocation expression. + // While doing so, move leading trivia to the surrounding await expression. var asyncMethodName = syncMethodName.WithIdentifier(SyntaxFactory.Identifier(this.diagnostic.Properties[AsyncMethodKeyName])); - awaitExpression = SyntaxFactory.AwaitExpression(syncExpression.ReplaceNode(syncMethodName, asyncMethodName)); + awaitExpression = SyntaxFactory.AwaitExpression( + syncExpression.ReplaceNode(syncMethodName, asyncMethodName).WithoutLeadingTrivia()) + .WithLeadingTrivia(syncExpression.GetLeadingTrivia()); if (!(syncExpression.Parent is ExpressionStatementSyntax)) { awaitExpression = SyntaxFactory.ParenthesizedExpression(awaitExpression) @@ -156,7 +159,8 @@ protected override async Task GetChangedSolutionAsync(CancellationToke syncMemberStrippedExpression = expressionMethodCall.Expression; } - awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression); + awaitExpression = SyntaxFactory.AwaitExpression(syncMemberStrippedExpression.WithoutLeadingTrivia()) + .WithLeadingTrivia(syncMemberStrippedExpression.GetLeadingTrivia()); } updatedMethod = updatedMethod