Skip to content

Commit f31733b

Browse files
committed
Auto merge of rust-lang#13871 - lowr:fix/extract-module-merge-multiple-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.
2 parents da15d92 + 332dd6a commit f31733b

File tree

3 files changed

+64
-20
lines changed

3 files changed

+64
-20
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ide-assists/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ cov-mark = "2.0.0-pre.1"
1414

1515
itertools = "0.10.5"
1616
either = "1.7.0"
17+
smallvec = "1.10.0"
1718

1819
stdx = { path = "../stdx", version = "0.0.0" }
1920
syntax = { path = "../syntax", version = "0.0.0" }

crates/ide-assists/src/handlers/extract_module.rs

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use ide_db::{
1010
defs::{Definition, NameClass, NameRefClass},
1111
search::{FileReference, SearchScope},
1212
};
13+
use itertools::Itertools;
14+
use smallvec::SmallVec;
1315
use stdx::format_to;
1416
use syntax::{
1517
algo::find_node_at_range,
@@ -657,28 +659,23 @@ impl Module {
657659

658660
fn check_intersection_and_push(
659661
import_paths_to_be_removed: &mut Vec<TextRange>,
660-
import_path: TextRange,
662+
mut import_path: TextRange,
661663
) {
662-
if import_paths_to_be_removed.len() > 0 {
663-
// Text ranges received here for imports are extended to the
664-
// next/previous comma which can cause intersections among them
665-
// and later deletion of these can cause panics similar
666-
// to reported in #11766. So to mitigate it, we
667-
// check for intersection between all current members
668-
// and if it exists we combine both text ranges into
669-
// one
670-
let r = import_paths_to_be_removed
671-
.into_iter()
672-
.position(|it| it.intersect(import_path).is_some());
673-
match r {
674-
Some(it) => {
675-
import_paths_to_be_removed[it] = import_paths_to_be_removed[it].cover(import_path)
676-
}
677-
None => import_paths_to_be_removed.push(import_path),
678-
}
679-
} else {
680-
import_paths_to_be_removed.push(import_path);
664+
// Text ranges received here for imports are extended to the
665+
// next/previous comma which can cause intersections among them
666+
// and later deletion of these can cause panics similar
667+
// to reported in #11766. So to mitigate it, we
668+
// check for intersection between all current members
669+
// and combine all such ranges into one.
670+
let s: SmallVec<[_; 2]> = import_paths_to_be_removed
671+
.into_iter()
672+
.positions(|it| it.intersect(import_path).is_some())
673+
.collect();
674+
for pos in s.into_iter().rev() {
675+
let intersecting_path = import_paths_to_be_removed.swap_remove(pos);
676+
import_path = import_path.cover(intersecting_path);
681677
}
678+
import_paths_to_be_removed.push(import_path);
682679
}
683680

684681
fn does_source_exists_outside_sel_in_same_mod(
@@ -1766,4 +1763,49 @@ mod modname {
17661763
",
17671764
)
17681765
}
1766+
1767+
#[test]
1768+
fn test_merge_multiple_intersections() {
1769+
check_assist(
1770+
extract_module,
1771+
r#"
1772+
mod dep {
1773+
pub struct A;
1774+
pub struct B;
1775+
pub struct C;
1776+
}
1777+
1778+
use dep::{A, B, C};
1779+
1780+
$0struct S {
1781+
inner: A,
1782+
state: C,
1783+
condvar: B,
1784+
}$0
1785+
"#,
1786+
r#"
1787+
mod dep {
1788+
pub struct A;
1789+
pub struct B;
1790+
pub struct C;
1791+
}
1792+
1793+
use dep::{};
1794+
1795+
mod modname {
1796+
use super::dep::B;
1797+
1798+
use super::dep::C;
1799+
1800+
use super::dep::A;
1801+
1802+
pub(crate) struct S {
1803+
pub(crate) inner: A,
1804+
pub(crate) state: C,
1805+
pub(crate) condvar: B,
1806+
}
1807+
}
1808+
"#,
1809+
);
1810+
}
17691811
}

0 commit comments

Comments
 (0)