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.
This commit is contained in:
parent
4cf11765dc
commit
29e2376ac7
9 changed files with 244 additions and 19 deletions
162
src/librustc_resolve/error_reporting.rs
Normal file
162
src/librustc_resolve/error_reporting.rs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -85,7 +85,7 @@ use macros::{InvocationData, LegacyBinding, ParentScope};
|
||||||
// NB: This module needs to be declared first so diagnostics are
|
// NB: This module needs to be declared first so diagnostics are
|
||||||
// registered before they are used.
|
// registered before they are used.
|
||||||
mod diagnostics;
|
mod diagnostics;
|
||||||
|
mod error_reporting;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod check_unused;
|
mod check_unused;
|
||||||
mod build_reduced_graph;
|
mod build_reduced_graph;
|
||||||
|
|
|
@ -957,17 +957,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
PathResult::Failed(span, msg, true) => {
|
PathResult::Failed(span, msg, true) => {
|
||||||
let (mut self_path, mut self_result) = (module_path.clone(), None);
|
return if let Some(suggested_path) = self.make_path_suggestion(
|
||||||
let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
|
span, module_path.clone()
|
||||||
ident.name != keywords::CrateRoot.name();
|
) {
|
||||||
if !self_path.is_empty() && !is_special(self_path[0]) &&
|
Some((
|
||||||
!(self_path.len() > 1 && is_special(self_path[1])) {
|
span,
|
||||||
self_path[0].name = keywords::SelfValue.name();
|
format!("Did you mean `{}`?", names_to_string(&suggested_path[..]))
|
||||||
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[..]))))
|
|
||||||
} else {
|
} else {
|
||||||
Some((span, msg))
|
Some((span, msg))
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,15 +19,15 @@ mod a {
|
||||||
mod b {
|
mod b {
|
||||||
use alloc::HashMap;
|
use alloc::HashMap;
|
||||||
//~^ ERROR unresolved import `alloc` [E0432]
|
//~^ ERROR unresolved import `alloc` [E0432]
|
||||||
//~| Did you mean `a::alloc`?
|
//~| Did you mean `super::alloc`?
|
||||||
mod c {
|
mod c {
|
||||||
use alloc::HashMap;
|
use alloc::HashMap;
|
||||||
//~^ ERROR unresolved import `alloc` [E0432]
|
//~^ ERROR unresolved import `alloc` [E0432]
|
||||||
//~| Did you mean `a::alloc`?
|
//~| Did you mean `std::alloc`?
|
||||||
mod d {
|
mod d {
|
||||||
use alloc::HashMap;
|
use alloc::HashMap;
|
||||||
//~^ ERROR unresolved import `alloc` [E0432]
|
//~^ ERROR unresolved import `alloc` [E0432]
|
||||||
//~| Did you mean `a::alloc`?
|
//~| Did you mean `std::alloc`?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,19 @@ error[E0432]: unresolved import `alloc`
|
||||||
--> $DIR/resolve_self_super_hint.rs:20:13
|
--> $DIR/resolve_self_super_hint.rs:20:13
|
||||||
|
|
|
|
||||||
LL | use alloc::HashMap;
|
LL | use alloc::HashMap;
|
||||||
| ^^^^^ Did you mean `a::alloc`?
|
| ^^^^^ Did you mean `super::alloc`?
|
||||||
|
|
||||||
error[E0432]: unresolved import `alloc`
|
error[E0432]: unresolved import `alloc`
|
||||||
--> $DIR/resolve_self_super_hint.rs:24:17
|
--> $DIR/resolve_self_super_hint.rs:24:17
|
||||||
|
|
|
|
||||||
LL | use alloc::HashMap;
|
LL | use alloc::HashMap;
|
||||||
| ^^^^^ Did you mean `a::alloc`?
|
| ^^^^^ Did you mean `std::alloc`?
|
||||||
|
|
||||||
error[E0432]: unresolved import `alloc`
|
error[E0432]: unresolved import `alloc`
|
||||||
--> $DIR/resolve_self_super_hint.rs:28:21
|
--> $DIR/resolve_self_super_hint.rs:28:21
|
||||||
|
|
|
|
||||||
LL | use alloc::HashMap;
|
LL | use alloc::HashMap;
|
||||||
| ^^^^^ Did you mean `a::alloc`?
|
| ^^^^^ Did you mean `std::alloc`?
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
|
15
src/test/ui/rust-2018/auxiliary/baz.rs
Normal file
15
src/test/ui/rust-2018/auxiliary/baz.rs
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ error[E0432]: unresolved import `alloc`
|
||||||
--> $DIR/issue-54006.rs:16:5
|
--> $DIR/issue-54006.rs:16:5
|
||||||
|
|
|
|
||||||
LL | use alloc::vec;
|
LL | use alloc::vec;
|
||||||
| ^^^^^ Could not find `alloc` in `{{root}}`
|
| ^^^^^ Did you mean `std::alloc`?
|
||||||
|
|
||||||
error: cannot determine resolution for the macro `vec`
|
error: cannot determine resolution for the macro `vec`
|
||||||
--> $DIR/issue-54006.rs:20:18
|
--> $DIR/issue-54006.rs:20:18
|
||||||
|
|
31
src/test/ui/rust-2018/local-path-suggestions.rs
Normal file
31
src/test/ui/rust-2018/local-path-suggestions.rs
Normal file
|
@ -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() { }
|
21
src/test/ui/rust-2018/local-path-suggestions.stderr
Normal file
21
src/test/ui/rust-2018/local-path-suggestions.stderr
Normal file
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue