Skip to content

Commit 4bf883b

Browse files
committed
Auto merge of #54391 - davidtwco:issue-54230, r=petrochenkov
suggest `crate::...` for "local" paths in 2018 Fixes #54230. 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. r? @nikomatsakis
2 parents 5597ee8 + 5872d3e commit 4bf883b

11 files changed

+296
-19
lines changed
+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use {CrateLint, PathResult};
12+
13+
use std::collections::BTreeSet;
14+
15+
use syntax::ast::Ident;
16+
use syntax::symbol::{keywords, Symbol};
17+
use syntax_pos::Span;
18+
19+
use resolve_imports::ImportResolver;
20+
21+
impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
22+
/// Add suggestions for a path that cannot be resolved.
23+
pub(crate) fn make_path_suggestion(
24+
&mut self,
25+
span: Span,
26+
path: Vec<Ident>
27+
) -> Option<Vec<Ident>> {
28+
debug!("make_path_suggestion: span={:?} path={:?}", span, path);
29+
// If we don't have a path to suggest changes to, then return.
30+
if path.is_empty() {
31+
return None;
32+
}
33+
34+
// Check whether a ident is a path segment that is not root.
35+
let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
36+
ident.name != keywords::CrateRoot.name();
37+
38+
match (path.get(0), path.get(1)) {
39+
// Make suggestions that require at least two non-special path segments.
40+
(Some(fst), Some(snd)) if !is_special(*fst) && !is_special(*snd) => {
41+
debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);
42+
43+
self.make_missing_self_suggestion(span, path.clone())
44+
.or_else(|| self.make_missing_crate_suggestion(span, path.clone()))
45+
.or_else(|| self.make_missing_super_suggestion(span, path.clone()))
46+
.or_else(|| self.make_external_crate_suggestion(span, path.clone()))
47+
},
48+
_ => None,
49+
}
50+
}
51+
52+
/// Suggest a missing `self::` if that resolves to an correct module.
53+
///
54+
/// ```
55+
/// |
56+
/// LL | use foo::Bar;
57+
/// | ^^^ Did you mean `self::foo`?
58+
/// ```
59+
fn make_missing_self_suggestion(
60+
&mut self,
61+
span: Span,
62+
mut path: Vec<Ident>
63+
) -> Option<Vec<Ident>> {
64+
// Replace first ident with `self` and check if that is valid.
65+
path[0].name = keywords::SelfValue.name();
66+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
67+
debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
68+
if let PathResult::Module(..) = result {
69+
Some(path)
70+
} else {
71+
None
72+
}
73+
}
74+
75+
/// Suggest a missing `crate::` if that resolves to an correct module.
76+
///
77+
/// ```
78+
/// |
79+
/// LL | use foo::Bar;
80+
/// | ^^^ Did you mean `crate::foo`?
81+
/// ```
82+
fn make_missing_crate_suggestion(
83+
&mut self,
84+
span: Span,
85+
mut path: Vec<Ident>
86+
) -> Option<Vec<Ident>> {
87+
// Replace first ident with `crate` and check if that is valid.
88+
path[0].name = keywords::Crate.name();
89+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
90+
debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result);
91+
if let PathResult::Module(..) = result {
92+
Some(path)
93+
} else {
94+
None
95+
}
96+
}
97+
98+
/// Suggest a missing `super::` if that resolves to an correct module.
99+
///
100+
/// ```
101+
/// |
102+
/// LL | use foo::Bar;
103+
/// | ^^^ Did you mean `super::foo`?
104+
/// ```
105+
fn make_missing_super_suggestion(
106+
&mut self,
107+
span: Span,
108+
mut path: Vec<Ident>
109+
) -> Option<Vec<Ident>> {
110+
// Replace first ident with `crate` and check if that is valid.
111+
path[0].name = keywords::Super.name();
112+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
113+
debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result);
114+
if let PathResult::Module(..) = result {
115+
Some(path)
116+
} else {
117+
None
118+
}
119+
}
120+
121+
/// Suggest a missing external crate name if that resolves to an correct module.
122+
///
123+
/// ```
124+
/// |
125+
/// LL | use foobar::Baz;
126+
/// | ^^^^^^ Did you mean `baz::foobar`?
127+
/// ```
128+
///
129+
/// Used when importing a submodule of an external crate but missing that crate's
130+
/// name as the first part of path.
131+
fn make_external_crate_suggestion(
132+
&mut self,
133+
span: Span,
134+
mut path: Vec<Ident>
135+
) -> Option<Vec<Ident>> {
136+
// Need to clone else we can't call `resolve_path` without a borrow error. We also store
137+
// into a `BTreeMap` so we can get consistent ordering (and therefore the same diagnostic)
138+
// each time.
139+
let external_crate_names: BTreeSet<Symbol> = self.resolver.session.extern_prelude
140+
.clone().drain().collect();
141+
142+
// Insert a new path segment that we can replace.
143+
let new_path_segment = path[0].clone();
144+
path.insert(1, new_path_segment);
145+
146+
// Iterate in reverse so that we start with crates at the end of the alphabet. This means
147+
// that we'll always get `std` before `core`.
148+
for name in external_crate_names.iter().rev() {
149+
let ident = Ident::with_empty_ctxt(*name);
150+
// Calling `maybe_process_path_extern` ensures that we're only running `resolve_path`
151+
// on a crate name that won't ICE.
152+
if let Some(_) = self.crate_loader.maybe_process_path_extern(*name, ident.span) {
153+
// Replace the first after root (a placeholder we inserted) with a crate name
154+
// and check if that is valid.
155+
path[1].name = *name;
156+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
157+
debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
158+
name, path, result);
159+
if let PathResult::Module(..) = result {
160+
return Some(path)
161+
}
162+
}
163+
}
164+
165+
// Remove our placeholder segment.
166+
path.remove(1);
167+
None
168+
}
169+
}

src/librustc_resolve/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ use macros::{InvocationData, LegacyBinding, ParentScope};
8484
// NB: This module needs to be declared first so diagnostics are
8585
// registered before they are used.
8686
mod diagnostics;
87-
87+
mod error_reporting;
8888
mod macros;
8989
mod check_unused;
9090
mod build_reduced_graph;

src/librustc_resolve/resolve_imports.rs

+7-11
Original file line numberDiff line numberDiff line change
@@ -958,17 +958,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
958958
return None;
959959
}
960960
PathResult::Failed(span, msg, true) => {
961-
let (mut self_path, mut self_result) = (module_path.clone(), None);
962-
let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
963-
ident.name != keywords::CrateRoot.name();
964-
if !self_path.is_empty() && !is_special(self_path[0]) &&
965-
!(self_path.len() > 1 && is_special(self_path[1])) {
966-
self_path[0].name = keywords::SelfValue.name();
967-
self_result = Some(self.resolve_path(None, &self_path, None, false,
968-
span, CrateLint::No));
969-
}
970-
return if let Some(PathResult::Module(..)) = self_result {
971-
Some((span, format!("Did you mean `{}`?", names_to_string(&self_path[..]))))
961+
return if let Some(suggested_path) = self.make_path_suggestion(
962+
span, module_path.clone()
963+
) {
964+
Some((
965+
span,
966+
format!("Did you mean `{}`?", names_to_string(&suggested_path[..]))
967+
))
972968
} else {
973969
Some((span, msg))
974970
};

src/test/ui/resolve_self_super_hint.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ mod a {
1919
mod b {
2020
use alloc::HashMap;
2121
//~^ ERROR unresolved import `alloc` [E0432]
22-
//~| Did you mean `a::alloc`?
22+
//~| Did you mean `super::alloc`?
2323
mod c {
2424
use alloc::HashMap;
2525
//~^ ERROR unresolved import `alloc` [E0432]
26-
//~| Did you mean `a::alloc`?
26+
//~| Did you mean `std::alloc`?
2727
mod d {
2828
use alloc::HashMap;
2929
//~^ ERROR unresolved import `alloc` [E0432]
30-
//~| Did you mean `a::alloc`?
30+
//~| Did you mean `std::alloc`?
3131
}
3232
}
3333
}

src/test/ui/resolve_self_super_hint.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ error[E0432]: unresolved import `alloc`
88
--> $DIR/resolve_self_super_hint.rs:20:13
99
|
1010
LL | use alloc::HashMap;
11-
| ^^^^^ Did you mean `a::alloc`?
11+
| ^^^^^ Did you mean `super::alloc`?
1212

1313
error[E0432]: unresolved import `alloc`
1414
--> $DIR/resolve_self_super_hint.rs:24:17
1515
|
1616
LL | use alloc::HashMap;
17-
| ^^^^^ Did you mean `a::alloc`?
17+
| ^^^^^ Did you mean `std::alloc`?
1818

1919
error[E0432]: unresolved import `alloc`
2020
--> $DIR/resolve_self_super_hint.rs:28:21
2121
|
2222
LL | use alloc::HashMap;
23-
| ^^^^^ Did you mean `a::alloc`?
23+
| ^^^^^ Did you mean `std::alloc`?
2424

2525
error: aborting due to 4 previous errors
2626

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// This file is used as part of the local-path-suggestions.rs test.
12+
13+
pub mod foobar {
14+
pub struct Baz;
15+
}

src/test/ui/rust-2018/issue-54006.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0432]: unresolved import `alloc`
22
--> $DIR/issue-54006.rs:16:5
33
|
44
LL | use alloc::vec;
5-
| ^^^^^ Could not find `alloc` in `{{root}}`
5+
| ^^^^^ Did you mean `std::alloc`?
66

77
error: cannot determine resolution for the macro `vec`
88
--> $DIR/issue-54006.rs:20:18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:baz.rs
12+
// compile-flags:--extern baz
13+
// edition:2015
14+
15+
// This test exists to demonstrate the behaviour of the import suggestions
16+
// from the `local-path-suggestions-2018.rs` test when not using the 2018 edition.
17+
18+
extern crate baz as aux_baz;
19+
20+
mod foo {
21+
pub type Bar = u32;
22+
}
23+
24+
mod baz {
25+
use foo::Bar;
26+
27+
fn baz() {
28+
let x: Bar = 22;
29+
}
30+
}
31+
32+
use foo::Bar;
33+
34+
use foobar::Baz;
35+
36+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0432]: unresolved import `foobar`
2+
--> $DIR/local-path-suggestions-2015.rs:34:5
3+
|
4+
LL | use foobar::Baz;
5+
| ^^^^^^ Did you mean `aux_baz::foobar`?
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0432`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:baz.rs
12+
// compile-flags:--extern baz
13+
// edition:2018
14+
15+
mod foo {
16+
pub type Bar = u32;
17+
}
18+
19+
mod baz {
20+
use foo::Bar;
21+
22+
fn baz() {
23+
let x: Bar = 22;
24+
}
25+
}
26+
27+
use foo::Bar;
28+
29+
use foobar::Baz;
30+
31+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0432]: unresolved import `foo`
2+
--> $DIR/local-path-suggestions-2018.rs:20:9
3+
|
4+
LL | use foo::Bar;
5+
| ^^^ Did you mean `crate::foo`?
6+
7+
error[E0432]: unresolved import `foo`
8+
--> $DIR/local-path-suggestions-2018.rs:27:5
9+
|
10+
LL | use foo::Bar;
11+
| ^^^ Did you mean `self::foo`?
12+
13+
error[E0432]: unresolved import `foobar`
14+
--> $DIR/local-path-suggestions-2018.rs:29:5
15+
|
16+
LL | use foobar::Baz;
17+
| ^^^^^^ Did you mean `baz::foobar`?
18+
19+
error: aborting due to 3 previous errors
20+
21+
For more information about this error, try `rustc --explain E0432`.

0 commit comments

Comments
 (0)