Skip to content

Commit 07ec3b3

Browse files
goffriecalebcartwright
authored andcommitted
Make merge_imports more granular with a Module option.
1 parent 94f17f8 commit 07ec3b3

File tree

16 files changed

+145
-33
lines changed

16 files changed

+145
-33
lines changed

Configurations.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,26 +1707,43 @@ pub enum Foo {}
17071707

17081708
## `merge_imports`
17091709

1710-
Merge multiple imports into a single nested import.
1710+
Merge together related imports based on their paths.
17111711

1712-
- **Default value**: `false`
1713-
- **Possible values**: `true`, `false`
1712+
This option requires `reorder_imports`, which is enabled by default.
1713+
1714+
- **Default value**: `Never`
1715+
- **Possible values**: `Never`, `Crate`, `Module`
17141716
- **Stable**: No (tracking issue: [#3362](https://github.com/rust-lang/rustfmt/issues/3362))
17151717

1716-
#### `false` (default):
1718+
#### `Never` (default):
17171719

17181720
```rust
1719-
use foo::{a, c, d};
1720-
use foo::{b, g};
1721-
use foo::{e, f};
1721+
use foo::b;
1722+
use foo::b::{f, g};
1723+
use foo::{a, c, d::e};
1724+
use qux::{h, i};
17221725
```
17231726

1724-
#### `true`:
1727+
#### `Crate`:
17251728

17261729
```rust
1727-
use foo::{a, b, c, d, e, f, g};
1730+
use foo::{
1731+
a, b,
1732+
b::{f, g},
1733+
c,
1734+
d::e,
1735+
};
1736+
use qux::{h, i};
17281737
```
17291738

1739+
#### `Module`:
1740+
1741+
```rust
1742+
use foo::b::{f, g};
1743+
use foo::d::e;
1744+
use foo::{a, b, c};
1745+
use qux::{h, i};
1746+
```
17301747

17311748
## `newline_style`
17321749

@@ -2560,7 +2577,7 @@ Enable unstable features on stable and beta channels (unstable features are avai
25602577

25612578
For example:
25622579
```bash
2563-
rustfmt src/lib.rs --config unstable_features=true merge_imports=true
2580+
rustfmt src/lib.rs --config unstable_features=true merge_imports=Crate
25642581
```
25652582

25662583
## `use_field_init_shorthand`

src/config.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ create_config! {
7777
// Imports
7878
imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports";
7979
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
80-
merge_imports: bool, false, false, "Merge imports";
80+
merge_imports: MergeImports, MergeImports::Never, false, "Merge imports";
8181
group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false,
8282
"Controls the strategy for how imports are grouped together";
8383

@@ -595,7 +595,7 @@ fn_single_line = false
595595
where_single_line = false
596596
imports_indent = "Block"
597597
imports_layout = "Mixed"
598-
merge_imports = false
598+
merge_imports = "Never"
599599
group_imports = "Preserve"
600600
reorder_imports = true
601601
reorder_modules = true
@@ -716,13 +716,13 @@ ignore = []
716716
}
717717
let toml = r#"
718718
unstable_features = true
719-
merge_imports = true
719+
merge_imports = "Crate"
720720
"#;
721721
let config = Config::from_toml(toml, Path::new("")).unwrap();
722722
assert_eq!(config.was_set().unstable_features(), true);
723723
assert_eq!(config.was_set().merge_imports(), true);
724724
assert_eq!(config.unstable_features(), true);
725-
assert_eq!(config.merge_imports(), true);
725+
assert_eq!(config.merge_imports(), MergeImports::Crate);
726726
}
727727

728728
#[test]
@@ -731,9 +731,9 @@ ignore = []
731731
// This test requires non-nightly
732732
return;
733733
}
734-
let config = Config::from_toml("merge_imports = true", Path::new("")).unwrap();
734+
let config = Config::from_toml("merge_imports = Crate", Path::new("")).unwrap();
735735
assert_eq!(config.was_set().merge_imports(), false);
736-
assert_eq!(config.merge_imports(), false);
736+
assert_eq!(config.merge_imports(), MergeImports::Never);
737737
}
738738

739739
#[test]
@@ -778,12 +778,12 @@ ignore = []
778778
}
779779
let mut config = Config::default();
780780
assert_eq!(config.unstable_features(), false);
781-
config.override_value("merge_imports", "true");
782-
assert_eq!(config.merge_imports(), false);
781+
config.override_value("merge_imports", "Crate");
782+
assert_eq!(config.merge_imports(), MergeImports::Crate);
783783
config.override_value("unstable_features", "true");
784784
assert_eq!(config.unstable_features(), true);
785-
config.override_value("merge_imports", "true");
786-
assert_eq!(config.merge_imports(), true);
785+
config.override_value("merge_imports", "Crate");
786+
assert_eq!(config.merge_imports(), MergeImports::Crate);
787787
}
788788

789789
#[test]

src/config/options.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ pub enum GroupImportsTactic {
118118
StdExternalCrate,
119119
}
120120

121+
#[config_type]
122+
/// How to merge imports.
123+
pub enum MergeImports {
124+
/// Do not merge imports.
125+
Never,
126+
/// Use one `use` statement per crate.
127+
Crate,
128+
/// Use one `use` statement per module.
129+
Module,
130+
}
131+
121132
#[config_type]
122133
pub enum ReportTactic {
123134
Always,

src/formatting/imports.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,48 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
179179
result
180180
}
181181

182+
/// Split apart nested imports in use trees.
183+
pub(crate) fn unnest_use_trees(mut use_trees: Vec<UseTree>) -> Vec<UseTree> {
184+
let mut result = Vec::with_capacity(use_trees.len());
185+
while let Some(mut use_tree) = use_trees.pop() {
186+
if !use_tree.has_comment() && use_tree.attrs.is_none() {
187+
if let Some((UseSegment::List(list), ref prefix)) = use_tree.path.split_last_mut() {
188+
let span = use_tree.span;
189+
let visibility = &use_tree.visibility;
190+
list.retain(|nested_use_tree| {
191+
if matches!(
192+
nested_use_tree.path[..],
193+
[UseSegment::Ident(..)] | [UseSegment::Slf(..)] | [UseSegment::Glob]
194+
) {
195+
return true;
196+
}
197+
if nested_use_tree.has_comment() {
198+
return true;
199+
}
200+
// nested item detected; flatten once, but process it again
201+
// in case it has more nesting
202+
use_trees.push(UseTree {
203+
path: prefix
204+
.iter()
205+
.cloned()
206+
.chain(nested_use_tree.path.iter().cloned())
207+
.collect(),
208+
span,
209+
list_item: None,
210+
visibility: visibility.clone(),
211+
attrs: None,
212+
});
213+
// remove this item
214+
false
215+
});
216+
use_tree = use_tree.normalize();
217+
}
218+
}
219+
result.push(use_tree);
220+
}
221+
result
222+
}
223+
182224
impl fmt::Debug for UseTree {
183225
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184226
fmt::Display::fmt(self, f)

src/formatting/reorder.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ use std::cmp::{Ord, Ordering};
1111
use rustc_ast::ast;
1212
use rustc_span::{symbol::sym, Span};
1313

14-
use crate::config::{Config, GroupImportsTactic};
14+
use crate::config::{Config, GroupImportsTactic, MergeImports};
1515
use crate::formatting::imports::UseSegment;
1616
use crate::formatting::modules::{get_mod_inner_attrs, FileModMap};
1717
use crate::formatting::{
18-
imports::{merge_use_trees, UseTree},
18+
imports::{merge_use_trees, unnest_use_trees, UseTree},
1919
items::{is_mod_decl, rewrite_extern_crate, rewrite_mod},
2020
lists::{itemize_list, write_list, ListFormatting, ListItem},
2121
rewrite::RewriteContext,
@@ -226,8 +226,12 @@ fn rewrite_reorderable_or_regroupable_items(
226226
for (item, list_item) in normalized_items.iter_mut().zip(list_items) {
227227
item.list_item = Some(list_item.clone());
228228
}
229-
if context.config.merge_imports() {
230-
normalized_items = merge_use_trees(normalized_items);
229+
match context.config.merge_imports() {
230+
MergeImports::Crate => normalized_items = merge_use_trees(normalized_items),
231+
MergeImports::Module => {
232+
normalized_items = unnest_use_trees(merge_use_trees(normalized_items))
233+
}
234+
MergeImports::Never => {}
231235
}
232236

233237
let mut regrouped_items = match context.config.group_imports() {

src/rustfmt/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ struct Opt {
100100
/// Set options from command line.
101101
///
102102
/// Set configuration options via command line by specifying a list of key-value pairs
103-
/// separated by commas (e.g., rustfmt --config=max_width=100,merge_imports=true).
103+
/// separated by commas (e.g., rustfmt --config=max_width=100,merge_imports=Crate).
104104
/// These settings precedes any other settings specified in configuration files.
105105
#[structopt(long = "config")]
106106
inline_config: Option<Vec<InlineConfig>>,

tests/source/configs/group_imports/StdExternalCrate-merge_imports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// rustfmt-group_imports: StdExternalCrate
2-
// rustfmt-merge_imports: true
2+
// rustfmt-merge_imports: Crate
33
use chrono::Utc;
44
use super::update::convert_publish_payload;
55

tests/source/configs/imports_layout/merge_mixed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// rustfmt-imports_indent: Block
2-
// rustfmt-merge_imports: true
2+
// rustfmt-merge_imports: Crate
33
// rustfmt-imports_layout: Mixed
44

55
use std::{fmt, io, str};

tests/source/issue-3750.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// rustfmt-merge_imports: true
1+
// rustfmt-merge_imports: Crate
22

33
pub mod foo {
44
pub mod bar {

tests/source/merge_imports.rs renamed to tests/source/merge_imports_crate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// rustfmt-merge_imports: true
1+
// rustfmt-merge_imports: Crate
22

33
use a::{c,d,b};
44
use a::{d, e, b, a, f};

tests/source/merge_imports_module.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// rustfmt-merge_imports: Module
2+
3+
use a::{b::c, d::e};
4+
use a::{f, g::{h, i}};
5+
use a::{j::{self, k::{self, l}, m}, n::{o::p, q}};
6+
pub use a::{r::s, t};
7+
8+
#[cfg(test)]
9+
use foo::{a::b, c::d};
10+
use foo::e;
11+
12+
use bar::{
13+
// comment
14+
a::b,
15+
// more comment
16+
c::d,
17+
e::f,
18+
};

tests/target/configs/group_imports/StdExternalCrate-merge_imports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// rustfmt-group_imports: StdExternalCrate
2-
// rustfmt-merge_imports: true
2+
// rustfmt-merge_imports: Crate
33
use alloc::{alloc::Layout, vec::Vec};
44
use core::f32;
55
use std::sync::Arc;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// rustfmt-imports_indent: Block
2-
// rustfmt-merge_imports: true
2+
// rustfmt-merge_imports: Crate
33
// rustfmt-imports_layout: Mixed
44

55
use std::{fmt, io, str, str::FromStr};

tests/target/issue-3750.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// rustfmt-merge_imports: true
1+
// rustfmt-merge_imports: Crate
22

33
pub mod foo {
44
pub mod bar {

tests/target/merge_imports.rs renamed to tests/target/merge_imports_crate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// rustfmt-merge_imports: true
1+
// rustfmt-merge_imports: Crate
22

33
use a::{a, b, c, d, e, f, g};
44

tests/target/merge_imports_module.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// rustfmt-merge_imports: Module
2+
3+
use a::b::c;
4+
use a::d::e;
5+
use a::f;
6+
use a::g::{h, i};
7+
use a::j::k::{self, l};
8+
use a::j::{self, m};
9+
use a::n::o::p;
10+
use a::n::q;
11+
pub use a::r::s;
12+
pub use a::t;
13+
14+
use foo::e;
15+
#[cfg(test)]
16+
use foo::{a::b, c::d};
17+
18+
use bar::a::b;
19+
use bar::c::d;
20+
use bar::e::f;

0 commit comments

Comments
 (0)