Skip to content

Commit 256a605

Browse files
committed
Implement One option for imports_granularity (rust-lang#4669)
This option merges all imports into a single `use` statement as long as they have the same visibility.
1 parent 5762933 commit 256a605

File tree

6 files changed

+171
-3
lines changed

6 files changed

+171
-3
lines changed

Configurations.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1710,7 +1710,7 @@ pub enum Foo {}
17101710
Merge together related imports based on their paths.
17111711

17121712
- **Default value**: `Preserve`
1713-
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`
1713+
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One`
17141714
- **Stable**: No
17151715

17161716
#### `Preserve` (default):
@@ -1764,6 +1764,23 @@ use qux::h;
17641764
use qux::i;
17651765
```
17661766

1767+
#### `One`:
1768+
1769+
Merge all imports into a single `use` statement if they have the same visibility.
1770+
1771+
```rust
1772+
pub use foo::{x, y};
1773+
use {
1774+
bar::{
1775+
a, b,
1776+
b::{f, g},
1777+
c,
1778+
d::e,
1779+
},
1780+
qux::{h, i},
1781+
};
1782+
```
1783+
17671784
## `merge_imports`
17681785

17691786
This option is deprecated. Use `imports_granularity = "Crate"` instead.

src/config/options.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ pub enum ImportGranularity {
130130
Module,
131131
/// Use one `use` statement per imported item.
132132
Item,
133+
/// Use one `use` statement including all items.
134+
One,
133135
}
134136

135137
#[config_type]

src/formatting/imports.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ impl UseSegment {
162162

163163
pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -> Vec<UseTree> {
164164
let mut result = Vec::with_capacity(use_trees.len());
165+
166+
// If `merge_by` is `SharedPrefix::One`, we firstly compute the result as if it's
167+
// `SharedPrefix::Crate`, and then merge the result one more time.
168+
let effective_merge_by = if merge_by == SharedPrefix::One {
169+
SharedPrefix::Crate
170+
} else {
171+
merge_by
172+
};
173+
165174
for use_tree in use_trees {
166175
if use_tree.has_comment() || use_tree.attrs.is_some() {
167176
result.push(use_tree);
@@ -171,14 +180,19 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -
171180
for flattened in use_tree.flatten() {
172181
if let Some(tree) = result
173182
.iter_mut()
174-
.find(|tree| tree.share_prefix(&flattened, merge_by))
183+
.find(|tree| tree.share_prefix(&flattened, effective_merge_by))
175184
{
176-
tree.merge(&flattened, merge_by);
185+
tree.merge(&flattened, effective_merge_by);
177186
} else {
178187
result.push(flattened);
179188
}
180189
}
181190
}
191+
192+
if merge_by == SharedPrefix::One {
193+
result = merge_use_trees_into_one(result);
194+
}
195+
182196
result
183197
}
184198

@@ -560,6 +574,7 @@ impl UseTree {
560574
SharedPrefix::Module => {
561575
self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
562576
}
577+
SharedPrefix::One => unreachable!(),
563578
}
564579
}
565580
}
@@ -673,6 +688,39 @@ fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by:
673688
trees.sort();
674689
}
675690

691+
fn merge_use_trees_into_one(trees: Vec<UseTree>) -> Vec<UseTree> {
692+
let mut result = vec![];
693+
for mut tree in trees {
694+
if tree.attrs.is_some() {
695+
result.push(tree);
696+
} else if let Some(UseSegment::List(list)) = result.iter_mut().find_map(|r| {
697+
if r.same_visibility(&tree) && r.attrs.is_none() {
698+
r.path.get_mut(0)
699+
} else {
700+
None
701+
}
702+
}) {
703+
list.push(tree);
704+
} else {
705+
tree.path = vec![UseSegment::List(vec![tree.clone()])];
706+
result.push(tree);
707+
}
708+
}
709+
710+
// As a result of merging into one, if a `UseTree` has only one crate path just like
711+
// `use {a::{b, c}}`, outmost curly braces can be removed.
712+
for r in result.iter_mut() {
713+
match r.path.get(0) {
714+
Some(UseSegment::List(list)) if r.path.len() == 1 && list.len() == 1 => {
715+
*r = list[0].clone();
716+
}
717+
_ => {}
718+
}
719+
}
720+
721+
result
722+
}
723+
676724
impl PartialOrd for UseSegment {
677725
fn partial_cmp(&self, other: &UseSegment) -> Option<Ordering> {
678726
Some(self.cmp(other))
@@ -872,6 +920,7 @@ impl Rewrite for UseTree {
872920
pub(crate) enum SharedPrefix {
873921
Crate,
874922
Module,
923+
One,
875924
}
876925

877926
#[cfg(test)]
@@ -1086,6 +1135,25 @@ mod test {
10861135
);
10871136
}
10881137

1138+
#[test]
1139+
fn test_use_tree_merge_one() {
1140+
test_merge!(One, ["a", "b"], ["{a, b}"]);
1141+
1142+
test_merge!(
1143+
One,
1144+
["a", "a::{aa, ab::{aba, abb}}"],
1145+
["a::{self, aa, ab::{aba, abb}}"]
1146+
);
1147+
1148+
test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]);
1149+
1150+
test_merge!(
1151+
One,
1152+
["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"],
1153+
["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"]
1154+
);
1155+
}
1156+
10891157
#[test]
10901158
fn test_flatten_use_trees() {
10911159
assert_eq!(

src/formatting/reorder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ fn rewrite_reorderable_or_regroupable_items(
234234
merge_use_trees(normalized_items, SharedPrefix::Module)
235235
}
236236
ImportGranularity::Item => flatten_use_trees(normalized_items),
237+
ImportGranularity::One => merge_use_trees(normalized_items, SharedPrefix::One),
237238
ImportGranularity::Preserve => normalized_items,
238239
};
239240

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// rustfmt-imports_granularity: One
2+
3+
use a;
4+
5+
use a;
6+
use b;
7+
8+
use a;
9+
use a::{ab, aa::aaa};
10+
11+
use a::aa;
12+
use a::ab::{aba, abb};
13+
use b::ba::{baa as foo, bab};
14+
15+
use a::aa;
16+
#[cfg(test)]
17+
use a::{ab, ac::aca};
18+
#[cfg(test)]
19+
use b::{ba, bb, bc::bca::{bcaa, bcab}};
20+
use a::ad::ada;
21+
use b;
22+
23+
pub use a::aa;
24+
use a::{ab, ac, ad};
25+
pub use a::ae;
26+
use b::ba;
27+
pub use b::{bb, bc::bca};
28+
29+
use a::{aa::*, ab};
30+
use b::{bb::{self, bba}, ba};
31+
use a::ac::{acb, aca};
32+
use a::aa::aaa;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// rustfmt-imports_granularity: One
2+
3+
use a;
4+
5+
use {a, b};
6+
7+
use a::{self, aa::aaa, ab};
8+
9+
use {
10+
a::{
11+
aa,
12+
ab::{aba, abb},
13+
},
14+
b::ba::{baa as foo, bab},
15+
};
16+
17+
#[cfg(test)]
18+
use a::{ab, ac::aca};
19+
#[cfg(test)]
20+
use b::{
21+
ba, bb,
22+
bc::bca::{bcaa, bcab},
23+
};
24+
use {
25+
a::{aa, ad::ada},
26+
b,
27+
};
28+
29+
pub use {
30+
a::{aa, ae},
31+
b::{bb, bc::bca},
32+
};
33+
use {
34+
a::{ab, ac, ad},
35+
b::ba,
36+
};
37+
38+
use {
39+
a::{
40+
aa::{aaa, *},
41+
ab,
42+
ac::{aca, acb},
43+
},
44+
b::{
45+
ba,
46+
bb::{self, bba},
47+
},
48+
};

0 commit comments

Comments
 (0)