about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs38
-rw-r--r--compiler/rustc_resolve/src/imports.rs28
-rw-r--r--src/test/ui/extenv/issue-55897.stderr5
-rw-r--r--src/test/ui/imports/issue-56125.stderr12
-rw-r--r--src/test/ui/imports/issue-57015.stderr5
-rw-r--r--src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr7
-rw-r--r--src/test/ui/simd/portable-intrinsics-arent-exposed.stderr5
-rw-r--r--src/test/ui/test-attrs/inaccessible-test-modules.stderr14
-rw-r--r--src/test/ui/unresolved/unresolved-candidates.rs13
-rw-r--r--src/test/ui/unresolved/unresolved-candidates.stderr26
10 files changed, 145 insertions, 8 deletions
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 98982240af2..9a3eac2f866 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -70,6 +70,7 @@ impl TypoSuggestion {
 }
 
 /// A free importable items suggested in case of resolution failure.
+#[derive(Debug, Clone)]
 pub(crate) struct ImportSuggestion {
     pub did: Option<DefId>,
     pub descr: &'static str,
@@ -139,6 +140,7 @@ impl<'a> Resolver<'a> {
                     if instead { Instead::Yes } else { Instead::No },
                     found_use,
                     IsPattern::No,
+                    IsImport::No,
                     path,
                 );
                 err.emit();
@@ -698,6 +700,7 @@ impl<'a> Resolver<'a> {
                         Instead::No,
                         FoundUse::Yes,
                         IsPattern::Yes,
+                        IsImport::No,
                         vec![],
                     );
                 }
@@ -1481,6 +1484,7 @@ impl<'a> Resolver<'a> {
             Instead::No,
             FoundUse::Yes,
             IsPattern::No,
+            IsImport::No,
             vec![],
         );
 
@@ -2449,6 +2453,34 @@ enum IsPattern {
     No,
 }
 
+/// Whether a binding is part of a use statement. Used for diagnostics.
+enum IsImport {
+    Yes,
+    No,
+}
+
+pub(crate) fn import_candidates(
+    session: &Session,
+    source_span: &IndexVec<LocalDefId, Span>,
+    err: &mut Diagnostic,
+    // This is `None` if all placement locations are inside expansions
+    use_placement_span: Option<Span>,
+    candidates: &[ImportSuggestion],
+) {
+    show_candidates(
+        session,
+        source_span,
+        err,
+        use_placement_span,
+        candidates,
+        Instead::Yes,
+        FoundUse::Yes,
+        IsPattern::No,
+        IsImport::Yes,
+        vec![],
+    );
+}
+
 /// When an entity with a given name is not available in scope, we search for
 /// entities with that name in all crates. This method allows outputting the
 /// results of this search in a programmer-friendly way
@@ -2462,6 +2494,7 @@ fn show_candidates(
     instead: Instead,
     found_use: FoundUse,
     is_pattern: IsPattern,
+    is_import: IsImport,
     path: Vec<Segment>,
 ) {
     if candidates.is_empty() {
@@ -2521,7 +2554,8 @@ fn show_candidates(
                 // produce an additional newline to separate the new use statement
                 // from the directly following item.
                 let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
-                candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
+                let add_use = if let IsImport::Yes = is_import { "" } else { "use " };
+                candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline);
             }
 
             err.span_suggestions(
@@ -2551,7 +2585,7 @@ fn show_candidates(
 
             err.note(&msg);
         }
-    } else {
+    } else if matches!(is_import, IsImport::No) {
         assert!(!inaccessible_path_strings.is_empty());
 
         let prefix =
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 5bdb4274781..9e2234ae4a5 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1,9 +1,9 @@
 //! A bunch of methods and structures more or less related to resolving imports.
 
-use crate::diagnostics::Suggestion;
+use crate::diagnostics::{import_candidates, Suggestion};
 use crate::Determinacy::{self, *};
 use crate::Namespace::{self, *};
-use crate::{module_to_string, names_to_string};
+use crate::{module_to_string, names_to_string, ImportSuggestion};
 use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
 use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
 use crate::{NameBinding, NameBindingKind, PathResult};
@@ -406,6 +406,7 @@ struct UnresolvedImportError {
     label: Option<String>,
     note: Option<String>,
     suggestion: Option<Suggestion>,
+    candidate: Option<Vec<ImportSuggestion>>,
 }
 
 pub struct ImportResolver<'a, 'b> {
@@ -497,6 +498,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     label: None,
                     note: None,
                     suggestion: None,
+                    candidate: None,
                 };
                 if path.contains("::") {
                     errors.push((path, err))
@@ -547,6 +549,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 }
                 diag.multipart_suggestion(&msg, suggestions, applicability);
             }
+
+            if let Some(candidate) = &err.candidate {
+                import_candidates(
+                    self.r.session,
+                    &self.r.source_span,
+                    &mut diag,
+                    Some(err.span),
+                    &candidate,
+                )
+            }
         }
 
         diag.emit();
@@ -664,6 +676,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
             Some(finalize),
             ignore_binding,
         );
+
         let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
         import.vis.set(orig_vis);
         let module = match path_res {
@@ -706,12 +719,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                                 String::from("a similar path exists"),
                                 Applicability::MaybeIncorrect,
                             )),
+                            candidate: None,
                         },
                         None => UnresolvedImportError {
                             span,
                             label: Some(label),
                             note: None,
                             suggestion,
+                            candidate: None,
                         },
                     };
                     return Some(err);
@@ -754,6 +769,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                             label: Some(String::from("cannot glob-import a module into itself")),
                             note: None,
                             suggestion: None,
+                            candidate: None,
                         });
                     }
                 }
@@ -919,11 +935,19 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                     }
                 };
 
+                let parent_suggestion =
+                    self.r.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true);
+
                 Some(UnresolvedImportError {
                     span: import.span,
                     label: Some(label),
                     note,
                     suggestion,
+                    candidate: if !parent_suggestion.is_empty() {
+                        Some(parent_suggestion)
+                    } else {
+                        None
+                    },
                 })
             } else {
                 // `resolve_ident_in_module` reported a privacy error.
diff --git a/src/test/ui/extenv/issue-55897.stderr b/src/test/ui/extenv/issue-55897.stderr
index e2afe6f34c1..63797d4a71b 100644
--- a/src/test/ui/extenv/issue-55897.stderr
+++ b/src/test/ui/extenv/issue-55897.stderr
@@ -26,6 +26,11 @@ error[E0432]: unresolved import `env`
    |
 LL |     use env;
    |         ^^^ no `env` in the root
+   |
+help: consider importing this module instead
+   |
+LL |     use std::env;
+   |         ~~~~~~~~~
 
 error: cannot determine resolution for the macro `env`
   --> $DIR/issue-55897.rs:6:22
diff --git a/src/test/ui/imports/issue-56125.stderr b/src/test/ui/imports/issue-56125.stderr
index 2e4ba862376..059ca96808d 100644
--- a/src/test/ui/imports/issue-56125.stderr
+++ b/src/test/ui/imports/issue-56125.stderr
@@ -3,6 +3,18 @@ error[E0432]: unresolved import `empty::issue_56125`
    |
 LL |     use empty::issue_56125;
    |         ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty`
+   |
+help: consider importing one of these items instead
+   |
+LL |     use crate::m3::last_segment::issue_56125;
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     use crate::m3::non_last_segment::non_last_segment::issue_56125;
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     use issue_56125::issue_56125;
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~
+LL |     use issue_56125::last_segment::issue_56125;
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     and 1 other candidate
 
 error[E0659]: `issue_56125` is ambiguous
   --> $DIR/issue-56125.rs:6:9
diff --git a/src/test/ui/imports/issue-57015.stderr b/src/test/ui/imports/issue-57015.stderr
index d200d23ab28..3b72d57fee4 100644
--- a/src/test/ui/imports/issue-57015.stderr
+++ b/src/test/ui/imports/issue-57015.stderr
@@ -3,6 +3,11 @@ error[E0432]: unresolved import `single_err::something`
    |
 LL | use single_err::something;
    |     ^^^^^^^^^^^^^^^^^^^^^ no `something` in `single_err`
+   |
+help: consider importing this module instead
+   |
+LL | use glob_ok::something;
+   |     ~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr b/src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr
index a66330ccc46..761089cd387 100644
--- a/src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr
+++ b/src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr
@@ -3,6 +3,13 @@ error[E0432]: unresolved import `alloc`
    |
 LL | use alloc;
    |     ^^^^^ no external crate `alloc`
+   |
+help: consider importing one of these items instead
+   |
+LL | use core::alloc;
+   |     ~~~~~~~~~~~~
+LL | use std::alloc;
+   |     ~~~~~~~~~~~
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr
index 870f4064de4..8881ede0dbc 100644
--- a/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr
+++ b/src/test/ui/simd/portable-intrinsics-arent-exposed.stderr
@@ -11,6 +11,11 @@ error[E0432]: unresolved import `std::simd::intrinsics`
    |
 LL | use std::simd::intrinsics;
    |     ^^^^^^^^^^^^^^^^^^^^^ no `intrinsics` in `simd`
+   |
+help: consider importing this module instead
+   |
+LL | use std::intrinsics;
+   |     ~~~~~~~~~~~~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/test-attrs/inaccessible-test-modules.stderr b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
index a94ea1e79bc..0c16ecd4c86 100644
--- a/src/test/ui/test-attrs/inaccessible-test-modules.stderr
+++ b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
@@ -11,10 +11,16 @@ error[E0432]: unresolved import `test`
   --> $DIR/inaccessible-test-modules.rs:6:5
    |
 LL | use test as y;
-   |     ----^^^^^
-   |     |
-   |     no `test` in the root
-   |     help: a similar name exists in the module: `test`
+   |     ^^^^^^^^^ no `test` in the root
+   |
+help: a similar name exists in the module
+   |
+LL | use test as y;
+   |     ~~~~
+help: consider importing this module instead
+   |
+LL | use test::test;
+   |     ~~~~~~~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/unresolved/unresolved-candidates.rs b/src/test/ui/unresolved/unresolved-candidates.rs
new file mode 100644
index 00000000000..38b227f609b
--- /dev/null
+++ b/src/test/ui/unresolved/unresolved-candidates.rs
@@ -0,0 +1,13 @@
+mod a {
+    pub trait Trait {}
+}
+
+mod b {
+    use Trait; //~ ERROR unresolved import `Trait`
+}
+
+mod c {
+    impl Trait for () {} //~ ERROR cannot find trait `Trait` in this scope
+}
+
+fn main() {}
diff --git a/src/test/ui/unresolved/unresolved-candidates.stderr b/src/test/ui/unresolved/unresolved-candidates.stderr
new file mode 100644
index 00000000000..bbd3eec2a54
--- /dev/null
+++ b/src/test/ui/unresolved/unresolved-candidates.stderr
@@ -0,0 +1,26 @@
+error[E0432]: unresolved import `Trait`
+  --> $DIR/unresolved-candidates.rs:6:9
+   |
+LL |     use Trait;
+   |         ^^^^^ no `Trait` in the root
+   |
+help: consider importing this trait instead
+   |
+LL |     use a::Trait;
+   |         ~~~~~~~~~
+
+error[E0405]: cannot find trait `Trait` in this scope
+  --> $DIR/unresolved-candidates.rs:10:10
+   |
+LL |     impl Trait for () {}
+   |          ^^^^^ not found in this scope
+   |
+help: consider importing this trait
+   |
+LL |     use a::Trait;
+   |
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0405, E0432.
+For more information about an error, try `rustc --explain E0405`.