Repository navigation
refactor: remove unnecessary async state machines (VSTHRD202) - #201
Conversation
Resolves the 31 open SonarQube code smells on develop, all VSTHRD202: pass-through methods now return the inner Task directly instead of awaiting it. The rule is raised to warning in .editorconfig so the build (TreatWarningsAsErrors) prevents regressions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The changes are a uniform, mechanical refactor whose only semantic effect (synchronous guard throws) is safe because every verified caller awaits the methods directly without composing the returned tasks.
0 open findings
What changed in this PR
This PR resolves all 31 SonarQube VSTHRD202 ("Remove unnecessary async state machine") code smells. It targets pass-through methods whose body was a single trailing await, rewriting them to return the inner Task directly, which removes the compiler-generated async state machine overhead. It also wires the rule into the local build so regressions are caught before Sonar.
Changes:
- Converted 31
async/awaitpass-through sites across 14src/andtests/files to returnTask/Task<T>directly. - Four methods with a synchronous guard before the returned call (
AnalyzeContextUseCase,AnalyzeSnapshotUseCase,ProjGraphTools.PreparePathAsync,SpectreOutputConsole.RunWithStatusAsync) now throw guard exceptions synchronously; all callersawaitdirectly, so observed behavior is unchanged. - Enabled
dotnet_diagnostic.VSTHRD202.severity = warningin.editorconfig, enforced locally via the already-referencedMicrosoft.VisualStudio.Threading.Analyzerspackage andTreatWarningsAsErrors.
| File | Description |
|---|---|
| .editorconfig | Promotes VSTHRD202 to a warning so the build enforces it. |
| src/ProjGraph.Mcp/Program.cs | Main returns host.RunAsync() directly. |
| src/ProjGraph.Mcp/ProjGraphTools.cs | PreparePathAsync returns the resolve task; guard now throws synchronously (callers await). |
| src/ProjGraph.Lib.Core/Infrastructure/SpectreOutputConsole.cs | PromptSelectionAsync/RunWithStatusAsync return inner tasks; status lambda keeps its own async. |
| src/ProjGraph.Lib.EntityFramework/Application/EfAnalysisService.cs | Four service methods pass through tasks directly. |
| src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeContextUseCase.cs | Guard then returns analyzer task directly. |
| src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeSnapshotUseCase.cs | Guard then returns analyzer task directly. |
| src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs | SearchDirectoryForEntitiesAsync returns the recursive task. |
| src/ProjGraph.Lib.ClassDiagram/Application/ClassAnalysisService.cs | Two analysis methods pass through tasks directly. |
| src/ProjGraph.Lib.ClassDiagram/Infrastructure/TypeProcessor.cs | ProcessTypeQueueAsync returns internal task directly. |
| src/ProjGraph.Lib.ClassDiagram/Infrastructure/WorkspaceTypeDiscovery.cs | Computes root synchronously, returns search task. |
| tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpServerProcess.cs | ConnectAsync returns McpClient.CreateAsync directly. |
| tests/ProjGraph.Tests.Smoke.Aot/Helpers/McpServerPair.cs | Same connect-helper conversion. |
| tests/ProjGraph.Tests.Smoke.Aot/CliParityTests.cs | Test methods return parity-assertion tasks directly. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Summary
SonarQube reports 31 open code smells on
develop. All of them areexternal_roslyn:VSTHRD202("Remove unnecessary async state machine"). This PR fixes every one.Taskdirectly. Each one had a single trailingawait, soasync/awaitis dropped (14 files, 31 sites insrc/andtests/)..editorconfigsetsdotnet_diagnostic.VSTHRD202.severity = warning. WithTreatWarningsAsErrors, the local build now catches any new case before Sonar does.Behavioural note
Four of the methods run a synchronous guard before the returned call:
AnalyzeContextUseCase,AnalyzeSnapshotUseCase,ProjGraphTools.PreparePathAsyncandSpectreOutputConsole.RunWithStatusAsync. Their guard exceptions are now thrown synchronously instead of coming back as a faulted task. Every caller awaits these methods directly, so callers see the same exception.Test plan
dotnet build ProjGraph.slnx --no-incremental: 0 warnings, 0 errors with VSTHRD202 enforced (before the fix it reported the same 31 sites as Sonar)dotnet test ProjGraph.slnx: all 1,181 tests passaot-smokeCI job: theTests.Smoke.Aotchanges compile locally but only run in CI🤖 Generated with Claude Code