Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

suggest crate::... for "local" paths in 2018 #54391

Merged
merged 4 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions src/librustc_resolve/error_reporting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// 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 std::collections::BTreeSet;

use syntax::ast::Ident;
use syntax::symbol::{keywords, Symbol};
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. We also store
// into a `BTreeMap` so we can get consistent ordering (and therefore the same diagnostic)
// each time.
let external_crate_names: BTreeSet<Symbol> = self.resolver.session.extern_prelude
.clone().drain().collect();

// Insert a new path segment that we can replace.
let new_path_segment = path[0].clone();
path.insert(1, new_path_segment);

// Iterate in reverse so that we start with crates at the end of the alphabet. This means
// that we'll always get `std` before `core`.
for name in external_crate_names.iter().rev() {
let ident = Ident::with_empty_ctxt(*name);
// Calling `maybe_process_path_extern` ensures that we're only running `resolve_path`
// on a crate name that won't ICE.
if let Some(_) = self.crate_loader.maybe_process_path_extern(*name, ident.span) {
// 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
}
}
2 changes: 1 addition & 1 deletion src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 7 additions & 11 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
};
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/resolve_self_super_hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`?
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/resolve_self_super_hint.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
15 changes: 15 additions & 0 deletions src/test/ui/rust-2018/auxiliary/baz.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 1 addition & 1 deletion src/test/ui/rust-2018/issue-54006.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions src/test/ui/rust-2018/local-path-suggestions-2015.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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:2015

// This test exists to demonstrate the behaviour of the import suggestions
// from the `local-path-suggestions-2018.rs` test when not using the 2018 edition.

extern crate baz as aux_baz;

mod foo {
pub type Bar = u32;
}

mod baz {
use foo::Bar;

fn baz() {
let x: Bar = 22;
}
}

use foo::Bar;

use foobar::Baz;

fn main() { }
9 changes: 9 additions & 0 deletions src/test/ui/rust-2018/local-path-suggestions-2015.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0432]: unresolved import `foobar`
--> $DIR/local-path-suggestions-2015.rs:34:5
|
LL | use foobar::Baz;
| ^^^^^^ Did you mean `aux_baz::foobar`?

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
31 changes: 31 additions & 0 deletions src/test/ui/rust-2018/local-path-suggestions-2018.rs
Original file line number Diff line number Diff line change
@@ -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 {
pub type Bar = u32;
}

mod baz {
use foo::Bar;

fn baz() {
let x: Bar = 22;
}
}

use foo::Bar;

use foobar::Baz;

fn main() { }
21 changes: 21 additions & 0 deletions src/test/ui/rust-2018/local-path-suggestions-2018.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0432]: unresolved import `foo`
--> $DIR/local-path-suggestions-2018.rs:20:9
|
LL | use foo::Bar;
| ^^^ Did you mean `crate::foo`?

error[E0432]: unresolved import `foo`
--> $DIR/local-path-suggestions-2018.rs:27:5
|
LL | use foo::Bar;
| ^^^ Did you mean `self::foo`?

error[E0432]: unresolved import `foobar`
--> $DIR/local-path-suggestions-2018.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`.