Skip to content

Panics with 'index out of bounds' at crates/ide_db/src/line_index.rs:106 in neovim #11081

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dimbleby opened this issue Dec 21, 2021 · 8 comments
Labels
Broken Window Bugs / technical debt to be addressed immediately

Comments

@dimbleby
Copy link
Contributor

rust-analyzer version: (eg. output of "Rust Analyzer: Show RA Version" command)

rust-analyzer 0add6e9 2021-12-20 stable

rustc version: (eg. output of rustc -V)

rustc 1.57.0


I frequently have to restart rust-analyzer because it has panics like this:

[ERROR][2021-12-15 22:59:08] .../vim/lsp/rpc.lua:417>   "rpc">  "rust-analyzer">"stderr">   "Panic context:\n> \nversion: 7d6fcbc0b 2021-12-06 stable\nnotification: textDocument/didChange\n\nthread 'main' panicked at 'index out of bounds: the len is 146 but the index is 146', crates/ide_db/src/line_index.rs:106:9\nstack backtrace:\n"
[ERROR][2021-12-15 22:59:08] .../vim/lsp/rpc.lua:417>   "rpc">  "rust-analyzer">"stderr">   "   0: rust_begin_unwind\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5\n   1: core::panicking::panic_fmt\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14\n  "
[ERROR][2021-12-15 22:59:08] .../vim/lsp/rpc.lua:417>   "rpc">  "rust-analyzer">"stderr">   " 2: core::panicking::panic_bounds_check\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:76:5\n   3: rust_analyzer::from_proto::text_range\n   4: rust_analyzer::lsp_utils::apply_document_changes\n   5: core::ops::function::FnOnce::call_once\n   6: rust_analyzer::dispatch::NotificationDispatcher::on\n   7: rust_analyzer::main_loop::<impl rust_analyzer::global_state::GlobalState>::handle_event\n   8: rust_analyzer::main_loop::<impl rust_analyzer::global_state::GlobalState>::run\n   9: rust_analyzer::main_loop::main_loop\n  10: rust_analyzer::try_main\n  11: rust_analyzer::main\nnote: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\nthread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: \"SendError(..)\"', /home/runner/.cargo/registry/src/github.com.-1ecc6299db9ec823/lsp-server-0.5.2/src/stdio.rs:29:37\nstack backtrace:\n   0: rust_begin_unwind\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5\n   1: core::panicking::panic_fmt\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14\n   2: core::result::unwrap_failed\n             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/result.rs:1616:5\nnote: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n"

I can reliably provoke this in neovim as follows:

  • create a file with a mistake in it, eg like this (note the extra closing bracket)
  fn hello() {
     println!("Hello"));
  }
  • run a bit of vimscript that tries to format this, spots the failure, and immediately undoes that format. Here's a simplified version of my formatting code that is rust-specific and sufficient to reproduce the problem:
function! format#FormatFile() abort
  let tmpfile = tempname()
  let shellredir_save = &shellredir
  let &shellredir = '>%s 2>'.tmpfile
  silent execute '%!rustfmt --edition 2018 --color never --emit stdout'
  let &shellredir = shellredir_save
  if v:shell_error != 0
    silent undo
  endif
  call delete(tmpfile)
endfunction

Similar to #10138, but reporting separately because (i) it looks like a different stack and (ii) I have a clear repro.

@Veykril
Copy link
Member

Veykril commented Jan 31, 2022

Panic workaround implemented in #11182

@Veykril Veykril closed this as completed Jan 31, 2022
bors bot added a commit that referenced this issue Jan 31, 2022
11182: fix: don't panic on seeing an unexpected offset r=Veykril a=dimbleby

Intended as a fix, or at least a sticking plaster, for #11081.

I have arranged that [offset()](https://github.com./rust-analyzer/rust-analyzer/blob/1ba9a924d7b161c52e605e157ee16d582e4a8684/crates/ide_db/src/line_index.rs#L105-L107) returns `Option<TextSize>` instead of going out of bounds; other changes are the result of following the compiler after doing this.

Perhaps there's still an issue here - I suppose the server and client have gotten out of sync and that probably shouldn't happen in the first place?  I see that #10138 (comment) suggests what sounds like a more substantial fix which I think might be aimed in this direction.  So perhaps that one should be left open to cover such things?

Meanwhile, I hope that not-crashing is a good improvement: and I can confirm that it works out just fine in the repro I have at #11081.

Co-authored-by: David Hotham <[email protected]>
@matklad matklad reopened this Mar 4, 2022
@matklad
Copy link
Member

matklad commented Mar 4, 2022

Let's reopen this, as it sounds like something serious which we should fix!

To give a broad context here, in general rust-analyzer is carefully architectured such that panics do not crash the process or corrupt state. Panics happen fairly often, but they should just display an annoying message, and not require restarting of the process.

So, what we are seeing here, a panic bringing down the whole process, is a very serious issue. The problem isn't the panic per se, but rather the fact that the panic compromizes user experience.

According to the log, the error happens in notification: textDocument/didChange. This is a "write" path where we change the internal state (to account for user's typing), so, indeed, panic here is rather catastrophic -- if we recover here, we'll get state divergence between the client and the server, and that's bad.

I have two hypothesis here:

  • There's a bug in VIM LSP client, and it sends us invalid change notifications
  • We have some stupid off-by-one error at the boundary (the len is 146 but the index is 146')

@dimbleby Am I correct that you are using neovim's built-in client?

@matklad matklad added the Broken Window Bugs / technical debt to be addressed immediately label Mar 4, 2022
@dimbleby
Copy link
Contributor Author

dimbleby commented Mar 4, 2022

Am I correct that you are using neovim's built-in client?

yes

@matklad
Copy link
Member

matklad commented Mar 4, 2022

Got the LSP log:

[DEBUG][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:347	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          end = {            character = 0,            line = 3          },          start = {            character = 0,            line = 0          }        },        rangeLength = 46,        text = ""      } },    textDocument = {      uri = "file:///home/matklad/tmp/r/src/main.rs",      version = 4    }  }}
[DEBUG][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:347	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          end = {            character = 0,            line = 0          },          start = {            character = 0,            line = 0          }        },        rangeLength = 0,        text = 'fn main() {\n    println!("Hello, world!"));\n}'      } },    textDocument = {      uri = "file:///home/matklad/tmp/r/src/main.rs",      version = 5    }  }}
[DEBUG][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:347	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          end = {            character = 0,            line = 3          },          start = {            character = 0,            line = 3          }        },        rangeLength = 0,        text = ""      } },    textDocument = {      uri = "file:///home/matklad/tmp/r/src/main.rs",      version = 6    }  }}
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	"Panic context:\n> \nversion: f8329ba98 2022-03-04 dev\nnotification: textDocument/didChange\n\nthread 'main' panicked at 'Invalid line: 3, total lines: 3', crates/ide_db/src/line_index.rs:109:32\nstack backtrace:\n"
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	"   0: rust_begin_unwind\n             at /rustc/0a4f984a87c7ba6c74ec3e78442fec955a419e32/library/std/src/panicking.rs:584:5\n   1: core::panicking::"
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	"panic_fmt\n             at /rustc/0a4f984a87c7ba6c74ec3e78442fec955a419e32/library/core/src/panicking.rs:143:14\n   2: ide_db::line_index::LineIndex::offset\n   3: rust_analyzer::from_proto::text_range\n   4: rust_analyzer::lsp_utils::apply_document_changes\n   5: core::ops::function::FnOnce::call_once\n"
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	"   6: rust_analyzer::dispatch::NotificationDispatcher::on\n   7: rust_analyzer::main_loop::<impl rust_analyzer::global_state::GlobalState>::handle_event\n   8: rust_analyzer::main_loop::<impl rust_analyzer"
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	"::global_state::GlobalState>::run\n   9: rust_analyzer::main_loop::main_loop\n  10: rust_analyzer::run_server\n "
[ERROR][2022-03-04 16:26:08] .../vim/lsp/rpc.lua:420	"rpc"	"rust-analyzer"	"stderr"	" 11: rust_analyzer::main\nnote: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n"

I think it's pretty clear what the problem, these two messages are to blame (reformatted and lightly edited for clarity):

{ method = "textDocument/didChange",  params = {
  contentChanges = {{
    range = { end = { character = 0, line = 0 }, start = { character = 0, line = 0 } },
    rangeLength = 0,
    text = 'fn main() {\n    println!("Hello, world!"));\n}'
  }}, textDocument = { uri = "main.rs", version = 5 }}}

{ method = "textDocument/didChange",  params = {
  contentChanges = {{
    range = { end = { character = 0, line = 3 }, start = { character = 0, line = 3 } },
    rangeLength = 0,
    text = ""
  }}, textDocument = { uri = "main.rs", version = 6 }}}

Note how the second message refers to the third line, but, given the text in the previous message, there are only two lines. That is, this seems to be neovim bug. I have a vague feeling that I've seen this already somewhere (specifically, the problem with trailing newline), but I am can't find that now.

@jonas-schievink
Copy link
Contributor

Given that the panic has an implemented workaround, and this is not our bug, removing the Broken Window label

@jonas-schievink jonas-schievink removed the Broken Window Bugs / technical debt to be addressed immediately label Apr 22, 2022
@jonas-schievink jonas-schievink changed the title Panics with 'index out of bounds' at crates/ide_db/src/line_index.rs:106 Panics with 'index out of bounds' at crates/ide_db/src/line_index.rs:106 in neovim Apr 22, 2022
@matklad
Copy link
Member

matklad commented Apr 22, 2022 via email

@federico-terzi
Copy link

federico-terzi commented Jul 2, 2022

Hey @matklad ,
First of all, rust-analyzer is beyond amazing and I highly appreciate your work!

Are there any updates on this issue? Unfortunately, this problem makes rust-analyzer unusable for me on the recent VSCode extension versions...

Edit: just noticed the VSCode extension has migrated to another page (it wasn't showing further updates). Removing the previous one and re-installing the new one works as expected, sorry for bothering you :)

@Veykril Veykril added the Broken Window Bugs / technical debt to be addressed immediately label Nov 7, 2022
@Veykril
Copy link
Member

Veykril commented Nov 8, 2022

We seem to already be sending an error message back in these cases now (Bad Offset), so I think we can close this

@Veykril Veykril closed this as completed Nov 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Broken Window Bugs / technical debt to be addressed immediately
Projects
None yet
Development

No branches or pull requests

5 participants