about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Configurations.md17
-rw-r--r--src/config/options.rs2
-rw-r--r--src/imports.rs37
-rw-r--r--src/reorder.rs15
-rw-r--r--tests/source/imports_granularity_item.rs6
-rw-r--r--tests/target/imports_granularity_item.rs13
6 files changed, 81 insertions, 9 deletions
diff --git a/Configurations.md b/Configurations.md
index e4f84b23082..c92be5df010 100644
--- a/Configurations.md
+++ b/Configurations.md
@@ -1620,7 +1620,7 @@ pub enum Foo {}
 How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
 
 - **Default value**: `Preserve`
-- **Possible values**: `Preserve`, `Crate`, `Module`
+- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`
 - **Stable**: No
 
 #### `Preserve` (default):
@@ -1659,6 +1659,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 690e0b73a3e..c0491630c00 100644
--- a/src/config/options.rs
+++ b/src/config/options.rs
@@ -121,6 +121,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/imports.rs b/src/imports.rs
index 156bf6da5ab..0f635fe1ccb 100644
--- a/src/imports.rs
+++ b/src/imports.rs
@@ -181,6 +181,24 @@ pub(crate) fn merge_use_trees(use_trees: Vec<UseTree>, merge_by: SharedPrefix) -
     result
 }
 
+pub(crate) fn flatten_use_trees(use_trees: Vec<UseTree>) -> Vec<UseTree> {
+    use_trees
+        .into_iter()
+        .flat_map(UseTree::flatten)
+        .map(|mut tree| {
+            // If a path ends in `::self`, rewrite it to `::{self}`.
+            if let Some(UseSegment::Slf(..)) = tree.path.last() {
+                let self_segment = tree.path.pop().unwrap();
+                tree.path.push(UseSegment::List(vec![UseTree::from_path(
+                    vec![self_segment],
+                    DUMMY_SP,
+                )]));
+            }
+            tree
+        })
+        .collect()
+}
+
 impl fmt::Debug for UseTree {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         fmt::Display::fmt(self, f)
@@ -1085,6 +1103,25 @@ mod test {
     }
 
     #[test]
+    fn test_flatten_use_trees() {
+        assert_eq!(
+            flatten_use_trees(parse_use_trees!["foo::{a::{b, c}, d::e}"]),
+            parse_use_trees!["foo::a::b", "foo::a::c", "foo::d::e"]
+        );
+
+        assert_eq!(
+            flatten_use_trees(parse_use_trees!["foo::{self, a, b::{c, d}, e::*}"]),
+            parse_use_trees![
+                "foo::{self}",
+                "foo::a",
+                "foo::b::c",
+                "foo::b::d",
+                "foo::e::*"
+            ]
+        );
+    }
+
+    #[test]
     fn test_use_tree_flatten() {
         assert_eq!(
             parse_use_tree("a::b::{c, d, e, f}").flatten(),
diff --git a/src/reorder.rs b/src/reorder.rs
index 3ac6bc5f5c4..ac65ff2c108 100644
--- a/src/reorder.rs
+++ b/src/reorder.rs
@@ -12,7 +12,7 @@ use rustc_ast::ast;
 use rustc_span::{symbol::sym, Span};
 
 use crate::config::{Config, GroupImportsTactic, ImportGranularity};
-use crate::imports::{merge_use_trees, SharedPrefix, UseSegment, UseTree};
+use crate::imports::{flatten_use_trees, merge_use_trees, SharedPrefix, UseSegment, UseTree};
 use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod};
 use crate::lists::{itemize_list, write_list, ListFormatting, ListItem};
 use crate::rewrite::RewriteContext;
@@ -107,15 +107,14 @@ 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::Preserve => {}
-            }
+                ImportGranularity::Item => flatten_use_trees(normalized_items),
+                ImportGranularity::Preserve => normalized_items,
+            };
 
             let mut regrouped_items = match context.config.group_imports() {
                 GroupImportsTactic::Preserve => vec![normalized_items],
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_item.rs b/tests/target/imports_granularity_item.rs
new file mode 100644
index 00000000000..eace785e670
--- /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::m;
+use a::l::n::o;
+use a::l::p::*;
+use a::l::{self};
+use a::q::{self};