about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2018-09-20 17:15:52 +0200
committerDavid Wood <david@davidtw.co>2018-10-03 14:43:57 +0200
commit29e2376ac7f8719930a14f256a84fbcd65e10163 (patch)
treed525feadfb23506e2949b7c98c687b6382ff9060
parent4cf11765dc98536c6eedf33f2df7f72f6e161263 (diff)
downloadrust-29e2376ac7f8719930a14f256a84fbcd65e10163.tar.gz
rust-29e2376ac7f8719930a14f256a84fbcd65e10163.zip
Add suggestions for unresolved imports.
This commit adds suggestions for unresolved imports in the cases where
there could be a missing `crate::`, `super::`, `self::` or a missing
external crate name before an import.
-rw-r--r--src/librustc_resolve/error_reporting.rs162
-rw-r--r--src/librustc_resolve/lib.rs2
-rw-r--r--src/librustc_resolve/resolve_imports.rs18
-rw-r--r--src/test/ui/resolve_self_super_hint.rs6
-rw-r--r--src/test/ui/resolve_self_super_hint.stderr6
-rw-r--r--src/test/ui/rust-2018/auxiliary/baz.rs15
-rw-r--r--src/test/ui/rust-2018/issue-54006.stderr2
-rw-r--r--src/test/ui/rust-2018/local-path-suggestions.rs31
-rw-r--r--src/test/ui/rust-2018/local-path-suggestions.stderr21
9 files changed, 244 insertions, 19 deletions
diff --git a/src/librustc_resolve/error_reporting.rs b/src/librustc_resolve/error_reporting.rs
new file mode 100644
index 00000000000..3ca0a11c1bb
--- /dev/null
+++ b/src/librustc_resolve/error_reporting.rs
@@ -0,0 +1,162 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use {CrateLint, PathResult};
+
+use syntax::ast::Ident;
+use syntax::symbol::keywords;
+use syntax_pos::Span;
+
+use resolve_imports::ImportResolver;
+
+impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
+    /// Add suggestions for a path that cannot be resolved.
+    pub(crate) fn make_path_suggestion(
+        &mut self,
+        span: Span,
+        path: Vec<Ident>
+    ) -> Option<Vec<Ident>> {
+        debug!("make_path_suggestion: span={:?} path={:?}", span, path);
+        // If we don't have a path to suggest changes to, then return.
+        if path.is_empty() {
+            return None;
+        }
+
+        // Check whether a ident is a path segment that is not root.
+        let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
+                                        ident.name != keywords::CrateRoot.name();
+
+        match (path.get(0), path.get(1)) {
+            // Make suggestions that require at least two non-special path segments.
+            (Some(fst), Some(snd)) if !is_special(*fst) && !is_special(*snd) => {
+                debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);
+
+                self.make_missing_self_suggestion(span, path.clone())
+                    .or_else(|| self.make_missing_crate_suggestion(span, path.clone()))
+                    .or_else(|| self.make_missing_super_suggestion(span, path.clone()))
+                    .or_else(|| self.make_external_crate_suggestion(span, path.clone()))
+            },
+            _ => None,
+        }
+    }
+
+    /// Suggest a missing `self::` if that resolves to an correct module.
+    ///
+    /// ```
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ Did you mean `self::foo`?
+    /// ```
+    fn make_missing_self_suggestion(
+        &mut self,
+        span: Span,
+        mut path: Vec<Ident>
+    ) -> Option<Vec<Ident>> {
+        // Replace first ident with `self` and check if that is valid.
+        path[0].name = keywords::SelfValue.name();
+        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
+        debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result {
+            Some(path)
+        } else {
+            None
+        }
+    }
+
+    /// Suggest a missing `crate::` if that resolves to an correct module.
+    ///
+    /// ```
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ Did you mean `crate::foo`?
+    /// ```
+    fn make_missing_crate_suggestion(
+        &mut self,
+        span: Span,
+        mut path: Vec<Ident>
+    ) -> Option<Vec<Ident>> {
+        // Replace first ident with `crate` and check if that is valid.
+        path[0].name = keywords::Crate.name();
+        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
+        debug!("make_missing_crate_suggestion:  path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result {
+            Some(path)
+        } else {
+            None
+        }
+    }
+
+    /// Suggest a missing `super::` if that resolves to an correct module.
+    ///
+    /// ```
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ Did you mean `super::foo`?
+    /// ```
+    fn make_missing_super_suggestion(
+        &mut self,
+        span: Span,
+        mut path: Vec<Ident>
+    ) -> Option<Vec<Ident>> {
+        // Replace first ident with `crate` and check if that is valid.
+        path[0].name = keywords::Super.name();
+        let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
+        debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result {
+            Some(path)
+        } else {
+            None
+        }
+    }
+
+    /// Suggest a missing external crate name if that resolves to an correct module.
+    ///
+    /// ```
+    ///    |
+    /// LL | use foobar::Baz;
+    ///    |     ^^^ Did you mean `baz::foobar`?
+    /// ```
+    ///
+    /// Used when importing a submodule of an external crate but missing that crate's
+    /// name as the first part of path.
+    fn make_external_crate_suggestion(
+        &mut self,
+        span: Span,
+        mut path: Vec<Ident>
+    ) -> Option<Vec<Ident>> {
+        // Need to clone else we can't call `resolve_path` without a borrow error.
+        let external_crate_names = self.resolver.session.extern_prelude.clone();
+
+        // Insert a new path segment that we can replace.
+        let new_path_segment = path[0].clone();
+        path.insert(1, new_path_segment);
+
+        for name in &external_crate_names {
+            // Don't suggest meta as it will error in `resolve_path`.
+            if name.as_str() == "meta" {
+                continue;
+            }
+
+            // Replace the first after root (a placeholder we inserted) with a crate name
+            // and check if that is valid.
+            path[1].name = *name;
+            let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
+            debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
+                   name, path, result);
+            if let PathResult::Module(..) = result {
+                return Some(path)
+            }
+        }
+
+        // Remove our placeholder segment.
+        path.remove(1);
+        None
+    }
+}
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index b4e7f3a8b74..35241e2b155 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -85,7 +85,7 @@ use macros::{InvocationData, LegacyBinding, ParentScope};
 // NB: This module needs to be declared first so diagnostics are
 // registered before they are used.
 mod diagnostics;
-
+mod error_reporting;
 mod macros;
 mod check_unused;
 mod build_reduced_graph;
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index e689e6d70fd..bc93b43c6ae 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -957,17 +957,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
                 return None;
             }
             PathResult::Failed(span, msg, true) => {
-                let (mut self_path, mut self_result) = (module_path.clone(), None);
-                let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
-                                                ident.name != keywords::CrateRoot.name();
-                if !self_path.is_empty() && !is_special(self_path[0]) &&
-                   !(self_path.len() > 1 && is_special(self_path[1])) {
-                    self_path[0].name = keywords::SelfValue.name();
-                    self_result = Some(self.resolve_path(None, &self_path, None, false,
-                                                         span, CrateLint::No));
-                }
-                return if let Some(PathResult::Module(..)) = self_result {
-                    Some((span, format!("Did you mean `{}`?", names_to_string(&self_path[..]))))
+                return if let Some(suggested_path) = self.make_path_suggestion(
+                    span, module_path.clone()
+                ) {
+                    Some((
+                        span,
+                        format!("Did you mean `{}`?", names_to_string(&suggested_path[..]))
+                    ))
                 } else {
                     Some((span, msg))
                 };
diff --git a/src/test/ui/resolve_self_super_hint.rs b/src/test/ui/resolve_self_super_hint.rs
index c5dd367c0ab..a30e73cf02d 100644
--- a/src/test/ui/resolve_self_super_hint.rs
+++ b/src/test/ui/resolve_self_super_hint.rs
@@ -19,15 +19,15 @@ mod a {
     mod b {
         use alloc::HashMap;
         //~^ ERROR unresolved import `alloc` [E0432]
-        //~| Did you mean `a::alloc`?
+        //~| Did you mean `super::alloc`?
         mod c {
             use alloc::HashMap;
             //~^ ERROR unresolved import `alloc` [E0432]
-            //~| Did you mean `a::alloc`?
+            //~| Did you mean `std::alloc`?
             mod d {
                 use alloc::HashMap;
                 //~^ ERROR unresolved import `alloc` [E0432]
-                //~| Did you mean `a::alloc`?
+                //~| Did you mean `std::alloc`?
             }
         }
     }
diff --git a/src/test/ui/resolve_self_super_hint.stderr b/src/test/ui/resolve_self_super_hint.stderr
index 40b2a4bf968..b58a23724e4 100644
--- a/src/test/ui/resolve_self_super_hint.stderr
+++ b/src/test/ui/resolve_self_super_hint.stderr
@@ -8,19 +8,19 @@ error[E0432]: unresolved import `alloc`
   --> $DIR/resolve_self_super_hint.rs:20:13
    |
 LL |         use alloc::HashMap;
-   |             ^^^^^ Did you mean `a::alloc`?
+   |             ^^^^^ Did you mean `super::alloc`?
 
 error[E0432]: unresolved import `alloc`
   --> $DIR/resolve_self_super_hint.rs:24:17
    |
 LL |             use alloc::HashMap;
-   |                 ^^^^^ Did you mean `a::alloc`?
+   |                 ^^^^^ Did you mean `std::alloc`?
 
 error[E0432]: unresolved import `alloc`
   --> $DIR/resolve_self_super_hint.rs:28:21
    |
 LL |                 use alloc::HashMap;
-   |                     ^^^^^ Did you mean `a::alloc`?
+   |                     ^^^^^ Did you mean `std::alloc`?
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/rust-2018/auxiliary/baz.rs b/src/test/ui/rust-2018/auxiliary/baz.rs
new file mode 100644
index 00000000000..4ee9c051c65
--- /dev/null
+++ b/src/test/ui/rust-2018/auxiliary/baz.rs
@@ -0,0 +1,15 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This file is used as part of the local-path-suggestions.rs test.
+
+pub mod foobar {
+    pub struct Baz;
+}
diff --git a/src/test/ui/rust-2018/issue-54006.stderr b/src/test/ui/rust-2018/issue-54006.stderr
index 1183dc9794a..37bf19e61f8 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:16:5
    |
 LL | use alloc::vec;
-   |     ^^^^^ Could not find `alloc` in `{{root}}`
+   |     ^^^^^ Did you mean `std::alloc`?
 
 error: cannot determine resolution for the macro `vec`
   --> $DIR/issue-54006.rs:20:18
diff --git a/src/test/ui/rust-2018/local-path-suggestions.rs b/src/test/ui/rust-2018/local-path-suggestions.rs
new file mode 100644
index 00000000000..840e6103ec6
--- /dev/null
+++ b/src/test/ui/rust-2018/local-path-suggestions.rs
@@ -0,0 +1,31 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:baz.rs
+// compile-flags:--extern baz
+// edition:2018
+
+mod foo {
+    type Bar = u32;
+}
+
+mod baz {
+    use foo::Bar;
+
+    fn baz() {
+        let x: Bar = 22;
+    }
+}
+
+use foo::Bar;
+
+use foobar::Baz;
+
+fn main() { }
diff --git a/src/test/ui/rust-2018/local-path-suggestions.stderr b/src/test/ui/rust-2018/local-path-suggestions.stderr
new file mode 100644
index 00000000000..38a5d412183
--- /dev/null
+++ b/src/test/ui/rust-2018/local-path-suggestions.stderr
@@ -0,0 +1,21 @@
+error[E0432]: unresolved import `foo`
+  --> $DIR/local-path-suggestions.rs:20:9
+   |
+LL |     use foo::Bar;
+   |         ^^^ Did you mean `crate::foo`?
+
+error[E0432]: unresolved import `foo`
+  --> $DIR/local-path-suggestions.rs:27:5
+   |
+LL | use foo::Bar;
+   |     ^^^ Did you mean `self::foo`?
+
+error[E0432]: unresolved import `foobar`
+  --> $DIR/local-path-suggestions.rs:29:5
+   |
+LL | use foobar::Baz;
+   |     ^^^^^^ Did you mean `baz::foobar`?
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0432`.