Skip to content
This repository has been archived by the owner on Jan 2, 2021. It is now read-only.

Fix diagnostics update bug #959

Merged
merged 13 commits into from
Dec 21, 2020
Merged

Fix diagnostics update bug #959

merged 13 commits into from
Dec 21, 2020

Conversation

pepeiborra
Copy link
Collaborator

Given a FOI F with non null typechecking diagnostics D, imagine the following scenario:

  1. An edit notification for F is received, creating a new version
  2. GetModTime is executed, producing 0 diagnostics.
    2.1 updateFileDiagnostics is called
    2.2 setStageDiagnostics is called
    2.3 LSP.updateDiagnostics is called with a new version, resetting all the
    diagnostics for F
    2.4 newDiags=[] in updateFileDiagnostics, which is different from D (the last
    published diagnostics), which enqueues a new publishDiagnostics [] in the
    Debouncer
  3. An edit notification for F is received before typechecking has a chance to
    run which undoes the previous edit
  4. The debouncer publishes the empty set of diagnostics after waiting 0.1s
  5. GetFileContents runs and since the contents of the file haven't changed since
    the last time it ran, early cutoff skips everything donwstream

Since TypeCheck is skipped, the empty set of diagnostics stays published until
another edit comes. This is a bug.

The goal of this change is to prevent setStageDiagnostics from losing
diagnostics from other stages. To achieve this, we recover the old diagnostics
for all stages and merge them with the new stage.

Fixes #949

The test suite includes a new custom command that can be used to wait for arbitrary Shake actions, which can be used to implement the improvements outlined in #955

withMVar' is used to update the shakeSession var and it's crucial that the
third argument is not interrupted.

'mask' can still be interrupted for I/O actions and, while we were careful to
ensure none was used, if it ever breaks it will lead to very hard to debug
problems.
Since the contents of the buffer are not tracked by the fingerprint.
Given a FOI F with non null typechecking diagnostics D, imagine the following scenario:

1. An edit notification for F is received, creating a new version
2. GetModTime is executed, producing 0 diagnostics.
  2.1 updateFileDiagnostics is called
  2.2 setStageDiagnostics is called
  2.3 LSP.updateDiagnostics is called with a new version, resetting all the
  diagnostics for F
  2.4 newDiags=[] in updateFileDiagnostics, which is different from D (the last
  published diagnostics), which enqueues a new publishDiagnostics [] in the
  Debouncer
3. An edit notification for F is received before typechecking has a chance to
run which undoes the previous edit
4. The debouncer publishes the empty set of diagnostics after waiting 0.1s
5. GetFileContents runs and since the contents of the file haven't changed since
the last time it ran, early cutoff skips everything donwstream

Since TypeCheck is skipped, the empty set of diagnostics stays published until
another edit comes.

The goal of this change is to prevent setStageDiagnostics from losing
diagnostics from other stages. To achieve this, we recover the old diagnostics
for all stages and merge them with the new stage.
@wz1000
Copy link
Collaborator

wz1000 commented Dec 20, 2020

Looks good, must have been a tough one to crack!

@pepeiborra
Copy link
Collaborator Author

pepeiborra commented Dec 20, 2020

It was quite tricky, yeah. I have a vague memory of the diagnostics store semantics for updates causing trouble in the past. @cocreature @ndmitchell do you recall this? To summarise, the problem is that updating the diagnostics of file F for one source, say "GetFileContents", throws away all the F diagnostics if the version is newer than the version in the store.

Copy link
Member

@jneira jneira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for improve test infra on the way

@tomsmeding
Copy link

I have used this branch (well, aad22e6 to be precise) for an hour or two and it seems my problems reported in #949 are indeed fixed! Thanks a lot for the hard work!

@pepeiborra pepeiborra merged commit 0d4e3b9 into master Dec 21, 2020
@pepeiborra pepeiborra deleted the fix-cancellation-clean branch December 21, 2020 06:06
pepeiborra added a commit to haskell/haskell-language-server that referenced this pull request Dec 28, 2020
These tests were underspecified and broke with the recent improvements to ghcide
diagnostics in haskell/ghcide#959 and included in this
merge.

Fixed by waiting specifically for the typecheck diagnostics and by being less
prescriptive in the number and order of code actions
pepeiborra added a commit to haskell/haskell-language-server that referenced this pull request Dec 28, 2020
These tests were underspecified and broke with the recent improvements to ghcide
diagnostics in haskell/ghcide#959 and included in this
merge.

Fixed by waiting specifically for the typecheck diagnostics and by being less
prescriptive in the number and order of code actions
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Preventively switch to uninterruptible mask in withMVar'

withMVar' is used to update the shakeSession var and it's crucial that the
third argument is not interrupted.

'mask' can still be interrupted for I/O actions and, while we were careful to
ensure none was used, if it ever breaks it will lead to very hard to debug
problems.

* refactor: move to RuleTypes

* Add a TestRequest to wait for arbitrary ide actions

Closes haskell/ghcide#955

* expectCurrentDiagnostics

* Add a test suite for cancellation

* Introduce --test-no-kick to fix cancellation tests reliability

* delete unsafeClearDiagnostics (unused)

* GetModSummaryWithoutTimestamps - remove StringBuffer

Since the contents of the buffer are not tracked by the fingerprint.

* Fix diagnostics bug

Given a FOI F with non null typechecking diagnostics D, imagine the following scenario:

1. An edit notification for F is received, creating a new version
2. GetModTime is executed, producing 0 diagnostics.
  2.1 updateFileDiagnostics is called
  2.2 setStageDiagnostics is called
  2.3 LSP.updateDiagnostics is called with a new version, resetting all the
  diagnostics for F
  2.4 newDiags=[] in updateFileDiagnostics, which is different from D (the last
  published diagnostics), which enqueues a new publishDiagnostics [] in the
  Debouncer
3. An edit notification for F is received before typechecking has a chance to
run which undoes the previous edit
4. The debouncer publishes the empty set of diagnostics after waiting 0.1s
5. GetFileContents runs and since the contents of the file haven't changed since
the last time it ran, early cutoff skips everything donwstream

Since TypeCheck is skipped, the empty set of diagnostics stays published until
another edit comes.

The goal of this change is to prevent setStageDiagnostics from losing
diagnostics from other stages. To achieve this, we recover the old diagnostics
for all stages and merge them with the new stage.

* Fix hlint

* Use Map.insert for clarity

* Fix redundant imports

* Fix "code actions after edit" experiment"
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
These tests were underspecified and broke with the recent improvements to ghcide
diagnostics in haskell/ghcide#959 and included in this
merge.

Fixed by waiting specifically for the typecheck diagnostics and by being less
prescriptive in the number and order of code actions
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Preventively switch to uninterruptible mask in withMVar'

withMVar' is used to update the shakeSession var and it's crucial that the
third argument is not interrupted.

'mask' can still be interrupted for I/O actions and, while we were careful to
ensure none was used, if it ever breaks it will lead to very hard to debug
problems.

* refactor: move to RuleTypes

* Add a TestRequest to wait for arbitrary ide actions

Closes haskell/ghcide#955

* expectCurrentDiagnostics

* Add a test suite for cancellation

* Introduce --test-no-kick to fix cancellation tests reliability

* delete unsafeClearDiagnostics (unused)

* GetModSummaryWithoutTimestamps - remove StringBuffer

Since the contents of the buffer are not tracked by the fingerprint.

* Fix diagnostics bug

Given a FOI F with non null typechecking diagnostics D, imagine the following scenario:

1. An edit notification for F is received, creating a new version
2. GetModTime is executed, producing 0 diagnostics.
  2.1 updateFileDiagnostics is called
  2.2 setStageDiagnostics is called
  2.3 LSP.updateDiagnostics is called with a new version, resetting all the
  diagnostics for F
  2.4 newDiags=[] in updateFileDiagnostics, which is different from D (the last
  published diagnostics), which enqueues a new publishDiagnostics [] in the
  Debouncer
3. An edit notification for F is received before typechecking has a chance to
run which undoes the previous edit
4. The debouncer publishes the empty set of diagnostics after waiting 0.1s
5. GetFileContents runs and since the contents of the file haven't changed since
the last time it ran, early cutoff skips everything donwstream

Since TypeCheck is skipped, the empty set of diagnostics stays published until
another edit comes.

The goal of this change is to prevent setStageDiagnostics from losing
diagnostics from other stages. To achieve this, we recover the old diagnostics
for all stages and merge them with the new stage.

* Fix hlint

* Use Map.insert for clarity

* Fix redundant imports

* Fix "code actions after edit" experiment"
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
These tests were underspecified and broke with the recent improvements to ghcide
diagnostics in haskell/ghcide#959 and included in this
merge.

Fixed by waiting specifically for the typecheck diagnostics and by being less
prescriptive in the number and order of code actions
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
* Preventively switch to uninterruptible mask in withMVar'

withMVar' is used to update the shakeSession var and it's crucial that the
third argument is not interrupted.

'mask' can still be interrupted for I/O actions and, while we were careful to
ensure none was used, if it ever breaks it will lead to very hard to debug
problems.

* refactor: move to RuleTypes

* Add a TestRequest to wait for arbitrary ide actions

Closes haskell/ghcide#955

* expectCurrentDiagnostics

* Add a test suite for cancellation

* Introduce --test-no-kick to fix cancellation tests reliability

* delete unsafeClearDiagnostics (unused)

* GetModSummaryWithoutTimestamps - remove StringBuffer

Since the contents of the buffer are not tracked by the fingerprint.

* Fix diagnostics bug

Given a FOI F with non null typechecking diagnostics D, imagine the following scenario:

1. An edit notification for F is received, creating a new version
2. GetModTime is executed, producing 0 diagnostics.
  2.1 updateFileDiagnostics is called
  2.2 setStageDiagnostics is called
  2.3 LSP.updateDiagnostics is called with a new version, resetting all the
  diagnostics for F
  2.4 newDiags=[] in updateFileDiagnostics, which is different from D (the last
  published diagnostics), which enqueues a new publishDiagnostics [] in the
  Debouncer
3. An edit notification for F is received before typechecking has a chance to
run which undoes the previous edit
4. The debouncer publishes the empty set of diagnostics after waiting 0.1s
5. GetFileContents runs and since the contents of the file haven't changed since
the last time it ran, early cutoff skips everything donwstream

Since TypeCheck is skipped, the empty set of diagnostics stays published until
another edit comes.

The goal of this change is to prevent setStageDiagnostics from losing
diagnostics from other stages. To achieve this, we recover the old diagnostics
for all stages and merge them with the new stage.

* Fix hlint

* Use Map.insert for clarity

* Fix redundant imports

* Fix "code actions after edit" experiment"
pepeiborra added a commit to pepeiborra/ide that referenced this pull request Dec 29, 2020
These tests were underspecified and broke with the recent improvements to ghcide
diagnostics in haskell/ghcide#959 and included in this
merge.

Fixed by waiting specifically for the typecheck diagnostics and by being less
prescriptive in the number and order of code actions
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sending LSP messages very quickly causes all diagnostics to disappear
4 participants