Skip to content

Commit 43253c5

Browse files
bors[bot]Veykril
andauthored
Merge #6102
6102: Fix MergingBehaviour::Last creating unintuitive import trees r=jonas-schievink a=Veykril The way this behaviour currently works is actually a bit weird. Imagine the following three imports get requested for insertion in the given order: - `winapi::um::d3d11::ID3D11Device` - `winapi::shared::dxgiformat::DXGI_FORMAT` - `winapi::um::d3d11::D3D11_FILTER` After the first two you will have the following tree: ```rust use winapi::{shared::dxgiformat::DXGI_FORMAT, um::d3d11::ID3D11Device}; ``` which is to be expected as they arent nested this kind of merging is allowed, but now importing the third one will result in: ```rust use winapi::{shared::dxgiformat::DXGI_FORMAT, um::d3d11::ID3D11Device, um::d3d11::D3D11_FILTER}; ``` which is still fine according to the rules, but it looks weird(at least in my eyes) due to the long paths that are quite similar. The changes in this PR will change the criteria for when to reject `Last` merging, it still disallows multiple nesting but it also only allows single segment paths inside of the `UseTreeList`. With this change you get the following tree after the first two imports: ```rust use winapi::um::d3d11::ID3D11Device; use winapi::shared::dxgiformat::DXGI_FORMAT; ``` and after the third: ```rust use winapi::shared::dxgiformat::DXGI_FORMAT; use winapi::um::d3d11::{ID3D11Device, D3D11_FILTER}; ``` Which I believe looks more like what you would expect. Co-authored-by: Lukas Wirth <[email protected]>
2 parents 7c9ae77 + 0671bf2 commit 43253c5

File tree

1 file changed

+52
-21
lines changed

1 file changed

+52
-21
lines changed

crates/assists/src/utils/insert_use.rs

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -174,28 +174,30 @@ pub(crate) fn try_merge_trees(
174174
let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?;
175175
let lhs = lhs.split_prefix(&lhs_prefix);
176176
let rhs = rhs.split_prefix(&rhs_prefix);
177-
recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged)
177+
recursive_merge(&lhs, &rhs, merge)
178178
}
179179

180180
/// Recursively "zips" together lhs and rhs.
181181
fn recursive_merge(
182182
lhs: &ast::UseTree,
183183
rhs: &ast::UseTree,
184184
merge: MergeBehaviour,
185-
) -> Option<(ast::UseTree, bool)> {
185+
) -> Option<ast::UseTree> {
186186
let mut use_trees = lhs
187187
.use_tree_list()
188188
.into_iter()
189189
.flat_map(|list| list.use_trees())
190-
// check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this
191-
// so early exit the iterator by using Option's Intoiterator impl
192-
.map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() {
193-
true => None,
194-
false => Some(tree),
190+
// we use Option here to early return from this function(this is not the same as a `filter` op)
191+
.map(|tree| match merge.is_tree_allowed(&tree) {
192+
true => Some(tree),
193+
false => None,
195194
})
196195
.collect::<Option<Vec<_>>>()?;
197196
use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path()));
198197
for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) {
198+
if !merge.is_tree_allowed(&rhs_t) {
199+
return None;
200+
}
199201
let rhs_path = rhs_t.path();
200202
match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) {
201203
Ok(idx) => {
@@ -239,17 +241,9 @@ fn recursive_merge(
239241
}
240242
let lhs = lhs_t.split_prefix(&lhs_prefix);
241243
let rhs = rhs_t.split_prefix(&rhs_prefix);
242-
let this_has_children = use_trees.len() > 0;
243244
match recursive_merge(&lhs, &rhs, merge) {
244-
Some((_, has_multiple_children))
245-
if merge == MergeBehaviour::Last
246-
&& this_has_children
247-
&& has_multiple_children =>
248-
{
249-
return None
250-
}
251-
Some((use_tree, _)) => use_trees[idx] = use_tree,
252-
None => use_trees.insert(idx, rhs_t),
245+
Some(use_tree) => use_trees[idx] = use_tree,
246+
None => return None,
253247
}
254248
}
255249
Err(_)
@@ -264,8 +258,7 @@ fn recursive_merge(
264258
}
265259
}
266260
}
267-
let has_multiple_children = use_trees.len() > 1;
268-
Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children))
261+
Some(lhs.with_use_tree_list(make::use_tree_list(use_trees)))
269262
}
270263

271264
/// Traverses both paths until they differ, returning the common prefix of both.
@@ -308,6 +301,10 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl
308301
successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment()))
309302
}
310303

304+
fn path_len(path: ast::Path) -> usize {
305+
segment_iter(&path).count()
306+
}
307+
311308
/// Orders paths in the following way:
312309
/// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers
313310
// FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has
@@ -352,6 +349,19 @@ pub enum MergeBehaviour {
352349
Last,
353350
}
354351

352+
impl MergeBehaviour {
353+
#[inline]
354+
fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool {
355+
match self {
356+
MergeBehaviour::Full => true,
357+
// only simple single segment paths are allowed
358+
MergeBehaviour::Last => {
359+
tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
360+
}
361+
}
362+
}
363+
}
364+
355365
#[derive(Eq, PartialEq, PartialOrd, Ord)]
356366
enum ImportGroup {
357367
// the order here defines the order of new group inserts
@@ -675,6 +685,11 @@ use std::io;",
675685
)
676686
}
677687

688+
#[test]
689+
fn merge_last_into_self() {
690+
check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
691+
}
692+
678693
#[test]
679694
fn merge_groups_full() {
680695
check_full(
@@ -819,8 +834,24 @@ use std::io;",
819834
}
820835

821836
#[test]
822-
fn merge_last_too_long() {
823-
check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};");
837+
fn skip_merge_last_too_long() {
838+
check_last(
839+
"foo::bar",
840+
r"use foo::bar::baz::Qux;",
841+
r"use foo::bar;
842+
use foo::bar::baz::Qux;",
843+
);
844+
}
845+
846+
#[test]
847+
#[ignore] // FIXME: Order changes when switching lhs and rhs
848+
fn skip_merge_last_too_long2() {
849+
check_last(
850+
"foo::bar::baz::Qux",
851+
r"use foo::bar;",
852+
r"use foo::bar;
853+
use foo::bar::baz::Qux;",
854+
);
824855
}
825856

826857
#[test]

0 commit comments

Comments
 (0)