rust-lang / rust-analyzer

A Rust compiler front-end for IDEs
https://rust-analyzer.github.io/
Apache License 2.0
14.39k stars 1.62k forks source link

Structural search & replace of nested `try!()` causes corruption #11591

Open deviant opened 2 years ago

deviant commented 2 years ago

rust-analyzer version: 2022-01-31 rustc version: 1.58.1 relevant settings: N/A


Given the following code:

// abcdefghijklmnopqrstuvwxyz
fn main() {
    try!(try!("what"));
}

running

rust-analyzer ssr 'try!($expr) ==>> $expr?'

produces the following absurd result:

// abcdefghijklmnopqrstuvwxyz
fn main() {
    ijklmn??;
}

I have observed the following properties:

This implies the replacement data is being taken from a location relative to the start of the file, rather than relative to the match itself. I have no idea why this is, and am not sufficiently familiar with rust-analyzer's internals to investigate it further, but knowing these things will probably help with tracking down the problem.

deviant commented 2 years ago

I also had a crash, when running this command over a checkout of https://github.com/git-series/git-series/tree/0.9.1:

thread 'main' panicked at 'assertion failed: check_disjoint_and_sort(indels)', crates/text_edit/src/lib.rs:195:5
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic
   3: text_edit::TextEditBuilder::replace
   4: ide_ssr::replacing::matches_to_edit_at_offset
   5: ide_ssr::replacing::ReplacementRenderer::render_token
   6: ide_ssr::replacing::ReplacementRenderer::render_node
   7: ide_ssr::replacing::ReplacementRenderer::render_node
   8: ide_ssr::replacing::ReplacementRenderer::render_node
   9: ide_ssr::replacing::ReplacementRenderer::render_node
  10: ide_ssr::replacing::ReplacementRenderer::render_node
  11: ide_ssr::replacing::matches_to_edit_at_offset
  12: <core::iter::adapters::map::Map<I,F> as core::iter::traits::iterator::Iterator>::fold
  13: <std::collections::hash::map::HashMap<K,V,S> as core::iter::traits::collect::FromIterator<(K,V)>>::from_iter
  14: ide_ssr::MatchFinder::edits
  15: rust_analyzer::cli::ssr::<impl rust_analyzer::cli::flags::Ssr>::run
  16: rust_analyzer::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

This may or may not be related, but seems likely to be relevant.

deviant commented 2 years ago

If anyone else runs into this, I managed to work around it:

  1. Create an empty fn dummy() (with the appropriate type signature).
  2. Use textual search & replace to convert all instances of try! into dummy.
  3. Use structural search & replace to convert dummy($expr) ==>> $expr?.

I haven't tested whether the bug generalizes to all macros or just affects try!(), but that's probably something worth checking out.