Skip to content

Fix VSTHRD103 false positives for generic task flows - #1664

Open
Andrew Arnott (AArnott) wants to merge 2 commits into
mainfrom
aarnott-fix-vsthrd103-task-flows
Open

Fix VSTHRD103 false positives for generic task flows#1664
Andrew Arnott (AArnott) wants to merge 2 commits into
mainfrom
aarnott-fix-vsthrd103-task-flows

Conversation

@AArnott

Copy link
Copy Markdown
Member

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 Select forms, broader task-like flow shapes, contravariant consumers, non-generic near-misses, and configured VSTHRD002 behavior.

Fixes #1662

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>
Copilot AI lite review requested due to automatic review settings August 27, 2026 21:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.WhenAll scenario 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 SelectTask.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 async but has no await, which produces CS1998 and can introduce avoidable warnings in the test compilation. Consider making it a non-async Task method and return Task.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>
Copilot AI review requested due to automatic review settings August 27, 2026 21:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VSTHRD103 false positive for Task.WhenAll with Select

2 participants