From 0add87c68d8bb798ba477526ac738aee8746b5cb Mon Sep 17 00:00:00 2001 From: Neil Henderson Date: Tue, 15 Jun 2021 14:59:29 +1000 Subject: [PATCH] Fixes #849. AnalyzeAwaitedOrReturnedExpression now detects if the invocation is wrapped inside parentheses. --- .../VSTHRD003UseJtfRunAsyncAnalyzer.cs | 8 +++++ .../VSTHRD003UseJtfRunAsyncAnalyzerTests.cs | 34 +++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs index 223f648d0..c33579333 100644 --- a/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs +++ b/src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD003UseJtfRunAsyncAnalyzer.cs @@ -134,6 +134,14 @@ private void AnalyzeAwaitExpression(SyntaxNodeAnalysisContext context) SymbolInfo symbolToConsider = semanticModel.GetSymbolInfo(expressionSyntax, cancellationToken); if (CommonInterest.TaskConfigureAwait.Any(configureAwait => configureAwait.IsMatch(symbolToConsider.Symbol))) { + // If the invocation is wrapped inside parentheses then drill down to get the invocation. + while (expressionSyntax is ParenthesizedExpressionSyntax parenthesizedExprSyntax) + { + expressionSyntax = parenthesizedExprSyntax.Expression; + } + + Debug.Assert(expressionSyntax is InvocationExpressionSyntax, "expressionSyntax should be an invocation"); + if (((InvocationExpressionSyntax)expressionSyntax).Expression is MemberAccessExpressionSyntax memberAccessExpression) { symbolToConsider = semanticModel.GetSymbolInfo(memberAccessExpression.Expression, cancellationToken); diff --git a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs index 1f248245d..8fd19cc15 100644 --- a/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs +++ b/test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD003UseJtfRunAsyncAnalyzerTests.cs @@ -27,10 +27,12 @@ class Tests public void Test() { JoinableTaskFactory jtf = ThreadHelper.JoinableTaskFactory; - System.Threading.Tasks.Task task = SomeOperationAsync(); + System.Threading.Tasks.Task task1 = SomeOperationAsync(); + System.Threading.Tasks.Task task2 = SomeOperationAsync(); jtf.Run(async delegate { - await task; + await task1; + await (task2); // Bug 849 }); } @@ -42,7 +44,11 @@ public async Task SomeOperationAsync() } } "; - DiagnosticResult expected = Verify.Diagnostic().WithLocation(14, 19); + DiagnosticResult[] expected = + { + Verify.Diagnostic().WithLocation(15, 19), + Verify.Diagnostic().WithLocation(16, 19), + }; await Verify.VerifyAnalyzerAsync(test, expected); } @@ -1224,6 +1230,28 @@ static Task MyMethodAsync() await test.RunAsync(); } + [Fact] + public async Task DoNotReportWarningWithParenthesizedAwaitExpressions() + { + // This is a test for bug 849. Parenthesized expressions caused an InvalidCastException. + var test = @" +using System.Threading.Tasks; + +class Test { + async Task FooAsync() { + await (Task.Delay(1)); + await ((Task.Delay(1))); + await (((Task.Delay(1)))); + + await (Task.Delay(1).ConfigureAwait(false)); + await ((Task.Delay(1).ConfigureAwait(false))); + await (((Task.Delay(1).ConfigureAwait(false)))); + } +} +"; + await Verify.VerifyAnalyzerAsync(test); + } + private DiagnosticResult CreateDiagnostic(int line, int column, int length) => Verify.Diagnostic().WithSpan(line, column, line, column + length); }