diff --git a/Configurations.md b/Configurations.md index aaefc46180b..bff75027376 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1710,7 +1710,7 @@ pub enum Foo {} Merge together related imports based on their paths. - **Default value**: `Preserve` -- **Possible values**: `Preserve`, `Crate`, `Module` +- **Possible values**: `Preserve`, `Crate`, `Module`, `Item` - **Stable**: No #### `Preserve` (default): @@ -1749,6 +1749,21 @@ use foo::{a, b, c}; use qux::{h, i}; ``` +#### `Item`: + +Flatten imports so that each has its own `use` statement. + +```rust +use foo::a; +use foo::b; +use foo::b::f; +use foo::b::g; +use foo::c; +use foo::d::e; +use qux::h; +use qux::i; +``` + ## `merge_imports` This option is deprecated. Use `imports_granularity = "Crate"` instead. diff --git a/src/config/options.rs b/src/config/options.rs index a272eadd72f..0009d0a52bb 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -128,6 +128,8 @@ pub enum ImportGranularity { Crate, /// Use one `use` statement per module. Module, + /// Use one `use` statement per imported item. + Item, } #[config_type] diff --git a/src/formatting/imports.rs b/src/formatting/imports.rs index bfb862ae6e6..9de47825824 100644 --- a/src/formatting/imports.rs +++ b/src/formatting/imports.rs @@ -542,6 +542,7 @@ impl UseTree { SharedPrefix::Module => { self.path[..self.path.len() - 1] == other.path[..other.path.len() - 1] } + SharedPrefix::NoPrefix => false, } } } @@ -549,17 +550,17 @@ impl UseTree { fn flatten(self) -> Vec { match self.path.last() { Some(UseSegment::List(list)) => { - if list.len() == 1 && list[0].path.len() == 1 { - if let UseSegment::Slf(..) = list[0].path[0] { - return vec![self]; - }; - } let prefix = &self.path[..self.path.len() - 1]; let mut result = vec![]; for nested_use_tree in list { for flattened in &mut nested_use_tree.clone().flatten() { let mut new_path = prefix.to_vec(); new_path.append(&mut flattened.path); + if flattened.path.len() == 1 { + if let UseSegment::Slf(..) = flattened.path[0] { + new_path.pop(); + } + } result.push(UseTree { path: new_path, span: self.span, @@ -854,6 +855,7 @@ impl Rewrite for UseTree { pub(crate) enum SharedPrefix { Crate, Module, + NoPrefix, } #[cfg(test)] @@ -1049,7 +1051,7 @@ mod test { test_merge!( Crate, ["a::{self}", "b::{self as foo}"], - ["a::{self}", "b::{self as foo}"] + ["a::self", "b::self as foo"] ); } @@ -1068,6 +1070,21 @@ mod test { ); } + #[test] + fn test_use_tree_merge_no_prefix() { + test_merge!( + NoPrefix, + ["foo::{a::{b, c}, d::e}"], + ["foo::a::b", "foo::a::c", "foo::d::e"] + ); + + test_merge!( + NoPrefix, + ["foo::{self, a, b::{c, d}, e::*}"], + ["foo::self", "foo::a", "foo::b::c", "foo::b::d", "foo::e::*"] + ) + } + #[test] fn test_use_tree_flatten() { assert_eq!( diff --git a/src/formatting/reorder.rs b/src/formatting/reorder.rs index 9d1777d82df..0ca6c2cd443 100644 --- a/src/formatting/reorder.rs +++ b/src/formatting/reorder.rs @@ -228,14 +228,29 @@ fn rewrite_reorderable_or_regroupable_items( for (item, list_item) in normalized_items.iter_mut().zip(list_items) { item.list_item = Some(list_item.clone()); } - match context.config.imports_granularity() { - ImportGranularity::Crate => { - normalized_items = merge_use_trees(normalized_items, SharedPrefix::Crate) - } + normalized_items = match context.config.imports_granularity() { + ImportGranularity::Crate => merge_use_trees(normalized_items, SharedPrefix::Crate), ImportGranularity::Module => { - normalized_items = merge_use_trees(normalized_items, SharedPrefix::Module) + merge_use_trees(normalized_items, SharedPrefix::Module) + } + ImportGranularity::Item => { + merge_use_trees(normalized_items, SharedPrefix::NoPrefix) + } + ImportGranularity::Preserve => normalized_items, + }; + for item in normalized_items.iter_mut() { + if let Some(UseSegment::Slf(..)) = item.path.last() { + let self_seg = item.path.pop().unwrap(); + match self_seg { + UseSegment::Slf(Some(self_rename)) => match item.path.last_mut() { + Some(UseSegment::Ident(_, rename @ None)) + | Some(UseSegment::Crate(rename @ None)) + | Some(UseSegment::Super(rename @ None)) => *rename = Some(self_rename), + _ => {} + }, + _ => {} + } } - ImportGranularity::Preserve => {} } let mut regrouped_items = match context.config.group_imports() { diff --git a/tests/source/imports_granularity_item.rs b/tests/source/imports_granularity_item.rs new file mode 100644 index 00000000000..d0e94df66ae --- /dev/null +++ b/tests/source/imports_granularity_item.rs @@ -0,0 +1,6 @@ +// rustfmt-imports_granularity: Item + +use a::{b, c, d}; +use a::{f::g, h::{i, j}}; +use a::{l::{self, m, n::o, p::*}}; +use a::q::{self}; diff --git a/tests/target/imports_granularity_crate.rs b/tests/target/imports_granularity_crate.rs index d75906d30f1..472da59e653 100644 --- a/tests/target/imports_granularity_crate.rs +++ b/tests/target/imports_granularity_crate.rs @@ -16,13 +16,13 @@ pub use foo::{bar, foobar}; use a::b::c::{d, xxx, yyy, zzz, *}; // https://github.com/rust-lang/rustfmt/issues/3808 -use d::{self}; -use e::{self as foo}; +use d; +use e as foo; use f::{self, b}; use g::{self, a, b}; use h::a; -use i::a::{self}; -use j::a::{self}; +use i::a; +use j::a; use k::{a, b, c, d}; use l::{a, b, c, d}; diff --git a/tests/target/imports_granularity_item.rs b/tests/target/imports_granularity_item.rs new file mode 100644 index 00000000000..466fe5242a1 --- /dev/null +++ b/tests/target/imports_granularity_item.rs @@ -0,0 +1,13 @@ +// rustfmt-imports_granularity: Item + +use a::b; +use a::c; +use a::d; +use a::f::g; +use a::h::i; +use a::h::j; +use a::l; +use a::l::m; +use a::l::n::o; +use a::l::p::*; +use a::q; diff --git a/tests/target/issue-3645.rs b/tests/target/issue-3645.rs index 14bf96e6383..bb2a28bc9fc 100644 --- a/tests/target/issue-3645.rs +++ b/tests/target/issue-3645.rs @@ -1,3 +1,3 @@ mod x { - use super::self as x; + use super as x; }