about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_resolve/lib.rs94
-rw-r--r--src/librustc_resolve/macros.rs17
-rw-r--r--src/librustc_resolve/resolve_imports.rs207
-rw-r--r--src/test/ui/extenv/issue-55897.stderr5
-rw-r--r--src/test/ui/import.rs4
-rw-r--r--src/test/ui/import.stderr9
-rw-r--r--src/test/ui/imports/issue-55457.stderr5
-rw-r--r--src/test/ui/inaccessible-test-modules.stderr10
-rw-r--r--src/test/ui/issues/issue-31212.stderr2
-rw-r--r--src/test/ui/issues/issue-8208.rs6
-rw-r--r--src/test/ui/issues/issue-8208.stderr6
-rw-r--r--src/test/ui/resolve_self_super_hint.rs12
-rw-r--r--src/test/ui/resolve_self_super_hint.stderr20
-rw-r--r--src/test/ui/rust-2018/issue-54006.stderr2
-rw-r--r--src/test/ui/rust-2018/local-path-suggestions-2015.stderr5
-rw-r--r--src/test/ui/rust-2018/local-path-suggestions-2018.stderr4
-rw-r--r--src/test/ui/unresolved/unresolved-import.rs20
-rw-r--r--src/test/ui/unresolved/unresolved-import.stderr29
-rw-r--r--src/test/ui/use/use-nested-groups-error.stderr5
19 files changed, 310 insertions, 152 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index e9331fcd8ba..b50e37519d4 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -137,6 +137,9 @@ impl Ord for BindingError {
     }
 }
 
+/// A span, message, replacement text, and applicability.
+type Suggestion = (Span, String, String, Applicability);
+
 enum ResolutionError<'a> {
     /// Error E0401: can't use type or const parameters from outer function.
     GenericParamsFromOuterFunction(Def),
@@ -166,7 +169,7 @@ enum ResolutionError<'a> {
     /// Error E0431: `self` import can only appear in an import list with a non-empty prefix.
     SelfImportOnlyInImportListWithNonEmptyPrefix,
     /// Error E0433: failed to resolve.
-    FailedToResolve(&'a str),
+    FailedToResolve { label: String, suggestion: Option<Suggestion> },
     /// Error E0434: can't capture dynamic environment in a fn item.
     CannotCaptureDynamicEnvironmentInFnItem,
     /// Error E0435: attempt to use a non-constant value in a constant.
@@ -380,10 +383,15 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver<'_>,
             err.span_label(span, "can only appear in an import list with a non-empty prefix");
             err
         }
-        ResolutionError::FailedToResolve(msg) => {
+        ResolutionError::FailedToResolve { label, suggestion } => {
             let mut err = struct_span_err!(resolver.session, span, E0433,
-                                           "failed to resolve: {}", msg);
-            err.span_label(span, msg);
+                                           "failed to resolve: {}", &label);
+            err.span_label(span, label);
+
+            if let Some((span, msg, suggestion, applicability)) = suggestion {
+                err.span_suggestion(span, &msg, suggestion, applicability);
+            }
+
             err
         }
         ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
@@ -1050,7 +1058,12 @@ enum PathResult<'a> {
     Module(ModuleOrUniformRoot<'a>),
     NonModule(PathResolution),
     Indeterminate,
-    Failed(Span, String, bool /* is the error from the last segment? */),
+    Failed {
+        span: Span,
+        label: String,
+        suggestion: Option<Suggestion>,
+        is_error_from_last_segment: bool,
+    },
 }
 
 enum ModuleKind {
@@ -1775,13 +1788,18 @@ impl<'a> Resolver<'a> {
             PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 =>
                 path_res.base_def(),
             PathResult::NonModule(..) => {
-                let msg = "type-relative paths are not supported in this context";
-                error_callback(self, span, ResolutionError::FailedToResolve(msg));
+                error_callback(self, span, ResolutionError::FailedToResolve {
+                    label: String::from("type-relative paths are not supported in this context"),
+                    suggestion: None,
+                });
                 Def::Err
             }
             PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
-            PathResult::Failed(span, msg, _) => {
-                error_callback(self, span, ResolutionError::FailedToResolve(&msg));
+            PathResult::Failed { span, label, suggestion, .. } => {
+                error_callback(self, span, ResolutionError::FailedToResolve {
+                    label,
+                    suggestion,
+                });
                 Def::Err
             }
         };
@@ -3429,7 +3447,7 @@ impl<'a> Resolver<'a> {
             // Such behavior is required for backward compatibility.
             // The same fallback is used when `a` resolves to nothing.
             PathResult::Module(ModuleOrUniformRoot::Module(_)) |
-            PathResult::Failed(..)
+            PathResult::Failed { .. }
                     if (ns == TypeNS || path.len() > 1) &&
                        self.primitive_type_table.primitive_types
                            .contains_key(&path[0].ident.name) => {
@@ -3438,11 +3456,11 @@ impl<'a> Resolver<'a> {
             }
             PathResult::Module(ModuleOrUniformRoot::Module(module)) =>
                 PathResolution::new(module.def().unwrap()),
-            PathResult::Failed(span, msg, false) => {
-                resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+            PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
+                resolve_error(self, span, ResolutionError::FailedToResolve { label, suggestion });
                 err_path_resolution()
             }
-            PathResult::Module(..) | PathResult::Failed(..) => return None,
+            PathResult::Module(..) | PathResult::Failed { .. } => return None,
             PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"),
         };
 
@@ -3550,7 +3568,12 @@ impl<'a> Resolver<'a> {
                         }
                     }
                     let msg = "there are too many initial `super`s.".to_string();
-                    return PathResult::Failed(ident.span, msg, false);
+                    return PathResult::Failed {
+                        span: ident.span,
+                        label: msg,
+                        suggestion: None,
+                        is_error_from_last_segment: false,
+                    };
                 }
                 if i == 0 {
                     if name == keywords::SelfLower.name() {
@@ -3587,12 +3610,17 @@ impl<'a> Resolver<'a> {
                 } else {
                     format!("`{}`", name)
                 };
-                let msg = if i == 1 && path[0].ident.name == keywords::PathRoot.name() {
+                let label = if i == 1 && path[0].ident.name == keywords::PathRoot.name() {
                     format!("global paths cannot start with {}", name_str)
                 } else {
                     format!("{} in paths can only be used in start position", name_str)
                 };
-                return PathResult::Failed(ident.span, msg, false);
+                return PathResult::Failed {
+                    span: ident.span,
+                    label,
+                    suggestion: None,
+                    is_error_from_last_segment: false,
+                };
             }
 
             let binding = if let Some(module) = module {
@@ -3653,9 +3681,12 @@ impl<'a> Resolver<'a> {
                             def, path.len() - i - 1
                         ));
                     } else {
-                        return PathResult::Failed(ident.span,
-                                                  format!("not a module `{}`", ident),
-                                                  is_last);
+                        return PathResult::Failed {
+                            span: ident.span,
+                            label: format!("not a module `{}`", ident),
+                            suggestion: None,
+                            is_error_from_last_segment: is_last,
+                        };
                     }
                 }
                 Err(Undetermined) => return PathResult::Indeterminate,
@@ -3671,7 +3702,7 @@ impl<'a> Resolver<'a> {
                         Some(ModuleOrUniformRoot::Module(module)) => module.def(),
                         _ => None,
                     };
-                    let msg = if module_def == self.graph_root.def() {
+                    let (label, suggestion) = if module_def == self.graph_root.def() {
                         let is_mod = |def| match def { Def::Mod(..) => true, _ => false };
                         let mut candidates =
                             self.lookup_import_candidates(ident, TypeNS, is_mod);
@@ -3679,19 +3710,32 @@ impl<'a> Resolver<'a> {
                             (c.path.segments.len(), c.path.to_string())
                         });
                         if let Some(candidate) = candidates.get(0) {
-                            format!("did you mean `{}`?", candidate.path)
+                            (
+                                String::from("unresolved import"),
+                                Some((
+                                    ident.span,
+                                    String::from("a similar path exists"),
+                                    candidate.path.to_string(),
+                                    Applicability::MaybeIncorrect,
+                                )),
+                            )
                         } else if !ident.is_reserved() {
-                            format!("maybe a missing `extern crate {};`?", ident)
+                            (format!("maybe a missing `extern crate {};`?", ident), None)
                         } else {
                             // the parser will already have complained about the keyword being used
                             return PathResult::NonModule(err_path_resolution());
                         }
                     } else if i == 0 {
-                        format!("use of undeclared type or module `{}`", ident)
+                        (format!("use of undeclared type or module `{}`", ident), None)
                     } else {
-                        format!("could not find `{}` in `{}`", ident, path[i - 1].ident)
+                        (format!("could not find `{}` in `{}`", ident, path[i - 1].ident), None)
+                    };
+                    return PathResult::Failed {
+                        span: ident.span,
+                        label,
+                        suggestion,
+                        is_error_from_last_segment: is_last,
                     };
-                    return PathResult::Failed(ident.span, msg, is_last);
                 }
             }
         }
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 63f752ac9c9..8e4b2a9a4cb 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -426,9 +426,9 @@ impl<'a> Resolver<'a> {
                     Ok(path_res.base_def())
                 }
                 PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
-                PathResult::NonModule(..) | PathResult::Indeterminate | PathResult::Failed(..) => {
-                    Err(Determinacy::Determined)
-                }
+                PathResult::NonModule(..)
+                | PathResult::Indeterminate
+                | PathResult::Failed { .. } => Err(Determinacy::Determined),
                 PathResult::Module(..) => unreachable!(),
             };
 
@@ -990,14 +990,17 @@ impl<'a> Resolver<'a> {
                     let def = path_res.base_def();
                     check_consistency(self, &path, path_span, kind, initial_def, def);
                 }
-                path_res @ PathResult::NonModule(..) | path_res @ PathResult::Failed(..) => {
-                    let (span, msg) = if let PathResult::Failed(span, msg, ..) = path_res {
-                        (span, msg)
+                path_res @ PathResult::NonModule(..) | path_res @ PathResult::Failed { .. } => {
+                    let (span, label) = if let PathResult::Failed { span, label, .. } = path_res {
+                        (span, label)
                     } else {
                         (path_span, format!("partially resolved path in {} {}",
                                             kind.article(), kind.descr()))
                     };
-                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                    resolve_error(self, span, ResolutionError::FailedToResolve {
+                        label,
+                        suggestion: None
+                    });
                 }
                 PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
             }
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index b930c30c511..9daffd522bf 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -6,9 +6,11 @@ use crate::Namespace::{self, TypeNS, MacroNS};
 use crate::{NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
 use crate::{Resolver, Segment};
 use crate::{names_to_string, module_to_string};
-use crate::{resolve_error, ResolutionError};
+use crate::{resolve_error, ResolutionError, Suggestion};
 use crate::macros::ParentScope;
 
+use errors::Applicability;
+
 use rustc_data_structures::ptr_key::PtrKey;
 use rustc::ty;
 use rustc::lint::builtin::BuiltinLintDiagnostics;
@@ -27,7 +29,7 @@ use syntax::util::lev_distance::find_best_match_for_name;
 use syntax::{struct_span_err, unwrap_or};
 use syntax_pos::{MultiSpan, Span};
 
-use log::debug;
+use log::*;
 
 use std::cell::{Cell, RefCell};
 use std::{mem, ptr};
@@ -623,6 +625,16 @@ impl<'a> Resolver<'a> {
     }
 }
 
+/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
+/// import errors within the same use tree into a single diagnostic.
+#[derive(Debug, Clone)]
+struct UnresolvedImportError {
+    span: Span,
+    label: Option<String>,
+    note: Option<String>,
+    suggestion: Option<Suggestion>,
+}
+
 pub struct ImportResolver<'a, 'b: 'a> {
     pub resolver: &'a mut Resolver<'b>,
 }
@@ -675,14 +687,14 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             self.finalize_resolutions_in(module);
         }
 
-        let mut errors = false;
+        let mut has_errors = false;
         let mut seen_spans = FxHashSet::default();
-        let mut error_vec = Vec::new();
+        let mut errors = vec![];
         let mut prev_root_id: NodeId = NodeId::from_u32(0);
         for i in 0 .. self.determined_imports.len() {
             let import = self.determined_imports[i];
-            if let Some((span, err, note)) = self.finalize_import(import) {
-                errors = true;
+            if let Some(err) = self.finalize_import(import) {
+                has_errors = true;
 
                 if let SingleImport { source, ref source_bindings, .. } = import.subclass {
                     if source.name == "self" {
@@ -696,37 +708,36 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                 // If the error is a single failed import then create a "fake" import
                 // resolution for it so that later resolve stages won't complain.
                 self.import_dummy_binding(import);
-                if prev_root_id.as_u32() != 0 &&
-                    prev_root_id.as_u32() != import.root_id.as_u32() &&
-                    !error_vec.is_empty(){
-                    // in case of new import line, throw diagnostic message
-                    // for previous line.
-                    let mut empty_vec = vec![];
-                    mem::swap(&mut empty_vec, &mut error_vec);
-                    self.throw_unresolved_import_error(empty_vec, None);
+                if prev_root_id.as_u32() != 0
+                        && prev_root_id.as_u32() != import.root_id.as_u32()
+                        && !errors.is_empty() {
+                    // In the case of a new import line, throw a diagnostic message
+                    // for the previous line.
+                    self.throw_unresolved_import_error(errors, None);
+                    errors = vec![];
                 }
-                if !seen_spans.contains(&span) {
+                if !seen_spans.contains(&err.span) {
                     let path = import_path_to_string(
                         &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
                         &import.subclass,
-                        span,
+                        err.span,
                     );
-                    error_vec.push((span, path, err, note));
-                    seen_spans.insert(span);
+                    seen_spans.insert(err.span);
+                    errors.push((path, err));
                     prev_root_id = import.root_id;
                 }
             }
         }
 
-        if !error_vec.is_empty() {
-            self.throw_unresolved_import_error(error_vec.clone(), None);
+        if !errors.is_empty() {
+            self.throw_unresolved_import_error(errors.clone(), None);
         }
 
         // Report unresolved imports only if no hard error was already reported
         // to avoid generating multiple errors on the same import.
-        if !errors {
+        if !has_errors {
             for import in &self.indeterminate_imports {
-                self.throw_unresolved_import_error(error_vec, Some(MultiSpan::from(import.span)));
+                self.throw_unresolved_import_error(errors, Some(MultiSpan::from(import.span)));
                 break;
             }
         }
@@ -734,44 +745,58 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
 
     fn throw_unresolved_import_error(
         &self,
-        error_vec: Vec<(Span, String, String, Option<String>)>,
+        errors: Vec<(String, UnresolvedImportError)>,
         span: Option<MultiSpan>,
     ) {
-        let max_span_label_msg_count = 10;  // upper limit on number of span_label message.
-        let (span, msg, note) = if error_vec.is_empty() {
+        /// Upper limit on the number of `span_label` messages.
+        const MAX_LABEL_COUNT: usize = 10;
+
+        let (span, msg, note) = if errors.is_empty() {
             (span.unwrap(), "unresolved import".to_string(), None)
         } else {
             let span = MultiSpan::from_spans(
-                error_vec.clone().into_iter()
-                .map(|elem: (Span, String, String, Option<String>)| elem.0)
-                .collect()
+                errors
+                    .iter()
+                    .map(|(_, err)| err.span)
+                    .collect(),
             );
 
-            let note: Option<String> = error_vec.clone().into_iter()
-                .filter_map(|elem: (Span, String, String, Option<String>)| elem.3)
+            let note = errors
+                .iter()
+                .filter_map(|(_, err)| err.note.as_ref())
                 .last();
 
-            let path_vec: Vec<String> = error_vec.clone().into_iter()
-                .map(|elem: (Span, String, String, Option<String>)| format!("`{}`", elem.1))
-                .collect();
-            let path = path_vec.join(", ");
+            let paths = errors
+                .iter()
+                .map(|(path, _)| format!("`{}`", path))
+                .collect::<Vec<_>>();
+
             let msg = format!(
                 "unresolved import{} {}",
-                if path_vec.len() > 1 { "s" } else { "" },
-                path
+                if paths.len() > 1 { "s" } else { "" },
+                paths.join(", "),
             );
 
             (span, msg, note)
         };
 
-        let mut err = struct_span_err!(self.resolver.session, span, E0432, "{}", &msg);
-        for span_error in error_vec.into_iter().take(max_span_label_msg_count) {
-            err.span_label(span_error.0, span_error.2);
+        let mut diag = struct_span_err!(self.resolver.session, span, E0432, "{}", &msg);
+
+        if let Some(note) = &note {
+            diag.note(note);
         }
-        if let Some(note) = note {
-            err.note(&note);
+
+        for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
+            if let Some(label) = err.label {
+                diag.span_label(err.span, label);
+            }
+
+            if let Some((span, msg, suggestion, applicability)) = err.suggestion {
+                diag.span_suggestion(span, &msg, suggestion, applicability);
+            }
         }
-        err.emit();
+
+        diag.emit();
     }
 
     /// Attempts to resolve the given import, returning true if its resolution is determined.
@@ -802,7 +827,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
             match path_res {
                 PathResult::Module(module) => module,
                 PathResult::Indeterminate => return false,
-                PathResult::NonModule(..) | PathResult::Failed(..) => return true,
+                PathResult::NonModule(..) | PathResult::Failed { .. } => return true,
             }
         };
 
@@ -866,11 +891,14 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         !indeterminate
     }
 
-    // If appropriate, returns an error to report.
+    /// Performs final import resolution, consistency checks and error reporting.
+    ///
+    /// Optionally returns an unresolved import error. This error is buffered and used to
+    /// consolidate multiple unresolved import errors into a single diagnostic.
     fn finalize_import(
         &mut self,
         directive: &'b ImportDirective<'b>
-    ) -> Option<(Span, String, Option<String>)> {
+    ) -> Option<UnresolvedImportError> {
         self.current_module = directive.parent_scope.module;
 
         let orig_vis = directive.vis.replace(ty::Visibility::Invisible);
@@ -896,25 +924,48 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
 
                 module
             }
-            PathResult::Failed(span, msg, false) => {
+            PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
                 if no_ambiguity {
                     assert!(directive.imported_module.get().is_none());
-                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                    resolve_error(self, span, ResolutionError::FailedToResolve {
+                        label,
+                        suggestion,
+                    });
                 }
                 return None;
             }
-            PathResult::Failed(span, msg, true) => {
+            PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => {
                 if no_ambiguity {
                     assert!(directive.imported_module.get().is_none());
-                    return Some(match self.make_path_suggestion(span, directive.module_path.clone(),
-                                                                &directive.parent_scope) {
-                        Some((suggestion, note)) => (
-                            span,
-                            format!("did you mean `{}`?", Segment::names_to_string(&suggestion)),
-                            note,
-                        ),
-                        None => (span, msg, None),
-                    });
+                    let err = match self.make_path_suggestion(
+                        span,
+                        directive.module_path.clone(),
+                        &directive.parent_scope,
+                    ) {
+                        Some((suggestion, note)) => {
+                            UnresolvedImportError {
+                                span,
+                                label: None,
+                                note,
+                                suggestion: Some((
+                                    span,
+                                    String::from("a similar path exists"),
+                                    Segment::names_to_string(&suggestion),
+                                    Applicability::MaybeIncorrect,
+                                )),
+                            }
+                        }
+                        None => {
+                            UnresolvedImportError {
+                                span,
+                                label: Some(label),
+                                note: None,
+                                suggestion,
+                            }
+                        }
+                    };
+
+                    return Some(err);
                 }
                 return None;
             }
@@ -950,11 +1001,12 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                 if let ModuleOrUniformRoot::Module(module) = module {
                     if module.def_id() == directive.parent_scope.module.def_id() {
                         // Importing a module into itself is not allowed.
-                        return Some((
-                            directive.span,
-                            "Cannot glob-import a module into itself.".to_string(),
-                            None,
-                        ));
+                        return Some(UnresolvedImportError {
+                            span: directive.span,
+                            label: Some(String::from("cannot glob-import a module into itself")),
+                            note: None,
+                            suggestion: None,
+                        });
                     }
                 }
                 if !is_prelude &&
@@ -1059,31 +1111,42 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                         _ => Some(&i.name),
                     }
                 });
+
                 let lev_suggestion =
-                    match find_best_match_for_name(names, &ident.as_str(), None) {
-                        Some(name) => format!(". Did you mean to use `{}`?", name),
-                        None => String::new(),
-                    };
-                let msg = match module {
+                    find_best_match_for_name(names, &ident.as_str(), None).map(|suggestion| {
+                        (
+                            ident.span,
+                            String::from("a similar name exists in the module"),
+                            suggestion.to_string(),
+                            Applicability::MaybeIncorrect,
+                        )
+                    });
+
+                let label = match module {
                     ModuleOrUniformRoot::Module(module) => {
                         let module_str = module_to_string(module);
                         if let Some(module_str) = module_str {
-                            format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion)
+                            format!("no `{}` in `{}`", ident, module_str)
                         } else {
-                            format!("no `{}` in the root{}", ident, lev_suggestion)
+                            format!("no `{}` in the root", ident)
                         }
                     }
                     _ => {
                         if !ident.is_path_segment_keyword() {
-                            format!("no `{}` external crate{}", ident, lev_suggestion)
+                            format!("no `{}` external crate", ident)
                         } else {
                             // HACK(eddyb) this shows up for `self` & `super`, which
                             // should work instead - for now keep the same error message.
-                            format!("no `{}` in the root{}", ident, lev_suggestion)
+                            format!("no `{}` in the root", ident)
                         }
                     }
                 };
-                Some((directive.span, msg, None))
+                Some(UnresolvedImportError {
+                    span: directive.span,
+                    label: Some(label),
+                    note: None,
+                    suggestion: lev_suggestion,
+                })
             } else {
                 // `resolve_ident_in_module` reported a privacy error.
                 self.import_dummy_binding(directive);
diff --git a/src/test/ui/extenv/issue-55897.stderr b/src/test/ui/extenv/issue-55897.stderr
index 4d2e35dff46..603b29aa989 100644
--- a/src/test/ui/extenv/issue-55897.stderr
+++ b/src/test/ui/extenv/issue-55897.stderr
@@ -8,7 +8,10 @@ error[E0432]: unresolved import `prelude`
   --> $DIR/issue-55897.rs:1:5
    |
 LL | use prelude::*; //~ ERROR unresolved import `prelude`
-   |     ^^^^^^^ did you mean `std::prelude`?
+   |     ^^^^^^^
+   |     |
+   |     unresolved import
+   |     help: a similar path exists: `std::prelude`
 
 error: cannot determine resolution for the macro `env`
   --> $DIR/issue-55897.rs:6:22
diff --git a/src/test/ui/import.rs b/src/test/ui/import.rs
index 540258daaec..3170dd2fae1 100644
--- a/src/test/ui/import.rs
+++ b/src/test/ui/import.rs
@@ -1,6 +1,8 @@
 use zed::bar;
 use zed::baz; //~ ERROR unresolved import `zed::baz` [E0432]
-              //~^ no `baz` in `zed`. Did you mean to use `bar`?
+              //~| no `baz` in `zed`
+              //~| HELP a similar name exists in the module
+              //~| SUGGESTION bar
 
 
 mod zed {
diff --git a/src/test/ui/import.stderr b/src/test/ui/import.stderr
index 737d10cdecb..bfbb6560d49 100644
--- a/src/test/ui/import.stderr
+++ b/src/test/ui/import.stderr
@@ -2,16 +2,19 @@ error[E0432]: unresolved import `zed::baz`
   --> $DIR/import.rs:2:5
    |
 LL | use zed::baz; //~ ERROR unresolved import `zed::baz` [E0432]
-   |     ^^^^^^^^ no `baz` in `zed`. Did you mean to use `bar`?
+   |     ^^^^^---
+   |     |    |
+   |     |    help: a similar name exists in the module: `bar`
+   |     no `baz` in `zed`
 
 error[E0432]: unresolved import `foo`
-  --> $DIR/import.rs:8:9
+  --> $DIR/import.rs:10:9
    |
 LL |     use foo; //~ ERROR unresolved import `foo` [E0432]
    |         ^^^ no `foo` in the root
 
 error[E0603]: unresolved item `foo` is private
-  --> $DIR/import.rs:13:10
+  --> $DIR/import.rs:15:10
    |
 LL |     zed::foo(); //~ ERROR `foo` is private
    |          ^^^
diff --git a/src/test/ui/imports/issue-55457.stderr b/src/test/ui/imports/issue-55457.stderr
index 4ee0332d04b..a3474b2f7db 100644
--- a/src/test/ui/imports/issue-55457.stderr
+++ b/src/test/ui/imports/issue-55457.stderr
@@ -2,7 +2,10 @@ error[E0432]: unresolved import `NonExistent`
   --> $DIR/issue-55457.rs:1:5
    |
 LL | use NonExistent; //~ ERROR unresolved import `NonExistent`
-   |     ^^^^^^^^^^^ no `NonExistent` in the root. Did you mean to use `non_existent`?
+   |     ^^^^^^^^^^^
+   |     |
+   |     no `NonExistent` in the root
+   |     help: a similar name exists in the module: `non_existent`
 
 error[E0432]: unresolved import `non_existent`
   --> $DIR/issue-55457.rs:2:5
diff --git a/src/test/ui/inaccessible-test-modules.stderr b/src/test/ui/inaccessible-test-modules.stderr
index 636ef818705..40f2b7fd2ee 100644
--- a/src/test/ui/inaccessible-test-modules.stderr
+++ b/src/test/ui/inaccessible-test-modules.stderr
@@ -2,13 +2,19 @@ error[E0432]: unresolved import `__test`
   --> $DIR/inaccessible-test-modules.rs:5:5
    |
 LL | use __test as x; //~ ERROR unresolved import `__test`
-   |     ^^^^^^^^^^^ no `__test` in the root. Did you mean to use `test`?
+   |     ------^^^^^
+   |     |
+   |     no `__test` in the root
+   |     help: a similar name exists in the module: `test`
 
 error[E0432]: unresolved import `__test_reexports`
   --> $DIR/inaccessible-test-modules.rs:6:5
    |
 LL | use __test_reexports as y; //~ ERROR unresolved import `__test_reexports`
-   |     ^^^^^^^^^^^^^^^^^^^^^ no `__test_reexports` in the root. Did you mean to use `__test_reexports`?
+   |     ----------------^^^^^
+   |     |
+   |     no `__test_reexports` in the root
+   |     help: a similar name exists in the module: `__test_reexports`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-31212.stderr b/src/test/ui/issues/issue-31212.stderr
index d964b702f22..09300ffc787 100644
--- a/src/test/ui/issues/issue-31212.stderr
+++ b/src/test/ui/issues/issue-31212.stderr
@@ -2,7 +2,7 @@ error[E0432]: unresolved import `self::*`
   --> $DIR/issue-31212.rs:5:13
    |
 LL |     pub use self::*; //~ ERROR unresolved
-   |             ^^^^^^^ Cannot glob-import a module into itself.
+   |             ^^^^^^^ cannot glob-import a module into itself
 
 error[E0425]: cannot find function `f` in module `foo`
   --> $DIR/issue-31212.rs:9:10
diff --git a/src/test/ui/issues/issue-8208.rs b/src/test/ui/issues/issue-8208.rs
index ad94f99098d..1c566938f9d 100644
--- a/src/test/ui/issues/issue-8208.rs
+++ b/src/test/ui/issues/issue-8208.rs
@@ -1,14 +1,14 @@
 use self::*; //~ ERROR: unresolved import `self::*` [E0432]
-             //~^ Cannot glob-import a module into itself.
+             //~^ cannot glob-import a module into itself
 
 mod foo {
     use foo::*; //~ ERROR: unresolved import `foo::*` [E0432]
-                //~^ Cannot glob-import a module into itself.
+                //~^ cannot glob-import a module into itself
 
     mod bar {
         use super::bar::*;
         //~^ ERROR: unresolved import `super::bar::*` [E0432]
-        //~| Cannot glob-import a module into itself.
+        //~| cannot glob-import a module into itself
     }
 
 }
diff --git a/src/test/ui/issues/issue-8208.stderr b/src/test/ui/issues/issue-8208.stderr
index 6de95fb953a..a042dce1ac1 100644
--- a/src/test/ui/issues/issue-8208.stderr
+++ b/src/test/ui/issues/issue-8208.stderr
@@ -2,19 +2,19 @@ error[E0432]: unresolved import `self::*`
   --> $DIR/issue-8208.rs:1:5
    |
 LL | use self::*; //~ ERROR: unresolved import `self::*` [E0432]
-   |     ^^^^^^^ Cannot glob-import a module into itself.
+   |     ^^^^^^^ cannot glob-import a module into itself
 
 error[E0432]: unresolved import `foo::*`
   --> $DIR/issue-8208.rs:5:9
    |
 LL |     use foo::*; //~ ERROR: unresolved import `foo::*` [E0432]
-   |         ^^^^^^ Cannot glob-import a module into itself.
+   |         ^^^^^^ cannot glob-import a module into itself
 
 error[E0432]: unresolved import `super::bar::*`
   --> $DIR/issue-8208.rs:9:13
    |
 LL |         use super::bar::*;
-   |             ^^^^^^^^^^^^^ Cannot glob-import a module into itself.
+   |             ^^^^^^^^^^^^^ cannot glob-import a module into itself
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/resolve_self_super_hint.rs b/src/test/ui/resolve_self_super_hint.rs
index 193a6ecf9d5..91a01cc0fa2 100644
--- a/src/test/ui/resolve_self_super_hint.rs
+++ b/src/test/ui/resolve_self_super_hint.rs
@@ -5,19 +5,23 @@ mod a {
     extern crate alloc;
     use alloc::HashMap;
     //~^ ERROR unresolved import `alloc` [E0432]
-    //~| did you mean `self::alloc`?
+    //~| HELP a similar path exists
+    //~| SUGGESTION self::alloc
     mod b {
         use alloc::HashMap;
         //~^ ERROR unresolved import `alloc` [E0432]
-        //~| did you mean `super::alloc`?
+        //~| HELP a similar path exists
+        //~| SUGGESTION super::alloc
         mod c {
             use alloc::HashMap;
             //~^ ERROR unresolved import `alloc` [E0432]
-            //~| did you mean `a::alloc`?
+            //~| HELP a similar path exists
+            //~| SUGGESTION a::alloc
             mod d {
                 use alloc::HashMap;
                 //~^ ERROR unresolved import `alloc` [E0432]
-                //~| did you mean `a::alloc`?
+                //~| HELP a similar path exists
+                //~| SUGGESTION a::alloc
             }
         }
     }
diff --git a/src/test/ui/resolve_self_super_hint.stderr b/src/test/ui/resolve_self_super_hint.stderr
index ddae0e5f6aa..03214cad8e4 100644
--- a/src/test/ui/resolve_self_super_hint.stderr
+++ b/src/test/ui/resolve_self_super_hint.stderr
@@ -2,25 +2,31 @@ error[E0432]: unresolved import `alloc`
   --> $DIR/resolve_self_super_hint.rs:6:9
    |
 LL |     use alloc::HashMap;
-   |         ^^^^^ did you mean `self::alloc`?
+   |         ^^^^^ help: a similar path exists: `self::alloc`
 
 error[E0432]: unresolved import `alloc`
-  --> $DIR/resolve_self_super_hint.rs:10:13
+  --> $DIR/resolve_self_super_hint.rs:11:13
    |
 LL |         use alloc::HashMap;
-   |             ^^^^^ did you mean `super::alloc`?
+   |             ^^^^^ help: a similar path exists: `super::alloc`
 
 error[E0432]: unresolved import `alloc`
-  --> $DIR/resolve_self_super_hint.rs:14:17
+  --> $DIR/resolve_self_super_hint.rs:16:17
    |
 LL |             use alloc::HashMap;
-   |                 ^^^^^ did you mean `a::alloc`?
+   |                 ^^^^^
+   |                 |
+   |                 unresolved import
+   |                 help: a similar path exists: `a::alloc`
 
 error[E0432]: unresolved import `alloc`
-  --> $DIR/resolve_self_super_hint.rs:18:21
+  --> $DIR/resolve_self_super_hint.rs:21:21
    |
 LL |                 use alloc::HashMap;
-   |                     ^^^^^ did you mean `a::alloc`?
+   |                     ^^^^^
+   |                     |
+   |                     unresolved import
+   |                     help: a similar path exists: `a::alloc`
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/rust-2018/issue-54006.stderr b/src/test/ui/rust-2018/issue-54006.stderr
index 6c6d7720be2..1978138a688 100644
--- a/src/test/ui/rust-2018/issue-54006.stderr
+++ b/src/test/ui/rust-2018/issue-54006.stderr
@@ -2,7 +2,7 @@ error[E0432]: unresolved import `alloc`
   --> $DIR/issue-54006.rs:6:5
    |
 LL | use alloc::vec;
-   |     ^^^^^ did you mean `core::alloc`?
+   |     ^^^^^ help: a similar path exists: `core::alloc`
 
 error: cannot determine resolution for the macro `vec`
   --> $DIR/issue-54006.rs:10:18
diff --git a/src/test/ui/rust-2018/local-path-suggestions-2015.stderr b/src/test/ui/rust-2018/local-path-suggestions-2015.stderr
index be642c3bcdc..fafb35ec50d 100644
--- a/src/test/ui/rust-2018/local-path-suggestions-2015.stderr
+++ b/src/test/ui/rust-2018/local-path-suggestions-2015.stderr
@@ -2,7 +2,10 @@ error[E0432]: unresolved import `foobar`
   --> $DIR/local-path-suggestions-2015.rs:24:5
    |
 LL | use foobar::Baz; //~ ERROR unresolved import `foobar`
-   |     ^^^^^^ did you mean `aux_baz::foobar`?
+   |     ^^^^^^
+   |     |
+   |     unresolved import
+   |     help: a similar path exists: `aux_baz::foobar`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rust-2018/local-path-suggestions-2018.stderr b/src/test/ui/rust-2018/local-path-suggestions-2018.stderr
index 71c8289264f..759977b3f06 100644
--- a/src/test/ui/rust-2018/local-path-suggestions-2018.stderr
+++ b/src/test/ui/rust-2018/local-path-suggestions-2018.stderr
@@ -2,7 +2,7 @@ error[E0432]: unresolved import `foo`
   --> $DIR/local-path-suggestions-2018.rs:10:9
    |
 LL |     use foo::Bar; //~ ERROR unresolved import `foo`
-   |         ^^^ did you mean `crate::foo`?
+   |         ^^^ help: a similar path exists: `crate::foo`
    |
    = note: `use` statements changed in Rust 2018; read more at <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html>
 
@@ -10,7 +10,7 @@ error[E0432]: unresolved import `foobar`
   --> $DIR/local-path-suggestions-2018.rs:19:5
    |
 LL | use foobar::Baz; //~ ERROR unresolved import `foobar`
-   |     ^^^^^^ did you mean `baz::foobar`?
+   |     ^^^^^^ help: a similar path exists: `baz::foobar`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/unresolved/unresolved-import.rs b/src/test/ui/unresolved/unresolved-import.rs
index be2a2c75485..4c7d4bb9353 100644
--- a/src/test/ui/unresolved/unresolved-import.rs
+++ b/src/test/ui/unresolved/unresolved-import.rs
@@ -1,16 +1,20 @@
-// ignore-tidy-linelength
-
 use foo::bar; //~ ERROR unresolved import `foo` [E0432]
               //~^ maybe a missing `extern crate foo;`?
 
 use bar::Baz as x; //~ ERROR unresolved import `bar::Baz` [E0432]
-                   //~^ no `Baz` in `bar`. Did you mean to use `Bar`?
+                   //~| no `Baz` in `bar`
+                   //~| HELP a similar name exists in the module
+                   //~| SUGGESTION Bar
 
 use food::baz; //~ ERROR unresolved import `food::baz`
-               //~^ no `baz` in `food`. Did you mean to use `bag`?
+               //~| no `baz` in `food`
+               //~| HELP a similar name exists in the module
+               //~| SUGGESTION bag
 
 use food::{beens as Foo}; //~ ERROR unresolved import `food::beens` [E0432]
-                          //~^ no `beens` in `food`. Did you mean to use `beans`?
+                          //~| no `beens` in `food`
+                          //~| HELP a similar name exists in the module
+                          //~| SUGGESTION beans
 
 mod bar {
     pub struct Bar;
@@ -32,7 +36,8 @@ mod m {
     }
 
     use MyEnum::*; //~ ERROR unresolved import `MyEnum` [E0432]
-                   //~^ did you mean `self::MyEnum`?
+                   //~| HELP a similar path exists
+                   //~| SUGGESTION self::MyEnum
 }
 
 mod items {
@@ -41,7 +46,8 @@ mod items {
     }
 
     use Enum::*; //~ ERROR unresolved import `Enum` [E0432]
-                 //~^ did you mean `self::Enum`?
+                 //~| HELP a similar path exists
+                 //~| SUGGESTION self::Enum
 
     fn item() {}
 }
diff --git a/src/test/ui/unresolved/unresolved-import.stderr b/src/test/ui/unresolved/unresolved-import.stderr
index 0dc4b72208a..4f2fef938c9 100644
--- a/src/test/ui/unresolved/unresolved-import.stderr
+++ b/src/test/ui/unresolved/unresolved-import.stderr
@@ -1,38 +1,47 @@
 error[E0432]: unresolved import `foo`
-  --> $DIR/unresolved-import.rs:3:5
+  --> $DIR/unresolved-import.rs:1:5
    |
 LL | use foo::bar; //~ ERROR unresolved import `foo` [E0432]
    |     ^^^ maybe a missing `extern crate foo;`?
 
 error[E0432]: unresolved import `bar::Baz`
-  --> $DIR/unresolved-import.rs:6:5
+  --> $DIR/unresolved-import.rs:4:5
    |
 LL | use bar::Baz as x; //~ ERROR unresolved import `bar::Baz` [E0432]
-   |     ^^^^^^^^^^^^^ no `Baz` in `bar`. Did you mean to use `Bar`?
+   |     ^^^^^---^^^^^
+   |     |    |
+   |     |    help: a similar name exists in the module: `Bar`
+   |     no `Baz` in `bar`
 
 error[E0432]: unresolved import `food::baz`
   --> $DIR/unresolved-import.rs:9:5
    |
 LL | use food::baz; //~ ERROR unresolved import `food::baz`
-   |     ^^^^^^^^^ no `baz` in `food`. Did you mean to use `bag`?
+   |     ^^^^^^---
+   |     |     |
+   |     |     help: a similar name exists in the module: `bag`
+   |     no `baz` in `food`
 
 error[E0432]: unresolved import `food::beens`
-  --> $DIR/unresolved-import.rs:12:12
+  --> $DIR/unresolved-import.rs:14:12
    |
 LL | use food::{beens as Foo}; //~ ERROR unresolved import `food::beens` [E0432]
-   |            ^^^^^^^^^^^^ no `beens` in `food`. Did you mean to use `beans`?
+   |            -----^^^^^^^
+   |            |
+   |            no `beens` in `food`
+   |            help: a similar name exists in the module: `beans`
 
 error[E0432]: unresolved import `MyEnum`
-  --> $DIR/unresolved-import.rs:34:9
+  --> $DIR/unresolved-import.rs:38:9
    |
 LL |     use MyEnum::*; //~ ERROR unresolved import `MyEnum` [E0432]
-   |         ^^^^^^ did you mean `self::MyEnum`?
+   |         ^^^^^^ help: a similar path exists: `self::MyEnum`
 
 error[E0432]: unresolved import `Enum`
-  --> $DIR/unresolved-import.rs:43:9
+  --> $DIR/unresolved-import.rs:48:9
    |
 LL |     use Enum::*; //~ ERROR unresolved import `Enum` [E0432]
-   |         ^^^^ did you mean `self::Enum`?
+   |         ^^^^ help: a similar path exists: `self::Enum`
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/use/use-nested-groups-error.stderr b/src/test/ui/use/use-nested-groups-error.stderr
index 9d6fd9df6cb..7234c8ec621 100644
--- a/src/test/ui/use/use-nested-groups-error.stderr
+++ b/src/test/ui/use/use-nested-groups-error.stderr
@@ -2,7 +2,10 @@ error[E0432]: unresolved import `a::b1::C1`
   --> $DIR/use-nested-groups-error.rs:9:14
    |
 LL | use a::{b1::{C1, C2}, B2};
-   |              ^^ no `C1` in `a::b1`. Did you mean to use `C2`?
+   |              ^^
+   |              |
+   |              no `C1` in `a::b1`
+   |              help: a similar name exists in the module: `C2`
 
 error: aborting due to previous error