Skip to content

Commit c5736e2

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 a9876e8 commit c5736e2

File tree

6 files changed

+171
-3
lines changed

6 files changed

+171
-3
lines changed

Configurations.md

+18-1
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 as long as 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

+2
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

+70-2
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ impl UseSegment {
166166

167167
pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -> Vec<UseTree> {
168168
let mut result = Vec::with_capacity(use_trees.len());
169+
170+
// If `merge_by` is `SharedPrefix::One`, we firstly compute the result as if it's
171+
// `SharedPrefix::Crate`, and then merge the result one more time.
172+
let temp_merge_by = if merge_by == SharedPrefix::One {
173+
SharedPrefix::Crate
174+
} else {
175+
merge_by
176+
};
177+
169178
for use_tree in use_trees {
170179
if use_tree.has_comment() || use_tree.attrs.is_some() {
171180
result.push(use_tree);
@@ -187,14 +196,19 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -
187196
}
188197
if let Some(tree) = result
189198
.iter_mut()
190-
.find(|tree| tree.share_prefix(&flattened, merge_by))
199+
.find(|tree| tree.share_prefix(&flattened, temp_merge_by))
191200
{
192-
tree.merge(&flattened, merge_by);
201+
tree.merge(&flattened, temp_merge_by);
193202
} else {
194203
result.push(flattened);
195204
}
196205
}
197206
}
207+
208+
if merge_by == SharedPrefix::One {
209+
result = merge_use_trees_into_one(result);
210+
}
211+
198212
result
199213
}
200214

@@ -579,6 +593,7 @@ impl UseTree {
579593
SharedPrefix::Module => {
580594
self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1]
581595
}
596+
SharedPrefix::One => unreachable!(),
582597
}
583598
}
584599
}
@@ -692,6 +707,39 @@ fn merge_use_trees_inner(trees: &mut Vec<UseTree>, use_tree: UseTree, merge_by:
692707
trees.sort();
693708
}
694709

710+
fn merge_use_trees_into_one(trees: Vec<UseTree>) -> Vec<UseTree> {
711+
let mut result = vec![];
712+
for mut tree in trees {
713+
if tree.attrs.is_some() {
714+
result.push(tree);
715+
} else if let Some(UseSegment::List(list)) = result.iter_mut().find_map(|r| {
716+
if r.same_visibility(&tree) && r.attrs.is_none() {
717+
r.path.get_mut(0)
718+
} else {
719+
None
720+
}
721+
}) {
722+
list.push(tree);
723+
} else {
724+
tree.path = vec![UseSegment::List(vec![tree.clone()])];
725+
result.push(tree);
726+
}
727+
}
728+
729+
// As a result of merging into one, if a `UseTree` has only one crate path just like
730+
// `use {a::{b, c}}`, outmost curly braces can be removed.
731+
for r in result.iter_mut() {
732+
match r.path.get(0) {
733+
Some(UseSegment::List(list)) if r.path.len() == 1 && list.len() == 1 => {
734+
*r = list[0].clone();
735+
}
736+
_ => {}
737+
}
738+
}
739+
740+
result
741+
}
742+
695743
impl Hash for UseTree {
696744
fn hash<H: Hasher>(&self, state: &mut H) {
697745
self.path.hash(state);
@@ -897,6 +945,7 @@ impl Rewrite for UseTree {
897945
pub(crate) enum SharedPrefix {
898946
Crate,
899947
Module,
948+
One,
900949
}
901950

902951
#[cfg(test)]
@@ -1111,6 +1160,25 @@ mod test {
11111160
);
11121161
}
11131162

1163+
#[test]
1164+
fn test_use_tree_merge_one() {
1165+
test_merge!(One, ["a", "b"], ["{a, b}"]);
1166+
1167+
test_merge!(
1168+
One,
1169+
["a", "a::{aa, ab::{aba, abb}}"],
1170+
["a::{self, aa, ab::{aba, abb}}"]
1171+
);
1172+
1173+
test_merge!(One, ["a", "b::{ba, *}"], ["{a, b::{ba, *}}"]);
1174+
1175+
test_merge!(
1176+
One,
1177+
["a", "b::{ba, bb}", "a::{aa::*, ab::aba}"],
1178+
["{a::{self, aa::*, ab::aba}, b::{ba, bb}}"]
1179+
);
1180+
}
1181+
11141182
#[test]
11151183
fn test_flatten_use_trees() {
11161184
assert_eq!(

src/formatting/reorder.rs

+1
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

+32
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;
+48
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)