diff --git a/src/librustc_resolve/error_reporting.rs b/src/librustc_resolve/error_reporting.rs new file mode 100644 index 0000000000000..004a28e8b3355 --- /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 or the MIT license +// , 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 + ) -> Option> { + 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 + ) -> Option> { + // 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 + ) -> Option> { + // 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 + ) -> Option> { + // 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 + ) -> Option> { + // Need to clone else we can't call `resolve_path` without a borrow error. + let external_crate_names = self.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 10dddfed6a577..f68a304d90b7a 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 dc4a76db69266..da2738e6c9e0e 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 c5dd367c0ab88..a30e73cf02d12 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 40b2a4bf96842..b58a23724e413 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 0000000000000..4ee9c051c6568 --- /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 or the MIT license +// , 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 1183dc9794a22..37bf19e61f8d8 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 0000000000000..840e6103ec67d --- /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 or the MIT license +// , 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 0000000000000..38a5d412183cc --- /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`.