Skip to content

Fix VSTHRD110 firing in Expression-valued scenarios#1467

Merged
AArnott merged 4 commits into
mainfrom
copilot/fix-1167
Jun 11, 2025
Merged

Fix VSTHRD110 firing in Expression-valued scenarios#1467
AArnott merged 4 commits into
mainfrom
copilot/fix-1167

Conversation

Copilot AI commented Jun 11, 2025

Copy link
Copy Markdown
Contributor

Summary

This PR fixes VSTHRD110 analyzer incorrectly firing when Task-valued lambdas are passed to methods expecting Expression<> parameters. In these scenarios, the lambda is converted to an expression tree (data structure) rather than being executed, so no warning should be shown.

Problem

When using libraries like Moq, VSTHRD110 would incorrectly fire on legitimate code:

using Moq;

var mock = new Mock<ILogger>();
mock.Verify(
    x => x.InfoAsync(It.IsAny<string>()), // VSTHRD110 incorrectly fires here
    Times.Never,
    "No Log should have been written");

public interface ILogger
{
    Task InfoAsync(string message);
}

The lambda x => x.InfoAsync(It.IsAny<string>()) is converted to an Expression<Func<ILogger, Task>> for inspection by Moq, not actually executed, so VSTHRD110 should not apply.

Solution

Added detection logic to AbstractVSTHRD110ObserveResultOfAsyncCallsAnalyzer that:

  1. Checks if invocation is within a lambda: Walks up the operation tree to find containing IAnonymousFunctionOperation
  2. Detects Expression<> conversion: Identifies when the lambda is converted to System.Linq.Expressions.Expression<T> via:
    • IConversionOperation (direct assignment: Expression<Func<T, Task>> expr = x => x.Method())
    • IArgumentOperation (method parameter: SomeMethod(x => x.Method()) where parameter is Expression<>)
  3. Suppresses diagnostic: Skips VSTHRD110 when expression tree conversion is detected

Test Coverage

Added comprehensive test cases covering:

  • ✅ Moq.Verify-like scenarios (mock.Verify(x => x.InfoAsync("test"), Times.Never))
  • ✅ Direct assignment (Expression<Func<T, Task>> expr = x => x.InfoAsync("test"))
  • ✅ Method parameters (SomeMethod(x => x.InfoAsync("test")) where parameter is Expression<>)
  • ✅ Regression test ensuring normal Task calls still trigger VSTHRD110

Edge Cases Handled

  • Intermediate operations like parentheses between lambda and conversion
  • Generic Expression<> types with various Func signatures
  • Other expression tree types in System.Linq.Expressions namespace

Fixes #1167.

Warning

Firewall rules blocked me from connecting to one or more addresses

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 1elvsblobprodwus2177.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 2wrvsblobprodwus2180.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • 31bvsblobprodwus2143.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 5o1vsblobprodwus2115.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 5zivsblobprodwus217.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 78kvsblobprodwus2152.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 8wdvsblobprodwus2137.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • 9yyvsblobprodwus2157.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • abcvsblobprodwus2161.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • an0vsblobprodwus2146.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • b15vsblobprodwus2164.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • b53vsblobprodwus2154.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • cffvsblobprodwus218.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • d94vsblobprodwus2119.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • dyrvsblobprodwus2173.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • fcwvsblobprodwus2113.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • h8evsblobprodwus2120.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • i01vsblobprodwus216.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • isovsblobprodwus211.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • jk4vsblobprodwus2186.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • jlovsblobprodwus2163.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: /home/REDACTED/work/_temp/ghcca-node/node/bin/node /home/REDACTED/work/_temp/copilot-developer-action-main/dist/index.js (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
  • km4vsblobprodwus2183.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • mdcvsblobprodwus2159.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • nypvsblobprodwus2178.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • pkwvsblobprodwus2126.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • s0ivsblobprodwus2134.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • s8nvsblobprodwus2158.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • tn2vsblobprodwus2124.vsblob.vsassets.io
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • v41vsblobprodwus2175.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • vd2vsblobprodwus2150.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • vipvsblobprodwus2139.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
  • wztvsblobprodwus2155.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • x0dvsblobprodwus2111.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • x1mvsblobprodwus2181.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • xktvsblobprodwus2141.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • xofvsblobprodwus2114.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • y1mvsblobprodwus2138.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet build src/Microsoft.VisualStudio.Threading.Analyzers/Microsoft.VisualStudio.Threading.Analyzers.csproj --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • z2bvsblobprodwus2185.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • zaxvsblobprodwus215.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)
  • zugvsblobprodwus2169.vsblob.vsassets.io
    • Triggering command: dotnet build --configuration Release (dns block)
    • Triggering command: dotnet restore --configfile nuget.config (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI and others added 2 commits June 11, 2025 02:54
Co-authored-by: AArnott <3548+AArnott@users.noreply.github.com>
Co-authored-by: AArnott <3548+AArnott@users.noreply.github.com>
Copilot AI changed the title [WIP] VSTHRD110 fires in Expression-valued scenarios Fix VSTHRD110 firing in Expression-valued scenarios Jun 11, 2025
Copilot AI requested a review from AArnott June 11, 2025 03:00
@AArnott

AArnott commented Jun 11, 2025

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@AArnott

AArnott commented Jun 11, 2025

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@AArnott AArnott marked this pull request as ready for review June 11, 2025 13:40
@AArnott AArnott requested review from davkean and lifengl June 11, 2025 13:40
@AArnott AArnott enabled auto-merge (squash) June 11, 2025 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VSTHRD110 fires in Expression-valued scenarios

3 participants