dotnet / dotnet/fsharp

P1: `DocumentDiagnosticAnalyzer` repeats typecheck/diagnostics work on every crawler pass (no version-stamp cache)

Open
#20,120 0 comments 0 reactions 0 assignees View on GitHub
Needs-Triage
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 11h
Merged PRs (30d)
131

Description

## Problem

`FSharpDocumentDiagnosticAnalyzer.GetDiagnostics` (in `vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs`) recomputes syntax/semantic diagnostics (parse, typecheck, `UnusedParentheses`) on every invocation from the Roslyn solution crawler, even when the underlying document text and project state have not changed since the last computation.

This causes redundant CPU work (parsing, typechecking, and diagnostic conversion) to repeat unnecessarily during idle background analysis passes, contributing to overall background CPU churn in the F# editor experience.

## Impact

Identified as part of a broader background-activity minimization effort (see prior P1 fix for `Compilation.Emit` metadata-only re-emission in `FSharpProjectOptionsManager.fs`). CPU profiling of `devenv.exe` shows a significant share of samples spent in typecheck (`FSharp.Compiler.CheckDeclarations`) and `UnusedOpens` computation paths reachable from this analyzer, some of which are redundant re-computations for documents whose content/version has not changed between crawler passes.

## Proposed Fix

Add a version-stamp-aware cache keyed by `(DocumentId, DiagnosticsType)`, storing the last computed `(textVersion, projectVersion, diagnostics)` tuple. On each call, compare current `document.GetTextVersionAsync()` (and, for semantic diagnostics, `document.Project.GetDependentVersionAsync()`) against the cached versions; if unchanged, return the cached `ImmutableArray` instead of recomputing.

This mirrors the versioned-cache pattern already used for referenced-project compilation emission in `FSharpProjectOptionsManager.fs` (`emitCache: ConcurrentDictionary>`).

## Repro / Validation

1. Open a large F# solution in Visual Studio.
2. Collect a CPU trace (Instrumentation/Sampling) of `devenv.exe` while idle with a document open.
3. Observe repeated `FSharp.Compiler.CheckDeclarations` / `FSharp.Compiler.EditorServices.UnusedOpens` invocations from `FSharpDocumentDiagnosticAnalyzer.GetDiagnostics` for a document whose version has not changed between crawler passes.

Contributor guide

Open the contributing guide

Research direction

Start with GetDiagnostics in vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs, then compare the versioned cache pattern in FSharpProjectOptionsManager.fs. Trace document and dependent-project version retrieval, and validate with an idle devenv.exe CPU trace. Done means unchanged versions reuse diagnostics while changed document or project versions recompute them.

Written by the indexing model from the issue text.

Assessment

Tech stack
fsharp
Domain
developer-experience, devtools, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.