Diagnostics use a stale parse_result when the request is read before its didChange is applied
Les mainteneurs répondent en général sous 1 jour
Évaluation
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Accessibilité débutants
- 52/100
Piste de recherche
Start with BaseServer#start, the worker's push_edits path, and the diagnostic request handling; compare when needs_parsing is set with when document.parse_result is refreshed. Check the RuboCop add-on's RuntimeAdapter#prism_result and Requests::Diagnostics for both reported effects. Done means diagnostics and syntax warnings use a parse result matching the updated document source in the reproduction.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
Ruby LSP Information
- ruby-lsp 0.26.11 (code in question is unchanged on
main) - rubocop 1.87.0 (also checked 1.88.0
RuntimeAdapter#prism_result, unchanged), prism 1.9.0 - Ruby 4.0.6 via rbenv, Linux
- Neovim 0.11.6, built-in LSP client (pull diagnostics)
initializationOptions: { linters: ["rubocop"] }(RuboCop's own add-on)
Description
Diagnostics are intermittently computed with a parse_result from the previous document version while document.source is already the new version. With RuboCop's add-on, which reuses document.parse_result as prism_result, this produces wrong offenses that stay until the next edit. Syntax warnings from Requests::Diagnostics read the same stale tree.
Order of events, as I read it:
- Client sends
didChangeand immediatelytextDocument/diagnostic(Neovim pulls diagnostics on everydidChange). - The main thread (
BaseServer#start) only callsdocument.parse!for non-did*messages and queuesdidChangewithout applying it. When the diagnostic request arrives, the edit isn't applied yet,needs_parsingisfalse, so nothing is parsed. - The worker applies the edits (
push_editssetsneeds_parsing = true), then handles the diagnostic request without parsing.
Server-side log from a stale run (a small workspace add-on that logs at enqueue, worker start and in RuntimeAdapter#run_diagnostic):
reader enqueue textDocument/didChange doc v0 needs_parsing=false tree=current
worker start textDocument/didChange doc v0 needs_parsing=false tree=current
reader enqueue textDocument/diagnostic doc v0 needs_parsing=false tree=current
worker push_edits v15 start
worker push_edits v15 done src="# frozen_string_literal: true\n\ndef foo\n y = \"abc\"\n x = 1\n x\nend\n"
worker start textDocument/diagnostic doc v15 needs_parsing=true tree=STALE
adapter run_diagnostic v15 source==parsed_source? false
source: "# frozen_string_literal: true\n\ndef foo\n y = \"abc\"\n x = 1\n x\nend\n"
parse_result: "# frozen_string_literal: true\n\ndef foo\n x = 1\n x\nend\n"
Resulting diagnostics vs. rubocop --stdin on the same text:
client: 4:3 Style/RedundantAssignment | 4:9 Layout/ExtraSpacing
rubocop: 4:3 Lint/UselessAssignment | 4:7 Style/StringLiterals | 5:3 Style/RedundantAssignment
In a later stale run the parser warning assigned but unused variable - y remained after y had been deleted, so this isn't specific to the RuboCop add-on.
Reproduction steps
Project: Gemfile with gem "rubocop", a .rubocop.yml with AllCops: { NewCops: enable }, and app.rb:
# frozen_string_literal: true
def foo
x = 1
x
end
Minimal Neovim config (nvim --clean -u init.lua app.rb):
vim.lsp.config("ruby_lsp", {
cmd = { "rbenv", "exec", "ruby-lsp" },
filetypes = { "ruby" },
root_markers = { "Gemfile" },
init_options = { linters = { "rubocop" } },
})
vim.lsp.enable("ruby_lsp")
- Open
app.rband wait for diagnostics. - Insert a new line
y = "abc"afterdef foo. I typed it vianvim_buf_set_textone character at a time, 40ms apart; the client sends it as a singledidChange. - See diagnostics that don't match the buffer.
Results (headless, 6 runs each):
| setting | stale runs |
|---|---|
linters: ["rubocop"] |
6/6 |
linters: ["rubocop_internal"] |
0/6 |
no linters (detects rubocop_internal) |
0/6 |
["rubocop"] in my full config, with a 300ms delay before sending the diagnostic request |
0/5 (vs 2/5 without the delay in that batch) |
rubocop_internal is unaffected because RuboCopRunner#run doesn't pass prism_result through (the assignment is commented out) and RuboCop parses contents itself.
Possible fix direction: call document.parse! in the worker before handling a request, or parse after push_edits, so a request never sees a tree older than its source. I haven't checked whether VS Code hits this; its pull timing may hide it.
- Langage dominant
- Ruby
- Étoiles
- 2k
- Forks
- 286
- Merge moyen
- 8 h 37 min
- PR mergées (30 j)
- 12
Préparer son environnement
Lance le conteneur de développement du projet dans votre navigateur, avec votre propre compte GitHub.
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de Shopify/ruby-lsp
-
Dependencies view: `getParent` loops forever on untitled documents, extension host runs out of memoryPeut-être pris @efegokdemir l’a pris il y a 14 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
Les mainteneurs répondent en général sous 1 jour
-
Linux file system with square brackets in the directory/filename are failing to be indexedPeut-être pris @andriytyurnikov l’a pris il y a 218 jours. Ouvertebug help-wanted pinned
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
Shopify/ruby-lsp#3503 · 8 commentaires · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
rbenv activation leaks RBENV_VERSION and RBENV_DIR into process.env of the extension hostPeut-être pris @efegokdemir l’a pris il y a 9 jours. Ouvertebug
Difficulté 3/5 Une demi-journée Accessibilité débutants 45/100
Les mainteneurs répondent en général sous 1 jour
-
Rename of a class moves its file and leaves require_relative on the old pathPeut-être pris @efegokdemir l’a pris il y a 15 jours. Ouverte
Difficulté 4/5 3-5 jours Accessibilité débutants 22/100
Les mainteneurs répondent en général sous 1 jour
-
Paths need to be escapedPeut-être pris @Vraiment l’a pris il y a 19 jours. Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 25/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de Shopify/ruby-lsp
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
Les mainteneurs répondent en général sous 1 jour
-
[Bug] Chatwoot v4.19.0 – Super Admin returns 500 due to missing superadmin.scss Vite manifest entryOuverteBug
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
Les mainteneurs répondent en général sous 2 jours
-
PRM baseline System 1 and 2 fans run continuously in unoccupied hours (low priority)Peut-être pris @anchapin l’a pris aujourd’hui. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 70/100
NatLabRockies/openstudio-standards#2167 ·
Les mainteneurs répondent en général sous 1 jour
-
Phone validation rejects valid numbers in new NANP area code 738 (phonelib 0.10.15 outdated)Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 76/100
18F/identity-idp#13734 · 1 commentaire ·
Les mainteneurs répondent en général sous 1 jour
-
Ubuntu 26.04: gems linking OpenSSL statically (e.g. rugged) fail to build – libjitterentropy3-dev missingPeut-être pris @BattleAurora l’a pris aujourd’hui. OuverteArea: Ruby bug report needs triage OS: Ubuntu
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
actions/runner-images#15068 · 1 commentaire ·
Les mainteneurs répondent en général sous 3 jours