Fix VSTHRD103 false positives for generic task flows - #1664
Fix VSTHRD103 false positives for generic task flows#1664Andrew Arnott (AArnott) wants to merge 2 commits into
Conversation
Recognize variance-aware generic task flow through synchronous composition APIs so async alternatives are not incorrectly preferred. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the VSTHRD103 (and related VSTHRD002) analyzer logic to avoid false positives when a synchronous generic method is simply composing async-compatible values (e.g., projecting items to Task for Task.WhenAll) rather than blocking. It implements variance-aware detection of async-compatible type flow from parameters into returned container shapes and adds targeted regression tests (Fixes #1662).
Changes:
- Suppress VSTHRD103 suggestions when a generic method’s return value carries async-compatible values that flow from its inputs (including projections, containers/arrays/tuples, and reduced extension methods).
- Ensure VSTHRD002 still reports when such a method is explicitly configured as sync-blocking via additional files.
- Add test coverage for the reported
Select+Task.WhenAllscenario and several additional flow/non-flow shapes.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs | Adds regression tests for Select→Task.WhenAll and broader generic “task-like flow” shapes. |
| test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD002UseJtfRunAnalyzerTests.cs | Adds coverage to ensure configured sync-blocking methods still report under VSTHRD002. |
| src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD103UseAsyncOptionAnalyzer.cs | Uses the new flow-detection helper to avoid reporting VSTHRD103 in valid task-composition patterns. |
| src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/VSTHRD002UseJtfRunAnalyzer.cs | Excludes flow-through methods from being considered “covered by VSTHRD103” so configured blocking behavior remains reported. |
| src/Microsoft.VisualStudio.Threading.Analyzers.CSharp/CSharpCommonInterest.cs | Introduces ReturnsAsyncCompatibleValuesFromParameters with variance-aware generic flow analysis. |
Suppressed comments (1)
test/Microsoft.VisualStudio.Threading.Analyzers.Tests/VSTHRD103UseAsyncOptionAnalyzerTests.cs:2083
- This test method is marked
asyncbut has noawait, which produces CS1998 and can introduce avoidable warnings in the test compilation. Consider making it a non-asyncTaskmethod and returnTask.CompletedTask, using discards for the locals that are only present to trigger analyzer behavior.
async Task ProcessAllAsync()
{
IEnumerable<Task> tasks = {|#0:GetTasks|}();
{|#1:Run|}(() => Task.CompletedTask);
(Task, int) wrappedTask = {|#2:Wrap|}(Task.CompletedTask);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
VSTHRD103 currently treats an Async-suffixed overload as preferable even when a synchronous generic API is only composing asynchronous work, such as projecting items to tasks for
Task.WhenAll.This change recognizes variance-aware generic type-parameter flow from inputs into task-bearing results. It covers LINQ projections and more general container, array, tuple, and reduced-extension patterns without suppressing APIs that merely consume tasks or independently return them. VSTHRD002 retains ownership when such a method is explicitly configured as blocking.
Tests cover the reported direct and assigned
Selectforms, broader task-like flow shapes, contravariant consumers, non-generic near-misses, and configured VSTHRD002 behavior.Fixes #1662