about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs9
-rw-r--r--src/librustc_resolve/lib.rs275
-rw-r--r--src/librustc_resolve/resolve_imports.rs28
-rw-r--r--src/libsyntax/ast.rs7
-rw-r--r--src/test/ui/double-type-import.stderr9
-rw-r--r--src/test/ui/error-codes/E0430.stderr10
-rw-r--r--src/test/ui/imports/duplicate.stderr9
-rw-r--r--src/test/ui/issues/auxiliary/issue-52891.rs33
-rw-r--r--src/test/ui/issues/issue-26886.stderr18
-rw-r--r--src/test/ui/issues/issue-45829/import-twice.stderr10
-rw-r--r--src/test/ui/issues/issue-52891.fixed37
-rw-r--r--src/test/ui/issues/issue-52891.rs38
-rw-r--r--src/test/ui/issues/issue-52891.stderr145
-rw-r--r--src/test/ui/proc-macro/shadow.stderr17
-rw-r--r--src/test/ui/resolve/resolve-conflict-import-vs-import.stderr9
-rw-r--r--src/test/ui/unresolved/unresolved-extern-mod-suggestion.stderr9
-rw-r--r--src/test/ui/use/use-mod.stderr9
-rw-r--r--src/test/ui/use/use-paths-as-items.stderr9
18 files changed, 570 insertions, 111 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 3752d953f8a..c5401ac3f55 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -238,12 +238,14 @@ impl<'a> Resolver<'a> {
                         macro_ns: Cell::new(None),
                     },
                     type_ns_only,
+                    nested,
                 };
                 self.add_import_directive(
                     module_path,
                     subclass,
                     use_tree.span,
                     id,
+                    item,
                     root_span,
                     item.id,
                     vis,
@@ -260,6 +262,7 @@ impl<'a> Resolver<'a> {
                     subclass,
                     use_tree.span,
                     id,
+                    item,
                     root_span,
                     item.id,
                     vis,
@@ -379,6 +382,9 @@ impl<'a> Resolver<'a> {
                         source: orig_name,
                         target: ident,
                     },
+                    has_attributes: !item.attrs.is_empty(),
+                    use_span_with_attributes: item.span_with_attributes(),
+                    use_span: item.span,
                     root_span: item.span,
                     span: item.span,
                     module_path: Vec::new(),
@@ -824,6 +830,9 @@ impl<'a> Resolver<'a> {
             parent_scope: parent_scope.clone(),
             imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
             subclass: ImportDirectiveSubclass::MacroUse,
+            use_span_with_attributes: item.span_with_attributes(),
+            has_attributes: !item.attrs.is_empty(),
+            use_span: item.span,
             root_span: span,
             span,
             module_path: Vec::new(),
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 55d5cdedd6d..3973bc2ad62 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -63,7 +63,7 @@ use syntax::ast::{Label, Local, Mutability, Pat, PatKind, Path};
 use syntax::ast::{QSelf, TraitItemKind, TraitRef, Ty, TyKind};
 use syntax::ptr::P;
 
-use syntax_pos::{Span, DUMMY_SP, MultiSpan};
+use syntax_pos::{BytePos, Span, DUMMY_SP, MultiSpan};
 use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
 
 use std::cell::{Cell, RefCell};
@@ -1228,6 +1228,16 @@ enum NameBindingKind<'a> {
     },
 }
 
+impl<'a> NameBindingKind<'a> {
+    /// Is this a name binding of a import?
+    fn is_import(&self) -> bool {
+        match *self {
+            NameBindingKind::Import { .. } => true,
+            _ => false,
+        }
+    }
+}
+
 struct PrivacyError<'a>(Span, Ident, &'a NameBinding<'a>);
 
 struct UseError<'a> {
@@ -5134,64 +5144,235 @@ impl<'a> Resolver<'a> {
         );
 
         // See https://github.com/rust-lang/rust/issues/32354
+        use NameBindingKind::Import;
         let directive = match (&new_binding.kind, &old_binding.kind) {
-            (NameBindingKind::Import { directive, .. }, _) if !new_binding.span.is_dummy() =>
-                Some((directive, new_binding.span)),
-            (_, NameBindingKind::Import { directive, .. }) if !old_binding.span.is_dummy() =>
-                Some((directive, old_binding.span)),
+            // If there are two imports where one or both have attributes then prefer removing the
+            // import without attributes.
+            (Import { directive: new, .. }, Import { directive: old, .. }) if {
+                !new_binding.span.is_dummy() && !old_binding.span.is_dummy() &&
+                    (new.has_attributes || old.has_attributes)
+            } => {
+                if old.has_attributes {
+                    Some((new, new_binding.span, true))
+                } else {
+                    Some((old, old_binding.span, true))
+                }
+            },
+            // Otherwise prioritize the new binding.
+            (Import { directive, .. }, other) if !new_binding.span.is_dummy() =>
+                Some((directive, new_binding.span, other.is_import())),
+            (other, Import { directive, .. }) if !old_binding.span.is_dummy() =>
+                Some((directive, old_binding.span, other.is_import())),
             _ => None,
         };
-        if let Some((directive, binding_span)) = directive {
-            let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
-                format!("Other{}", name)
-            } else {
-                format!("other_{}", name)
-            };
 
-            let mut suggestion = None;
-            match directive.subclass {
-                ImportDirectiveSubclass::SingleImport { type_ns_only: true, .. } =>
-                    suggestion = Some(format!("self as {}", suggested_name)),
-                ImportDirectiveSubclass::SingleImport { source, .. } => {
-                    if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0)
-                                                         .map(|pos| pos as usize) {
-                        if let Ok(snippet) = self.session.source_map()
-                                                         .span_to_snippet(binding_span) {
-                            if pos <= snippet.len() {
-                                suggestion = Some(format!(
-                                    "{} as {}{}",
-                                    &snippet[..pos],
-                                    suggested_name,
-                                    if snippet.ends_with(";") { ";" } else { "" }
-                                ))
-                            }
+        // Check if the target of the use for both bindings is the same.
+        let duplicate = new_binding.def().opt_def_id() == old_binding.def().opt_def_id();
+        let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();
+        let from_item = self.extern_prelude.get(&ident)
+            .map(|entry| entry.introduced_by_item)
+            .unwrap_or(true);
+        // Only suggest removing an import if both bindings are to the same def, if both spans
+        // aren't dummy spans. Further, if both bindings are imports, then the ident must have
+        // been introduced by a item.
+        let should_remove_import = duplicate && !has_dummy_span &&
+            ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item);
+
+        match directive {
+            Some((directive, span, true)) if should_remove_import && directive.is_nested() =>
+                self.add_suggestion_for_duplicate_nested_use(&mut err, directive, span),
+            Some((directive, _, true)) if should_remove_import && !directive.is_glob() => {
+                // Simple case - remove the entire import. Due to the above match arm, this can
+                // only be a single use so just remove it entirely.
+                err.span_suggestion(
+                    directive.use_span_with_attributes,
+                    "remove unnecessary import",
+                    String::new(),
+                    Applicability::MaybeIncorrect,
+                );
+            },
+            Some((directive, span, _)) =>
+                self.add_suggestion_for_rename_of_use(&mut err, name, directive, span),
+            _ => {},
+        }
+
+        err.emit();
+        self.name_already_seen.insert(name, span);
+    }
+
+    /// This function adds a suggestion to change the binding name of a new import that conflicts
+    /// with an existing import.
+    ///
+    /// ```ignore (diagnostic)
+    /// help: you can use `as` to change the binding name of the import
+    ///    |
+    /// LL | use foo::bar as other_bar;
+    ///    |     ^^^^^^^^^^^^^^^^^^^^^
+    /// ```
+    fn add_suggestion_for_rename_of_use(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        name: Symbol,
+        directive: &ImportDirective<'_>,
+        binding_span: Span,
+    ) {
+        let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
+            format!("Other{}", name)
+        } else {
+            format!("other_{}", name)
+        };
+
+        let mut suggestion = None;
+        match directive.subclass {
+            ImportDirectiveSubclass::SingleImport { type_ns_only: true, .. } =>
+                suggestion = Some(format!("self as {}", suggested_name)),
+            ImportDirectiveSubclass::SingleImport { source, .. } => {
+                if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0)
+                                                     .map(|pos| pos as usize) {
+                    if let Ok(snippet) = self.session.source_map()
+                                                     .span_to_snippet(binding_span) {
+                        if pos <= snippet.len() {
+                            suggestion = Some(format!(
+                                "{} as {}{}",
+                                &snippet[..pos],
+                                suggested_name,
+                                if snippet.ends_with(";") { ";" } else { "" }
+                            ))
                         }
                     }
                 }
-                ImportDirectiveSubclass::ExternCrate { source, target, .. } =>
-                    suggestion = Some(format!(
-                        "extern crate {} as {};",
-                        source.unwrap_or(target.name),
-                        suggested_name,
-                    )),
-                _ => unreachable!(),
             }
+            ImportDirectiveSubclass::ExternCrate { source, target, .. } =>
+                suggestion = Some(format!(
+                    "extern crate {} as {};",
+                    source.unwrap_or(target.name),
+                    suggested_name,
+                )),
+            _ => unreachable!(),
+        }
+
+        let rename_msg = "you can use `as` to change the binding name of the import";
+        if let Some(suggestion) = suggestion {
+            err.span_suggestion(
+                binding_span,
+                rename_msg,
+                suggestion,
+                Applicability::MaybeIncorrect,
+            );
+        } else {
+            err.span_label(binding_span, rename_msg);
+        }
+    }
 
-            let rename_msg = "you can use `as` to change the binding name of the import";
-            if let Some(suggestion) = suggestion {
-                err.span_suggestion(
-                    binding_span,
-                    rename_msg,
-                    suggestion,
-                    Applicability::MaybeIncorrect,
-                );
-            } else {
-                err.span_label(binding_span, rename_msg);
+    /// This function adds a suggestion to remove a unnecessary binding from an import that is
+    /// nested. In the following example, this function will be invoked to remove the `a` binding
+    /// in the second use statement:
+    ///
+    /// ```ignore (diagnostic)
+    /// use issue_52891::a;
+    /// use issue_52891::{d, a, e};
+    /// ```
+    ///
+    /// The following suggestion will be added:
+    ///
+    /// ```ignore (diagnostic)
+    /// use issue_52891::{d, a, e};
+    ///                      ^-- help: remove unnecessary import
+    /// ```
+    ///
+    /// If the nested use contains only one import then the suggestion will remove the entire
+    /// line.
+    ///
+    /// It is expected that the directive provided is a nested import - this isn't checked by the
+    /// function. If this invariant is not upheld, this function's behaviour will be unexpected
+    /// as characters expected by span manipulations won't be present.
+    fn add_suggestion_for_duplicate_nested_use(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        directive: &ImportDirective<'_>,
+        binding_span: Span,
+    ) {
+        assert!(directive.is_nested());
+        let message = "remove unnecessary import";
+        let source_map = self.session.source_map();
+
+        // Two examples will be used to illustrate the span manipulations we're doing:
+        //
+        // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is
+        //   `a` and `directive.use_span` is `issue_52891::{d, a, e};`.
+        // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is
+        //   `a` and `directive.use_span` is `issue_52891::{d, e, a};`.
+
+        // Find the span of everything after the binding.
+        //   ie. `a, e};` or `a};`
+        let binding_until_end = binding_span.with_hi(directive.use_span.hi());
+
+        // Find everything after the binding but not including the binding.
+        //   ie. `, e};` or `};`
+        let after_binding_until_end = binding_until_end.with_lo(binding_span.hi());
+
+        // Keep characters in the span until we encounter something that isn't a comma or
+        // whitespace.
+        //   ie. `, ` or ``.
+        //
+        // Also note whether a closing brace character was encountered. If there
+        // was, then later go backwards to remove any trailing commas that are left.
+        let mut found_closing_brace = false;
+        let after_binding_until_next_binding = source_map.span_take_while(
+            after_binding_until_end,
+            |&ch| {
+                if ch == '}' { found_closing_brace = true; }
+                ch == ' ' || ch == ','
+            }
+        );
+
+        // Combine the two spans.
+        //   ie. `a, ` or `a`.
+        //
+        // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };`
+        let span = binding_span.with_hi(after_binding_until_next_binding.hi());
+
+        // If there was a closing brace then identify the span to remove any trailing commas from
+        // previous imports.
+        if found_closing_brace {
+            if let Ok(prev_source) = source_map.span_to_prev_source(span) {
+                // `prev_source` will contain all of the source that came before the span.
+                // Then split based on a command and take the first (ie. closest to our span)
+                // snippet. In the example, this is a space.
+                let prev_comma = prev_source.rsplit(',').collect::<Vec<_>>();
+                let prev_starting_brace = prev_source.rsplit('{').collect::<Vec<_>>();
+                if prev_comma.len() > 1 && prev_starting_brace.len() > 1 {
+                    let prev_comma = prev_comma.first().unwrap();
+                    let prev_starting_brace = prev_starting_brace.first().unwrap();
+
+                    // If the amount of source code before the comma is greater than
+                    // the amount of source code before the starting brace then we've only
+                    // got one item in the nested item (eg. `issue_52891::{self}`).
+                    if prev_comma.len() > prev_starting_brace.len() {
+                        // So just remove the entire line...
+                        err.span_suggestion(
+                            directive.use_span_with_attributes,
+                            message,
+                            String::new(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        return;
+                    }
+
+                    let span = span.with_lo(BytePos(
+                        // Take away the number of bytes for the characters we've found and an
+                        // extra for the comma.
+                        span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1
+                    ));
+                    err.span_suggestion(
+                        span, message, String::new(), Applicability::MaybeIncorrect,
+                    );
+                    return;
+                }
             }
         }
 
-        err.emit();
-        self.name_already_seen.insert(name, span);
+        err.span_suggestion(span, message, String::new(), Applicability::MachineApplicable);
     }
 
     fn extern_prelude_get(&mut self, ident: Ident, speculative: bool)
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index fd55897522b..5105c80fcbc 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -18,7 +18,7 @@ use rustc::hir::def::*;
 use rustc::session::DiagnosticMessageId;
 use rustc::util::nodemap::FxHashSet;
 
-use syntax::ast::{Ident, Name, NodeId, CRATE_NODE_ID};
+use syntax::ast::{self, Ident, Name, NodeId, CRATE_NODE_ID};
 use syntax::ext::base::Determinacy::{self, Determined, Undetermined};
 use syntax::ext::hygiene::Mark;
 use syntax::symbol::keywords;
@@ -42,6 +42,8 @@ pub enum ImportDirectiveSubclass<'a> {
         target_bindings: PerNS<Cell<Option<&'a NameBinding<'a>>>>,
         /// `true` for `...::{self [as target]}` imports, `false` otherwise.
         type_ns_only: bool,
+        /// Did this import result from a nested import? ie. `use foo::{bar, baz};`
+        nested: bool,
     },
     GlobImport {
         is_prelude: bool,
@@ -78,6 +80,15 @@ crate struct ImportDirective<'a> {
     /// `UseTree` node.
     pub root_id: NodeId,
 
+    /// Span of the entire use statement.
+    pub use_span: Span,
+
+    /// Span of the entire use statement with attributes.
+    pub use_span_with_attributes: Span,
+
+    /// Did the use statement have any attributes?
+    pub has_attributes: bool,
+
     /// Span of this use tree.
     pub span: Span,
 
@@ -98,6 +109,13 @@ impl<'a> ImportDirective<'a> {
         match self.subclass { ImportDirectiveSubclass::GlobImport { .. } => true, _ => false }
     }
 
+    pub fn is_nested(&self) -> bool {
+        match self.subclass {
+            ImportDirectiveSubclass::SingleImport { nested, .. } => nested,
+            _ => false
+        }
+    }
+
     crate fn crate_lint(&self) -> CrateLint {
         CrateLint::UsePath { root_id: self.root_id, root_span: self.root_span }
     }
@@ -390,6 +408,7 @@ impl<'a> Resolver<'a> {
                                 subclass: ImportDirectiveSubclass<'a>,
                                 span: Span,
                                 id: NodeId,
+                                item: &ast::Item,
                                 root_span: Span,
                                 root_id: NodeId,
                                 vis: ty::Visibility,
@@ -402,6 +421,9 @@ impl<'a> Resolver<'a> {
             subclass,
             span,
             id,
+            use_span: item.span,
+            use_span_with_attributes: item.span_with_attributes(),
+            has_attributes: !item.attrs.is_empty(),
             root_span,
             root_id,
             vis: Cell::new(vis),
@@ -787,7 +809,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         let (source, target, source_bindings, target_bindings, type_ns_only) =
                 match directive.subclass {
             SingleImport { source, target, ref source_bindings,
-                           ref target_bindings, type_ns_only } =>
+                           ref target_bindings, type_ns_only, .. } =>
                 (source, target, source_bindings, target_bindings, type_ns_only),
             GlobImport { .. } => {
                 self.resolve_glob_import(directive);
@@ -908,7 +930,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         let (ident, target, source_bindings, target_bindings, type_ns_only) =
                 match directive.subclass {
             SingleImport { source, target, ref source_bindings,
-                           ref target_bindings, type_ns_only } =>
+                           ref target_bindings, type_ns_only, .. } =>
                 (source, target, source_bindings, target_bindings, type_ns_only),
             GlobImport { is_prelude, ref max_vis } => {
                 if directive.module_path.len() <= 1 {
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index af521848e90..4f3f5631cc3 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -2164,6 +2164,13 @@ pub struct Item {
     pub tokens: Option<TokenStream>,
 }
 
+impl Item {
+    /// Return the span that encompasses the attributes.
+    pub fn span_with_attributes(&self) -> Span {
+        self.attrs.iter().fold(self.span, |acc, attr| acc.to(attr.span()))
+    }
+}
+
 /// A function header.
 ///
 /// All the information between the visibility and the name of the function is
diff --git a/src/test/ui/double-type-import.stderr b/src/test/ui/double-type-import.stderr
index 4bdee0c4c9f..c7288af13c2 100644
--- a/src/test/ui/double-type-import.stderr
+++ b/src/test/ui/double-type-import.stderr
@@ -4,13 +4,12 @@ error[E0252]: the name `X` is defined multiple times
 LL |     pub use self::bar::X;
    |             ------------ previous import of the type `X` here
 LL |     use self::bar::X;
-   |         ^^^^^^^^^^^^ `X` reimported here
+   |     ----^^^^^^^^^^^^-
+   |     |   |
+   |     |   `X` reimported here
+   |     help: remove unnecessary import
    |
    = note: `X` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL |     use self::bar::X as OtherX;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0430.stderr b/src/test/ui/error-codes/E0430.stderr
index 0151cde887f..78e4e43ac2f 100644
--- a/src/test/ui/error-codes/E0430.stderr
+++ b/src/test/ui/error-codes/E0430.stderr
@@ -10,15 +10,13 @@ error[E0252]: the name `fmt` is defined multiple times
   --> $DIR/E0430.rs:1:22
    |
 LL | use std::fmt::{self, self}; //~ ERROR E0430
-   |                ----  ^^^^ `fmt` reimported here
-   |                |
+   |                ------^^^^
+   |                |   | |
+   |                |   | `fmt` reimported here
+   |                |   help: remove unnecessary import
    |                previous import of the module `fmt` here
    |
    = note: `fmt` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use std::fmt::{self, self as other_fmt}; //~ ERROR E0430
-   |                      ^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/imports/duplicate.stderr b/src/test/ui/imports/duplicate.stderr
index acd66826fdf..29660d908e4 100644
--- a/src/test/ui/imports/duplicate.stderr
+++ b/src/test/ui/imports/duplicate.stderr
@@ -4,13 +4,12 @@ error[E0252]: the name `foo` is defined multiple times
 LL |     use a::foo;
    |         ------ previous import of the value `foo` here
 LL |     use a::foo; //~ ERROR the name `foo` is defined multiple times
-   |         ^^^^^^ `foo` reimported here
+   |     ----^^^^^^-
+   |     |   |
+   |     |   `foo` reimported here
+   |     help: remove unnecessary import
    |
    = note: `foo` must be defined only once in the value namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL |     use a::foo as other_foo; //~ ERROR the name `foo` is defined multiple times
-   |         ^^^^^^^^^^^^^^^^^^^
 
 error[E0659]: `foo` is ambiguous (glob import vs glob import in the same module)
   --> $DIR/duplicate.rs:46:15
diff --git a/src/test/ui/issues/auxiliary/issue-52891.rs b/src/test/ui/issues/auxiliary/issue-52891.rs
new file mode 100644
index 00000000000..07598118322
--- /dev/null
+++ b/src/test/ui/issues/auxiliary/issue-52891.rs
@@ -0,0 +1,33 @@
+pub mod a {
+    pub mod inner {
+    }
+}
+
+pub mod b {
+    pub mod inner {
+    }
+}
+
+pub mod c {}
+
+pub mod d {}
+
+pub mod e {}
+
+pub mod f {}
+
+pub mod g {}
+
+pub mod h {}
+
+pub mod i {}
+
+pub mod j {}
+
+pub mod k {}
+
+pub mod l {}
+
+pub mod m {}
+
+pub mod n {}
diff --git a/src/test/ui/issues/issue-26886.stderr b/src/test/ui/issues/issue-26886.stderr
index 08faa9c9ca2..70dacb353fe 100644
--- a/src/test/ui/issues/issue-26886.stderr
+++ b/src/test/ui/issues/issue-26886.stderr
@@ -4,13 +4,12 @@ error[E0252]: the name `Arc` is defined multiple times
 LL | use std::sync::{self, Arc};
    |                       --- previous import of the type `Arc` here
 LL | use std::sync::Arc; //~ ERROR the name `Arc` is defined multiple times
-   |     ^^^^^^^^^^^^^^ `Arc` reimported here
+   | ----^^^^^^^^^^^^^^-
+   | |   |
+   | |   `Arc` reimported here
+   | help: remove unnecessary import
    |
    = note: `Arc` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use std::sync::Arc as OtherArc; //~ ERROR the name `Arc` is defined multiple times
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0252]: the name `sync` is defined multiple times
   --> $DIR/issue-26886.rs:4:5
@@ -19,13 +18,12 @@ LL | use std::sync::{self, Arc};
    |                 ---- previous import of the module `sync` here
 ...
 LL | use std::sync; //~ ERROR the name `sync` is defined multiple times
-   |     ^^^^^^^^^ `sync` reimported here
+   | ----^^^^^^^^^-
+   | |   |
+   | |   `sync` reimported here
+   | help: remove unnecessary import
    |
    = note: `sync` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use std::sync as other_sync; //~ ERROR the name `sync` is defined multiple times
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-45829/import-twice.stderr b/src/test/ui/issues/issue-45829/import-twice.stderr
index 374b809647e..2a1ac576511 100644
--- a/src/test/ui/issues/issue-45829/import-twice.stderr
+++ b/src/test/ui/issues/issue-45829/import-twice.stderr
@@ -2,15 +2,13 @@ error[E0252]: the name `A` is defined multiple times
   --> $DIR/import-twice.rs:6:14
    |
 LL | use foo::{A, A};
-   |           -  ^ `A` reimported here
-   |           |
+   |           ---^
+   |           || |
+   |           || `A` reimported here
+   |           |help: remove unnecessary import
    |           previous import of the type `A` here
    |
    = note: `A` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use foo::{A, A as OtherA};
-   |              ^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-52891.fixed b/src/test/ui/issues/issue-52891.fixed
new file mode 100644
index 00000000000..e694b5c9b15
--- /dev/null
+++ b/src/test/ui/issues/issue-52891.fixed
@@ -0,0 +1,37 @@
+// aux-build:issue-52891.rs
+// run-rustfix
+
+#![allow(warnings)]
+
+extern crate issue_52891;
+
+// Check that we don't suggest renaming duplicate imports but instead
+// suggest removing one.
+
+use issue_52891::a;
+ //~ ERROR `a` is defined multiple times
+
+use issue_52891::{b, c}; //~ ERROR `a` is defined multiple times
+use issue_52891::{d, e}; //~ ERROR `a` is defined multiple times
+use issue_52891::{f, g}; //~ ERROR `a` is defined multiple times
+
+use issue_52891::{//~ ERROR `a` is defined multiple times
+    h,
+    i};
+use issue_52891::{j,
+    //~ ERROR `a` is defined multiple times
+    k};
+use issue_52891::{l,
+    m}; //~ ERROR `a` is defined multiple times
+
+use issue_52891::a::inner;
+use issue_52891::b::inner as other_inner; //~ ERROR `inner` is defined multiple times
+
+
+//~^ ERROR `issue_52891` is defined multiple times
+
+
+#[macro_use]
+use issue_52891::n; //~ ERROR `n` is defined multiple times
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-52891.rs b/src/test/ui/issues/issue-52891.rs
new file mode 100644
index 00000000000..cd4b40629ab
--- /dev/null
+++ b/src/test/ui/issues/issue-52891.rs
@@ -0,0 +1,38 @@
+// aux-build:issue-52891.rs
+// run-rustfix
+
+#![allow(warnings)]
+
+extern crate issue_52891;
+
+// Check that we don't suggest renaming duplicate imports but instead
+// suggest removing one.
+
+use issue_52891::a;
+use issue_52891::a; //~ ERROR `a` is defined multiple times
+
+use issue_52891::{a, b, c}; //~ ERROR `a` is defined multiple times
+use issue_52891::{d, a, e}; //~ ERROR `a` is defined multiple times
+use issue_52891::{f, g, a}; //~ ERROR `a` is defined multiple times
+
+use issue_52891::{a, //~ ERROR `a` is defined multiple times
+    h,
+    i};
+use issue_52891::{j,
+    a, //~ ERROR `a` is defined multiple times
+    k};
+use issue_52891::{l,
+    m,
+    a}; //~ ERROR `a` is defined multiple times
+
+use issue_52891::a::inner;
+use issue_52891::b::inner; //~ ERROR `inner` is defined multiple times
+
+use issue_52891::{self};
+//~^ ERROR `issue_52891` is defined multiple times
+
+use issue_52891::n;
+#[macro_use]
+use issue_52891::n; //~ ERROR `n` is defined multiple times
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-52891.stderr b/src/test/ui/issues/issue-52891.stderr
new file mode 100644
index 00000000000..55d611070d9
--- /dev/null
+++ b/src/test/ui/issues/issue-52891.stderr
@@ -0,0 +1,145 @@
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:12:5
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+LL | use issue_52891::a; //~ ERROR `a` is defined multiple times
+   | ----^^^^^^^^^^^^^^-
+   | |   |
+   | |   `a` reimported here
+   | help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:14:19
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+...
+LL | use issue_52891::{a, b, c}; //~ ERROR `a` is defined multiple times
+   |                   ^--
+   |                   |
+   |                   `a` reimported here
+   |                   help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:15:22
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+...
+LL | use issue_52891::{d, a, e}; //~ ERROR `a` is defined multiple times
+   |                      ^--
+   |                      |
+   |                      `a` reimported here
+   |                      help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:16:25
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+...
+LL | use issue_52891::{f, g, a}; //~ ERROR `a` is defined multiple times
+   |                       --^
+   |                       | |
+   |                       | `a` reimported here
+   |                       help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:18:19
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+...
+LL | use issue_52891::{a, //~ ERROR `a` is defined multiple times
+   |                   ^--
+   |                   |
+   |                   `a` reimported here
+   |                   help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:22:5
+   |
+LL | use issue_52891::a;
+   |     -------------- previous import of the module `a` here
+...
+LL |     a, //~ ERROR `a` is defined multiple times
+   |     ^--
+   |     |
+   |     `a` reimported here
+   |     help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `a` is defined multiple times
+  --> $DIR/issue-52891.rs:26:5
+   |
+LL |   use issue_52891::a;
+   |       -------------- previous import of the module `a` here
+...
+LL |       m,
+   |  ______-
+LL | |     a}; //~ ERROR `a` is defined multiple times
+   | |     -
+   | |     |
+   | |_____`a` reimported here
+   |       help: remove unnecessary import
+   |
+   = note: `a` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `inner` is defined multiple times
+  --> $DIR/issue-52891.rs:29:5
+   |
+LL | use issue_52891::a::inner;
+   |     --------------------- previous import of the module `inner` here
+LL | use issue_52891::b::inner; //~ ERROR `inner` is defined multiple times
+   |     ^^^^^^^^^^^^^^^^^^^^^ `inner` reimported here
+   |
+   = note: `inner` must be defined only once in the type namespace of this module
+help: you can use `as` to change the binding name of the import
+   |
+LL | use issue_52891::b::inner as other_inner; //~ ERROR `inner` is defined multiple times
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0254]: the name `issue_52891` is defined multiple times
+  --> $DIR/issue-52891.rs:31:19
+   |
+LL | extern crate issue_52891;
+   | ------------------------- previous import of the extern crate `issue_52891` here
+...
+LL | use issue_52891::{self};
+   | ------------------^^^^--
+   | |                 |
+   | |                 `issue_52891` reimported here
+   | help: remove unnecessary import
+   |
+   = note: `issue_52891` must be defined only once in the type namespace of this module
+
+error[E0252]: the name `n` is defined multiple times
+  --> $DIR/issue-52891.rs:36:5
+   |
+LL | use issue_52891::n;
+   | -------------------
+   | |   |
+   | |   previous import of the module `n` here
+   | help: remove unnecessary import
+LL | #[macro_use]
+LL | use issue_52891::n; //~ ERROR `n` is defined multiple times
+   |     ^^^^^^^^^^^^^^ `n` reimported here
+   |
+   = note: `n` must be defined only once in the type namespace of this module
+
+error: aborting due to 10 previous errors
+
+Some errors occurred: E0252, E0254.
+For more information about an error, try `rustc --explain E0252`.
diff --git a/src/test/ui/proc-macro/shadow.stderr b/src/test/ui/proc-macro/shadow.stderr
index 103c78126a8..2dfe2a94c88 100644
--- a/src/test/ui/proc-macro/shadow.stderr
+++ b/src/test/ui/proc-macro/shadow.stderr
@@ -1,17 +1,16 @@
 error[E0259]: the name `derive_a` is defined multiple times
   --> $DIR/shadow.rs:6:1
    |
-LL | extern crate derive_a;
-   | ---------------------- previous import of the extern crate `derive_a` here
-LL | #[macro_use]
-LL | extern crate derive_a; //~ ERROR the name `derive_a` is defined multiple times
-   | ^^^^^^^^^^^^^^^^^^^^^^ `derive_a` reimported here
+LL |   extern crate derive_a;
+   |   ---------------------- previous import of the extern crate `derive_a` here
+LL | / #[macro_use]
+LL | | extern crate derive_a; //~ ERROR the name `derive_a` is defined multiple times
+   | | ^^^^^^^^^^^^^^^^^^^^^-
+   | |_|____________________|
+   |   |                    help: remove unnecessary import
+   |   `derive_a` reimported here
    |
    = note: `derive_a` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | extern crate derive_a as other_derive_a; //~ ERROR the name `derive_a` is defined multiple times
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/resolve/resolve-conflict-import-vs-import.stderr b/src/test/ui/resolve/resolve-conflict-import-vs-import.stderr
index 2852d122e5e..1b4b058b783 100644
--- a/src/test/ui/resolve/resolve-conflict-import-vs-import.stderr
+++ b/src/test/ui/resolve/resolve-conflict-import-vs-import.stderr
@@ -4,13 +4,12 @@ error[E0252]: the name `transmute` is defined multiple times
 LL | use std::mem::transmute;
    |     ------------------- previous import of the value `transmute` here
 LL | use std::mem::transmute;
-   |     ^^^^^^^^^^^^^^^^^^^ `transmute` reimported here
+   | ----^^^^^^^^^^^^^^^^^^^-
+   | |   |
+   | |   `transmute` reimported here
+   | help: remove unnecessary import
    |
    = note: `transmute` must be defined only once in the value namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use std::mem::transmute as other_transmute;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/unresolved/unresolved-extern-mod-suggestion.stderr b/src/test/ui/unresolved/unresolved-extern-mod-suggestion.stderr
index 012d3c3a751..e8679a3726d 100644
--- a/src/test/ui/unresolved/unresolved-extern-mod-suggestion.stderr
+++ b/src/test/ui/unresolved/unresolved-extern-mod-suggestion.stderr
@@ -4,13 +4,12 @@ error[E0254]: the name `core` is defined multiple times
 LL | extern crate core;
    | ------------------ previous import of the extern crate `core` here
 LL | use core;
-   |     ^^^^ `core` reimported here
+   | ----^^^^-
+   | |   |
+   | |   `core` reimported here
+   | help: remove unnecessary import
    |
    = note: `core` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use core as other_core;
-   |     ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/use/use-mod.stderr b/src/test/ui/use/use-mod.stderr
index 9ab873b3598..c23ab34eae6 100644
--- a/src/test/ui/use/use-mod.stderr
+++ b/src/test/ui/use/use-mod.stderr
@@ -20,13 +20,12 @@ LL |     self,
    |     ---- previous import of the module `bar` here
 ...
 LL |     self
-   |     ^^^^ `bar` reimported here
+   |     ^^^^
+   |     |
+   |     `bar` reimported here
+   |     help: remove unnecessary import
    |
    = note: `bar` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL |     self as other_bar
-   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/use/use-paths-as-items.stderr b/src/test/ui/use/use-paths-as-items.stderr
index 5bcdf937d56..00f468cdf31 100644
--- a/src/test/ui/use/use-paths-as-items.stderr
+++ b/src/test/ui/use/use-paths-as-items.stderr
@@ -4,13 +4,12 @@ error[E0252]: the name `mem` is defined multiple times
 LL | use std::{mem, ptr};
    |           --- previous import of the module `mem` here
 LL | use std::mem; //~ ERROR the name `mem` is defined multiple times
-   |     ^^^^^^^^ `mem` reimported here
+   | ----^^^^^^^^-
+   | |   |
+   | |   `mem` reimported here
+   | help: remove unnecessary import
    |
    = note: `mem` must be defined only once in the type namespace of this module
-help: you can use `as` to change the binding name of the import
-   |
-LL | use std::mem as other_mem; //~ ERROR the name `mem` is defined multiple times
-   |     ^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error