about summary refs log tree commit diff
path: root/compiler/rustc_resolve/src/check_unused.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_resolve/src/check_unused.rs')
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs140
1 files changed, 68 insertions, 72 deletions
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index f6004fed828..fc3669fecc2 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -32,7 +32,7 @@ use rustc_ast as ast;
 use rustc_ast::visit::{self, Visitor};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{pluralize, MultiSpan};
+use rustc_errors::MultiSpan;
 use rustc_hir::def::{DefKind, Res};
 use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES};
 use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS};
@@ -128,7 +128,7 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
                     self.unused_import(self.base_id).add(id);
                 }
             }
-            ast::UseTreeKind::Nested(ref items) => self.check_imports_as_underscore(items),
+            ast::UseTreeKind::Nested { ref items, .. } => self.check_imports_as_underscore(items),
             _ => {}
         }
     }
@@ -151,11 +151,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
             // We do this in any edition.
             if warn_if_unused {
                 if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) {
-                    self.r.lint_buffer.buffer_lint_with_diagnostic(
+                    self.r.lint_buffer.buffer_lint(
                         UNUSED_EXTERN_CRATES,
                         extern_crate.id,
                         span,
-                        "unused extern crate",
                         BuiltinLintDiag::UnusedExternCrate {
                             removal_span: extern_crate.span_with_attributes,
                         },
@@ -204,11 +203,10 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
                 .span
                 .find_ancestor_inside(extern_crate.span)
                 .unwrap_or(extern_crate.ident.span);
-            self.r.lint_buffer.buffer_lint_with_diagnostic(
+            self.r.lint_buffer.buffer_lint(
                 UNUSED_EXTERN_CRATES,
                 extern_crate.id,
                 extern_crate.span,
-                "`extern crate` is not idiomatic in the new edition",
                 BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span },
             );
         }
@@ -254,7 +252,7 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
             return;
         }
 
-        if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
+        if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
             if items.is_empty() {
                 self.unused_import(self.base_id).add(id);
             }
@@ -268,9 +266,8 @@ impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
 
 enum UnusedSpanResult {
     Used,
-    FlatUnused(Span, Span),
-    NestedFullUnused(Vec<Span>, Span),
-    NestedPartialUnused(Vec<Span>, Vec<Span>),
+    Unused { spans: Vec<Span>, remove: Span },
+    PartialUnused { spans: Vec<Span>, remove: Vec<Span> },
 }
 
 fn calc_unused_spans(
@@ -288,36 +285,33 @@ fn calc_unused_spans(
     match use_tree.kind {
         ast::UseTreeKind::Simple(..) | ast::UseTreeKind::Glob => {
             if unused_import.unused.contains(&use_tree_id) {
-                UnusedSpanResult::FlatUnused(use_tree.span, full_span)
+                UnusedSpanResult::Unused { spans: vec![use_tree.span], remove: full_span }
             } else {
                 UnusedSpanResult::Used
             }
         }
-        ast::UseTreeKind::Nested(ref nested) => {
+        ast::UseTreeKind::Nested { items: ref nested, span: tree_span } => {
             if nested.is_empty() {
-                return UnusedSpanResult::FlatUnused(use_tree.span, full_span);
+                return UnusedSpanResult::Unused { spans: vec![use_tree.span], remove: full_span };
             }
 
             let mut unused_spans = Vec::new();
             let mut to_remove = Vec::new();
-            let mut all_nested_unused = true;
+            let mut used_children = 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) {
                     UnusedSpanResult::Used => {
-                        all_nested_unused = false;
+                        used_children += 1;
                         None
                     }
-                    UnusedSpanResult::FlatUnused(span, remove) => {
-                        unused_spans.push(span);
-                        Some(remove)
-                    }
-                    UnusedSpanResult::NestedFullUnused(mut spans, remove) => {
+                    UnusedSpanResult::Unused { mut spans, remove } => {
                         unused_spans.append(&mut spans);
                         Some(remove)
                     }
-                    UnusedSpanResult::NestedPartialUnused(mut spans, mut to_remove_extra) => {
-                        all_nested_unused = false;
+                    UnusedSpanResult::PartialUnused { mut spans, remove: mut to_remove_extra } => {
+                        used_children += 1;
                         unused_spans.append(&mut spans);
                         to_remove.append(&mut to_remove_extra);
                         None
@@ -326,7 +320,7 @@ fn calc_unused_spans(
                 if let Some(remove) = remove {
                     let remove_span = if nested.len() == 1 {
                         remove
-                    } else if pos == nested.len() - 1 || !all_nested_unused {
+                    } else if pos == nested.len() - 1 || used_children > 0 {
                         // Delete everything from the end of the last import, to delete the
                         // previous comma
                         nested[pos - 1].0.span.shrink_to_hi().to(use_tree.span)
@@ -344,14 +338,38 @@ 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() {
                 UnusedSpanResult::Used
-            } else if all_nested_unused {
-                UnusedSpanResult::NestedFullUnused(unused_spans, full_span)
+            } else if used_children == 0 {
+                UnusedSpanResult::Unused { spans: unused_spans, remove: full_span }
             } else {
-                UnusedSpanResult::NestedPartialUnused(unused_spans, to_remove)
+                // 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_children == 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 }
             }
         }
     }
@@ -374,10 +392,7 @@ impl Resolver<'_, '_> {
                                 MACRO_USE_EXTERN_CRATE,
                                 import.root_id,
                                 import.span,
-                                "deprecated `#[macro_use]` attribute used to \
-                                import macros should be replaced at use sites \
-                                with a `use` item to import the macro \
-                                instead",
+                                BuiltinLintDiag::MacroUseDeprecated,
                             );
                         }
                     }
@@ -394,8 +409,12 @@ impl Resolver<'_, '_> {
                     }
                 }
                 ImportKind::MacroUse { .. } => {
-                    let msg = "unused `#[macro_use]` import";
-                    self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg);
+                    self.lint_buffer.buffer_lint(
+                        UNUSED_IMPORTS,
+                        import.root_id,
+                        import.span,
+                        BuiltinLintDiag::UnusedMacroUse,
+                    );
                 }
                 _ => {}
             }
@@ -414,24 +433,12 @@ impl Resolver<'_, '_> {
         visitor.report_unused_extern_crate_items(maybe_unused_extern_crates);
 
         for unused in visitor.unused_imports.values() {
-            let mut fixes = Vec::new();
-            let spans = match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) {
-                UnusedSpanResult::Used => continue,
-                UnusedSpanResult::FlatUnused(span, remove) => {
-                    fixes.push((remove, String::new()));
-                    vec![span]
-                }
-                UnusedSpanResult::NestedFullUnused(spans, remove) => {
-                    fixes.push((remove, String::new()));
-                    spans
-                }
-                UnusedSpanResult::NestedPartialUnused(spans, remove) => {
-                    for fix in &remove {
-                        fixes.push((*fix, String::new()));
-                    }
-                    spans
-                }
-            };
+            let (spans, remove_spans) =
+                match calc_unused_spans(unused, &unused.use_tree, unused.use_tree_id) {
+                    UnusedSpanResult::Used => continue,
+                    UnusedSpanResult::Unused { spans, remove } => (spans, vec![remove]),
+                    UnusedSpanResult::PartialUnused { spans, remove } => (spans, remove),
+                };
 
             let ms = MultiSpan::from_spans(spans);
 
@@ -443,23 +450,8 @@ impl Resolver<'_, '_> {
                 .collect::<Vec<String>>();
             span_snippets.sort();
 
-            let msg = format!(
-                "unused import{}{}",
-                pluralize!(ms.primary_spans().len()),
-                if !span_snippets.is_empty() {
-                    format!(": {}", span_snippets.join(", "))
-                } else {
-                    String::new()
-                }
-            );
-
-            let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span {
-                "remove the whole `use` item"
-            } else if ms.primary_spans().len() > 1 {
-                "remove the unused imports"
-            } else {
-                "remove the unused import"
-            };
+            let remove_whole_use = remove_spans.len() == 1 && remove_spans[0] == unused.item_span;
+            let num_to_remove = ms.primary_spans().len();
 
             // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
             // attribute; however, if not, suggest adding the attribute. There is no way to
@@ -485,12 +477,17 @@ impl Resolver<'_, '_> {
                 }
             };
 
-            visitor.r.lint_buffer.buffer_lint_with_diagnostic(
+            visitor.r.lint_buffer.buffer_lint(
                 UNUSED_IMPORTS,
                 unused.use_tree_id,
                 ms,
-                msg,
-                BuiltinLintDiag::UnusedImports(fix_msg.into(), fixes, test_module_span),
+                BuiltinLintDiag::UnusedImports {
+                    remove_whole_use,
+                    num_to_remove,
+                    remove_spans,
+                    test_module_span,
+                    span_snippets,
+                },
             );
         }
 
@@ -536,11 +533,10 @@ impl Resolver<'_, '_> {
                 continue;
             }
 
-            self.lint_buffer.buffer_lint_with_diagnostic(
+            self.lint_buffer.buffer_lint(
                 UNUSED_QUALIFICATIONS,
                 unn_qua.node_id,
                 unn_qua.path_span,
-                "unnecessary qualification",
                 BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span },
             );
         }