Skip to content

Commit

Permalink
Auto merge of rust-lang#13871 - lowr:fix/extract-module-merge-multipl…
Browse files Browse the repository at this point in the history
…e-ranges, r=lnicola

fix: merge multiple intersecting ranges

Fixes rust-lang#13791

In `check_intersection_and_push()`, there may exist two ranges we should merge with the new one. We've been assuming there should be only one range that intersects, which lead to [this assertion](https://github.com/rust-lang/rust-analyzer/blob/da15d92a3204da419bad70cbfceb2676bfe0b528/crates/text-edit/src/lib.rs#L192) to fail under specific circumstances.
  • Loading branch information
bors committed Dec 31, 2022
2 parents da15d92 + 332dd6a commit f31733b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ide-assists/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cov-mark = "2.0.0-pre.1"

itertools = "0.10.5"
either = "1.7.0"
smallvec = "1.10.0"

stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" }
Expand Down
82 changes: 62 additions & 20 deletions crates/ide-assists/src/handlers/extract_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use ide_db::{
defs::{Definition, NameClass, NameRefClass},
search::{FileReference, SearchScope},
};
use itertools::Itertools;
use smallvec::SmallVec;
use stdx::format_to;
use syntax::{
algo::find_node_at_range,
Expand Down Expand Up @@ -657,28 +659,23 @@ impl Module {

fn check_intersection_and_push(
import_paths_to_be_removed: &mut Vec<TextRange>,
import_path: TextRange,
mut import_path: TextRange,
) {
if import_paths_to_be_removed.len() > 0 {
// Text ranges received here for imports are extended to the
// next/previous comma which can cause intersections among them
// and later deletion of these can cause panics similar
// to reported in #11766. So to mitigate it, we
// check for intersection between all current members
// and if it exists we combine both text ranges into
// one
let r = import_paths_to_be_removed
.into_iter()
.position(|it| it.intersect(import_path).is_some());
match r {
Some(it) => {
import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
}
None => import_paths_to_be_removed.push(import_path),
}
} else {
import_paths_to_be_removed.push(import_path);
// Text ranges received here for imports are extended to the
// next/previous comma which can cause intersections among them
// and later deletion of these can cause panics similar
// to reported in #11766. So to mitigate it, we
// check for intersection between all current members
// and combine all such ranges into one.
let s: SmallVec<[_; 2]> = import_paths_to_be_removed
.into_iter()
.positions(|it| it.intersect(import_path).is_some())
.collect();
for pos in s.into_iter().rev() {
let intersecting_path = import_paths_to_be_removed.swap_remove(pos);
import_path = import_path.cover(intersecting_path);
}
import_paths_to_be_removed.push(import_path);
}

fn does_source_exists_outside_sel_in_same_mod(
Expand Down Expand Up @@ -1766,4 +1763,49 @@ mod modname {
",
)
}

#[test]
fn test_merge_multiple_intersections() {
check_assist(
extract_module,
r#"
mod dep {
pub struct A;
pub struct B;
pub struct C;
}
use dep::{A, B, C};
$0struct S {
inner: A,
state: C,
condvar: B,
}$0
"#,
r#"
mod dep {
pub struct A;
pub struct B;
pub struct C;
}
use dep::{};
mod modname {
use super::dep::B;
use super::dep::C;
use super::dep::A;
pub(crate) struct S {
pub(crate) inner: A,
pub(crate) state: C,
pub(crate) condvar: B,
}
}
"#,
);
}
}

0 comments on commit f31733b

Please sign in to comment.