Skip to content

Diagnostics don't always refresh on edits. #4985

Description

@AndreasPK

Your environment

Windows/VSCode/ghc-9.10 currently but I think I've seen this issue with ghc-9.12 too.

Steps to reproduce

The Problem

Sometimes HLS doesn't refresh the diagnostics after the file changed.

Image

I think this is some race condition between updating diagnostics and saving the file.
That is a rapid succession of edit->save events makes this happen.

Activity

added
type: bugSomething isn't right: doesn't work as intended, documentation is missing/outdated, etc..
on Jun 21, 2026

crtschin commented on Jun 22, 2026

@crtschin
Collaborator

This is unfortunately quite easy to repro. The following test case fails pretty quickly running on repeat. It rapidly changes _ for different values generating diagnostics, but settling on 0 which should clear them:

foo :: Int
foo = _

Cause could be in ghcide or deeper in hls-graph.

patch containing a flaky repro
diff --git a/ghcide-test/exe/DiagnosticTests.hs b/ghcide-test/exe/DiagnosticTests.hs
index 99f2191f3..7a2e99bb8 100644
--- a/ghcide-test/exe/DiagnosticTests.hs
+++ b/ghcide-test/exe/DiagnosticTests.hs
@@ -15,7 +15,8 @@ import           Development.IDE.Test            (diagnostic,
                                                   expectDiagnostics,
                                                   expectDiagnosticsWithTags,
                                                   expectNoMoreDiagnostics,
-                                                  flushMessages, waitForAction)
+                                                  flushMessages, waitForAction,
+                                                  waitForBuildQueue)
 import           Development.IDE.Types.Location
 import qualified Language.LSP.Protocol.Lens      as L
 import           Language.LSP.Protocol.Message
@@ -39,6 +40,7 @@ import           Test.Hls                        (TestConfig (testConfigCaps, te
                                                   waitForProgressBegin)
 import           Test.Hls.FileSystem
 import           Test.Tasty
+import           Test.Tasty.ExpectedFailure      (ignoreTestBecause)
 import           Test.Tasty.HUnit
 
 tests :: TestTree
@@ -54,6 +56,21 @@ tests = testGroup "diagnostics"
               }
       changeDoc doc [change]
       expectDiagnostics [("Testing.hs", [])]
+  , testWithDummyPluginEmpty "rapid edits then save does not strand a stale diagnostic" $ do
+      let v rhs = T.unlines ["module Testing where", "foo :: Int", "foo = " <> rhs]
+          whole rhs = TextDocumentContentChangeEvent . InR . TextDocumentContentChangeWholeDocument $ v rhs
+      doc <- createDoc "Testing.hs" "haskell" (v "()")
+      expectDiagnostics [("Testing.hs", [(DiagnosticSeverity_Error, (2, 6), "Couldn't match expected type 'Int' with actual type '()'", Just "GHC-83865")])]
+      changeDoc doc [whole "()"]
+      changeDoc doc [whole "'a'"]
+      changeDoc doc [whole "True"]
+      changeDoc doc [whole "0"]
+      sendNotification SMethod_TextDocumentDidSave (DidSaveTextDocumentParams doc Nothing)
+      waitForBuildQueue
+      liftIO $ sleep 0.2
+      flushMessages
+      diags <- getCurrentDiagnostics doc
+      liftIO $ assertBool ("stale diagnostic survived: " ++ show diags) (null diags)
   , testWithDummyPluginEmpty "introduce syntax error" $ do
       let content = T.unlines [ "module Testing where" ]
       doc <- createDoc "Testing.hs" "haskell" content
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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions