From 2d3a9a5847dccb2623c956e5aa175493fa68aa2a Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Sun, 14 Apr 2024 18:40:49 +0200 Subject: [PATCH] remove braces when fixing a nested use tree into a single use --- compiler/rustc_resolve/src/check_unused.rs | 27 +++++++++++++- tests/ui/suggestions/unused-imports.fixed | 35 ++++++++++++++++++ tests/ui/suggestions/unused-imports.rs | 42 ++++++++++++++++++++++ tests/ui/suggestions/unused-imports.stderr | 32 +++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/ui/suggestions/unused-imports.fixed create mode 100644 tests/ui/suggestions/unused-imports.rs create mode 100644 tests/ui/suggestions/unused-imports.stderr diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 950a0f9ff652d..5fe68085d6537 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -292,7 +292,7 @@ fn calc_unused_spans( UnusedSpanResult::Used } } - ast::UseTreeKind::Nested { items: ref nested, .. } => { + ast::UseTreeKind::Nested { items: ref nested, span: tree_span } => { if nested.is_empty() { return UnusedSpanResult::Unused { spans: vec![use_tree.span], remove: full_span }; } @@ -300,6 +300,7 @@ fn calc_unused_spans( let mut unused_spans = Vec::new(); let mut to_remove = Vec::new(); let mut used_childs = 0; + let mut contains_self = false; let mut previous_unused = false; for (pos, (use_tree, use_tree_id)) in nested.iter().enumerate() { let remove = match calc_unused_spans(unused_import, use_tree, *use_tree_id) { @@ -339,6 +340,8 @@ fn calc_unused_spans( to_remove.push(remove_span); } } + contains_self |= use_tree.prefix == kw::SelfLower + && matches!(use_tree.kind, ast::UseTreeKind::Simple(None)); previous_unused = remove.is_some(); } if unused_spans.is_empty() { @@ -346,6 +349,28 @@ fn calc_unused_spans( } else if used_childs == 0 { UnusedSpanResult::Unused { spans: unused_spans, remove: full_span } } else { + // If there is only one remaining child that is used, the braces around the use + // tree are not needed anymore. In that case, we determine the span of the left + // brace and the right brace, and tell rustfix to remove them as well. + // + // This means that `use a::{B, C};` will be turned into `use a::B;` rather than + // `use a::{B};`, removing a rustfmt roundtrip. + // + // Note that we cannot remove the braces if the only item inside the use tree is + // `self`: `use foo::{self};` is valid Rust syntax, while `use foo::self;` errors + // out. We also cannot turn `use foo::{self}` into `use foo`, as the former doesn't + // import types with the same name as the module. + if used_childs == 1 && !contains_self { + // Left brace, from the start of the nested group to the first item. + to_remove.push( + tree_span.shrink_to_lo().to(nested.first().unwrap().0.span.shrink_to_lo()), + ); + // Right brace, from the end of the last item to the end of the nested group. + to_remove.push( + nested.last().unwrap().0.span.shrink_to_hi().to(tree_span.shrink_to_hi()), + ); + } + UnusedSpanResult::PartialUnused { spans: unused_spans, remove: to_remove } } } diff --git a/tests/ui/suggestions/unused-imports.fixed b/tests/ui/suggestions/unused-imports.fixed new file mode 100644 index 0000000000000..57dd091c0436d --- /dev/null +++ b/tests/ui/suggestions/unused-imports.fixed @@ -0,0 +1,35 @@ +//@ run-rustfix +//@ check-pass + +#![warn(unused_imports)] + +pub mod nested { + pub struct A; + pub struct B; + pub struct C; + pub struct D; + pub mod even_more { + pub struct E; + pub struct F; + pub struct G; + } + pub mod another { + pub struct H; + pub struct I; + } +} + +use nested::B; +//~^ WARN unused import + +use nested::even_more::F; +//~^^^^^^^ WARN unused import + +// Note that the following fix should result in `::{self}`, not `::self`. The latter is invalid +// Rust syntax, so the braces should not be removed. +use nested::another::{self}; +//~^ WARN unused import + +fn main() { + let _ = (B, F, another::I); +} diff --git a/tests/ui/suggestions/unused-imports.rs b/tests/ui/suggestions/unused-imports.rs new file mode 100644 index 0000000000000..5f9dd243bdd2a --- /dev/null +++ b/tests/ui/suggestions/unused-imports.rs @@ -0,0 +1,42 @@ +//@ run-rustfix +//@ check-pass + +#![warn(unused_imports)] + +pub mod nested { + pub struct A; + pub struct B; + pub struct C; + pub struct D; + pub mod even_more { + pub struct E; + pub struct F; + pub struct G; + } + pub mod another { + pub struct H; + pub struct I; + } +} + +use nested::{A, B, C}; +//~^ WARN unused import + +use nested::{ + D, + even_more::{ + E, + F, + G, + }, + }; +//~^^^^^^^ WARN unused import + +// Note that the following fix should result in `::{self}`, not `::self`. The latter is invalid +// Rust syntax, so the braces should not be removed. +use nested::another::{self, I}; +//~^ WARN unused import + +fn main() { + let _ = (B, F, another::I); +} diff --git a/tests/ui/suggestions/unused-imports.stderr b/tests/ui/suggestions/unused-imports.stderr new file mode 100644 index 0000000000000..bf112608da7ed --- /dev/null +++ b/tests/ui/suggestions/unused-imports.stderr @@ -0,0 +1,32 @@ +warning: unused imports: `A`, `C` + --> $DIR/unused-imports.rs:22:14 + | +LL | use nested::{A, B, C}; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-imports.rs:4:9 + | +LL | #![warn(unused_imports)] + | ^^^^^^^^^^^^^^ + +warning: unused imports: `D`, `E`, `G` + --> $DIR/unused-imports.rs:26:5 + | +LL | D, + | ^ +LL | even_more::{ +LL | E, + | ^ +LL | F, +LL | G, + | ^ + +warning: unused import: `I` + --> $DIR/unused-imports.rs:37:29 + | +LL | use nested::another::{self, I}; + | ^ + +warning: 3 warnings emitted +