about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-10-10 20:47:34 +0200
committerGitHub <noreply@github.com>2022-10-10 20:47:34 +0200
commit0bd1cba98bcee7579ee54e99dbab216d0d58dc6a (patch)
treec3d38e1d63f23fd163e28fb4581e07bb4bb87257
parent40c1410ce48d06ce8ee4efe1ce8c7b933259d849 (diff)
parent0571b0af65dcc0dd97cce4b976887ab436e5780d (diff)
Rollup merge of #102876 - SparrowLii:import-candidate, r=fee1-dead
suggest candidates for unresolved import

Currently we prompt suggestion of candidates(help notes of `use xxx::yyy`) for names which cannot be resolved, but we don't do that for import statements themselves that couldn't be resolved. It seems reasonable to add candidate help information for these statements as well.
Fixes #102711
-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`.