diff --git a/Configurations.md b/Configurations.md index 7130e80bfdc..10565f84451 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2548,6 +2548,35 @@ fn lorem() { } ``` +## `unnest_imports` + +Break apart nested import groups into separate `use` statements. +When used with `merge_imports`, imports will be merged together by module but +not merge into a single tree. + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No + +#### `false` (default): + +```rust +use foo::{a::b, c}; +use foo::{ + d::{e, f}, + g::{h, i}, +}; +``` + +#### `true`: + +```rust +use foo::a::b; +use foo::c; +use foo::d::{e, f}; +use foo::g::{h, i}; +``` + ## `unstable_features` Enable unstable features on stable and beta channels (unstable features are available by default on nightly). diff --git a/src/config.rs b/src/config.rs index 6a0c46f6275..f9a6b497959 100644 --- a/src/config.rs +++ b/src/config.rs @@ -78,6 +78,7 @@ create_config! { imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports"; imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block"; merge_imports: bool, false, false, "Merge imports"; + unnest_imports: bool, false, false, "Break apart nested import groups"; group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false, "Controls the strategy for how imports are grouped together"; @@ -596,6 +597,7 @@ where_single_line = false imports_indent = "Block" imports_layout = "Mixed" merge_imports = false +unnest_imports = false group_imports = "Preserve" reorder_imports = true reorder_modules = true diff --git a/src/formatting/imports.rs b/src/formatting/imports.rs index 72aff70987a..1b75ddf8668 100644 --- a/src/formatting/imports.rs +++ b/src/formatting/imports.rs @@ -179,6 +179,47 @@ pub(crate) fn merge_use_trees(use_trees: Vec) -> Vec { result } +pub(crate) fn unnest_use_trees(mut use_trees: Vec) -> Vec { + let mut result = Vec::with_capacity(use_trees.len()); + while let Some(mut use_tree) = use_trees.pop() { + if !use_tree.has_comment() && use_tree.attrs.is_none() { + if let Some((UseSegment::List(list), ref prefix)) = use_tree.path.split_last_mut() { + let span = use_tree.span; + let visibility = &use_tree.visibility; + list.retain(|nested_use_tree| { + if matches!( + nested_use_tree.path[..], + [UseSegment::Ident(..)] | [UseSegment::Slf(..)] | [UseSegment::Glob] + ) { + return true; + } + if nested_use_tree.has_comment() { + return true; + } + // nested item detected; flatten once, but process it again + // in case it has more nesting + use_trees.push(UseTree { + path: prefix + .iter() + .cloned() + .chain(nested_use_tree.path.iter().cloned()) + .collect(), + span, + list_item: None, + visibility: visibility.clone(), + attrs: None, + }); + // remove this item + false + }); + use_tree = use_tree.normalize(); + } + } + result.push(use_tree); + } + result +} + impl fmt::Debug for UseTree { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self, f) diff --git a/src/formatting/reorder.rs b/src/formatting/reorder.rs index 379aa109b14..0ba37d29991 100644 --- a/src/formatting/reorder.rs +++ b/src/formatting/reorder.rs @@ -15,7 +15,7 @@ use crate::config::{Config, GroupImportsTactic}; use crate::formatting::imports::UseSegment; use crate::formatting::modules::{get_mod_inner_attrs, FileModMap}; use crate::formatting::{ - imports::{merge_use_trees, UseTree}, + imports::{merge_use_trees, unnest_use_trees, UseTree}, items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}, lists::{itemize_list, write_list, ListFormatting, ListItem}, rewrite::RewriteContext, @@ -229,6 +229,9 @@ fn rewrite_reorderable_or_regroupable_items( if context.config.merge_imports() { normalized_items = merge_use_trees(normalized_items); } + if context.config.unnest_imports() { + normalized_items = unnest_use_trees(normalized_items); + } let mut regrouped_items = match context.config.group_imports() { GroupImportsTactic::Preserve => vec![normalized_items], diff --git a/tests/source/merge_and_unnest_imports.rs b/tests/source/merge_and_unnest_imports.rs new file mode 100644 index 00000000000..60bf06b47d6 --- /dev/null +++ b/tests/source/merge_and_unnest_imports.rs @@ -0,0 +1,9 @@ +// rustfmt-merge_imports: true +// rustfmt-unnest_imports: true + +use a::b; +use a::c; +use a::d::e; +use a::d::f; +use a::d::{g, h::{i, j::k, l}}; +pub use a::d::m; diff --git a/tests/source/unnest_imports.rs b/tests/source/unnest_imports.rs new file mode 100644 index 00000000000..c4b2e95b64a --- /dev/null +++ b/tests/source/unnest_imports.rs @@ -0,0 +1,17 @@ +// rustfmt-unnest_imports: true + +use a::{b::c, d::e}; +use a::{f, g::{h, i}}; +use a::{j::{self, k::{self, l}, m}, n::{o::p, q}}; +pub use a::{r::s, t}; + +#[cfg(test)] +use foo::{a::b, c::d}; + +use bar::{ + // comment + a::b, + // more comment + c::d, + e::f, +}; diff --git a/tests/target/merge_and_unnest_imports.rs b/tests/target/merge_and_unnest_imports.rs new file mode 100644 index 00000000000..2a240d9b4ac --- /dev/null +++ b/tests/target/merge_and_unnest_imports.rs @@ -0,0 +1,8 @@ +// rustfmt-merge_imports: true +// rustfmt-unnest_imports: true + +use a::d::h::j::k; +use a::d::h::{i, l}; +pub use a::d::m; +use a::d::{e, f, g}; +use a::{b, c}; diff --git a/tests/target/unnest_imports.rs b/tests/target/unnest_imports.rs new file mode 100644 index 00000000000..a3f33ac489a --- /dev/null +++ b/tests/target/unnest_imports.rs @@ -0,0 +1,23 @@ +// rustfmt-unnest_imports: true + +use a::b::c; +use a::d::e; +use a::f; +use a::g::{h, i}; +use a::j::k::{self, l}; +use a::j::{self, m}; +use a::n::o::p; +use a::n::q; +pub use a::r::s; +pub use a::t; + +#[cfg(test)] +use foo::{a::b, c::d}; + +use bar::e::f; +use bar::{ + // comment + a::b, + // more comment + c::d, +};