Skip to content

[Bug]: .editorconfig severity overrides are read once at load and never refreshed #238

Description

@MelbourneDeveloper

What happened?

.editorconfig severity overrides are read once, when the sidecar first loads the solution, and never again. Editing .editorconfig in a running session has no effect on published diagnostics until the language server is restarted.

Concretely: test-fixtures/workspace/.editorconfig sets

[*.{cs,vb}]
dotnet_diagnostic.CS0219.severity = error

and Refactor.cs has an unused local. dotnet build agrees and emits error CS0219. The live session keeps showing it as a warning.

This directly violates [DIAG-LSP-REFRESH], which already lists .editorconfig file change inside the solutionas a required refresh trigger, and the project's "100% reactive" hard rule inCLAUDE.md`.

Root cause

Nothing in the stack observes .editorconfig:

  1. No file watcher covers it. The only two globs in the extension are
    • src/editors/vscode/src/extension.ts:646const SOLUTION_FILE_GLOB = '**/*.{sln,slnx}';
    • src/editors/vscode/src/project-deps-store.ts:18const WATCH_GLOB = '**/{*.csproj,*.fsproj,Directory.Packages.props}';
  2. The Rust host registers no workspace/didChangeWatchedFiles handler at allgrep -rn "DidChangeWatchedFiles" src/sharplsp/src/ returns nothing.
  3. The sidecar never mutates its analyzer config. WithAnalyzerConfigDocumentText / AnalyzerConfigDocument appear nowhere in C# sidecar production code. WorkspaceManager.GetDiagnosticsAsync (src/sidecars/SharpLsp.Sidecar.CSharp/Workspace/WorkspaceManager.cs:227) reads through MapDiagnosticsmodel.GetDiagnostics() (.../WorkspaceManager.Helpers.cs:23) against the long-lived MSBuildWorkspace solution, whose AnalyzerConfigDocument text is a snapshot from load time.

So the severity is baked into the compilation at load and stays there.

Evidence

Roslyn itself is not at fault — it honours the file correctly on the exact code path the sidecar uses. Standalone probe against TestFixtures.csproj with the same package versions (Microsoft.CodeAnalysis 5.3.0, Microsoft.Build.Locator 1.11.2):

AnalyzerConfigDocuments: 3
  ACD: C:\Code\SharpLsp\.editorconfig
  ACD: ...\test-fixtures\workspace\.editorconfig
  ACD: ...\obj\Debug\net10.0\TestFixtures.GeneratedMSBuildEditorConfig.editorconfig
--- model.GetDiagnostics() (what MapDiagnostics uses) ---
  CS0219 severity=Error defaultSeverity=Warning isWarnAsError=True
SyntaxTreeOptionsProvider: Microsoft.CodeAnalysis.ProjectState+ProjectSyntaxTreeOptionsProvider

And the staleness itself, reproduced against a long-lived MSBuildWorkspace — the sidecar's exact lifetime model:

1. editorconfig says error -> live server reports: Error
2. editorconfig now says none -> live server STILL reports: Error
3. after restart (fresh load) -> reports: <absent>

AnalyzerConfigDocument text held by the live workspace after the edit:
  root = true |  | [*.cs] | dotnet_diagnostic.CS0219.severity = error |

Step 2 is the bug: the file on disk said none, the workspace still served Error, and the AnalyzerConfigDocument still held the pre-edit text.

dotnet build on the fixture, for comparison:

Refactor.cs(7,20): error CS0219: The variable 'unused' is assigned but its value is never used
    1 Warning(s)
    1 Error(s)

Steps to reproduce

  1. Open src/editors/vscode/test-fixtures/workspace and let the server load TestFixtures.sln.
  2. Open Refactor.csCS0219 is reported at its default severity, Warning.
  3. Add dotnet_diagnostic.CS0219.severity = error under [*.{cs,vb}] in that folder's .editorconfig and save.
  4. Edit and save Refactor.cs to force a re-pull.
  5. Observed: CS0219 is still a Warning.
    Expected: it becomes an Error, matching dotnet build.
  6. Restart the language server — it now reports Error, confirming the value is only ever read at load.

Scope

The same staleness class applies to every build-input file the workspace snapshots but nothing watches: .editorconfig, Directory.Build.props / .targets, global.json, and .globalconfig. A fix should cover the family, not just .editorconfig.

The F# sidecar should be checked separately — FCS does not consume dotnet_diagnostic.*.severity the way Roslyn does, so the parity story for F# needs its own answer rather than being assumed fixed by the C# change.

Suggested fix

  1. Watch **/.editorconfig (and the siblings above) in the extension, or — better, since it is editor-agnostic — register workspace/didChangeWatchedFiles from the Rust host so every editor gets it.
  2. On change, have the sidecar apply Solution.WithAnalyzerConfigDocumentText(...) for a known document, or reload the project when the file is new/deleted.
  3. Emit the diagnostics/refresh IPC notification so the host sends workspace/diagnostic/refresh, per [DIAG-LSP-REFRESH].
  4. Cover it with a coarse e2e test: load the fixture, assert CS0219 is a Warning, rewrite .editorconfig to error, assert it becomes an Error without a restart.

Note on the pasted marker

The Problems entry that surfaced this carries "owner": "msCompile", "source": "cpp" — that is VS Code's built-in $msCompile task problem matcher, i.e. a marker left by a dotnet build task, not a SharpLsp diagnostic (SharpLsp tags its own sharplsp-csharp / sharplsp-fsharp, src/sharplsp/src/diagnostics.rs:537). That particular row is a stale build marker. The SharpLsp defect above is separate and independently reproduced.

Component

C# sidecar (Roslyn) — with a required piece in the Rust LSP host and/or VS Code extension.

Language

C# (F# needs a separate assessment, see Scope)

SharpLsp version

0.1.0

Editor & OS

VS Code, Windows 11 (26200), .NET SDK 10.0.303

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions