Skip to content

Commit a2ffe02

Browse files
authored
Rollup merge of rust-lang#54391 - davidtwco:issue-54230, r=petrochenkov
suggest `crate::...` for "local" paths in 2018 Fixes rust-lang#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 294e467 + 1525e97 commit a2ffe02

11 files changed

+289
-19
lines changed
+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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 syntax::ast::Ident;
14+
use syntax::symbol::keywords;
15+
use syntax_pos::Span;
16+
17+
use resolve_imports::ImportResolver;
18+
19+
impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
20+
/// Add suggestions for a path that cannot be resolved.
21+
pub(crate) fn make_path_suggestion(
22+
&mut self,
23+
span: Span,
24+
path: Vec<Ident>
25+
) -> Option<Vec<Ident>> {
26+
debug!("make_path_suggestion: span={:?} path={:?}", span, path);
27+
// If we don't have a path to suggest changes to, then return.
28+
if path.is_empty() {
29+
return None;
30+
}
31+
32+
// Check whether a ident is a path segment that is not root.
33+
let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
34+
ident.name != keywords::CrateRoot.name();
35+
36+
match (path.get(0), path.get(1)) {
37+
// Make suggestions that require at least two non-special path segments.
38+
(Some(fst), Some(snd)) if !is_special(*fst) && !is_special(*snd) => {
39+
debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);
40+
41+
self.make_missing_self_suggestion(span, path.clone())
42+
.or_else(|| self.make_missing_crate_suggestion(span, path.clone()))
43+
.or_else(|| self.make_missing_super_suggestion(span, path.clone()))
44+
.or_else(|| self.make_external_crate_suggestion(span, path.clone()))
45+
},
46+
_ => None,
47+
}
48+
}
49+
50+
/// Suggest a missing `self::` if that resolves to an correct module.
51+
///
52+
/// ```
53+
/// |
54+
/// LL | use foo::Bar;
55+
/// | ^^^ Did you mean `self::foo`?
56+
/// ```
57+
fn make_missing_self_suggestion(
58+
&mut self,
59+
span: Span,
60+
mut path: Vec<Ident>
61+
) -> Option<Vec<Ident>> {
62+
// Replace first ident with `self` and check if that is valid.
63+
path[0].name = keywords::SelfValue.name();
64+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
65+
debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
66+
if let PathResult::Module(..) = result {
67+
Some(path)
68+
} else {
69+
None
70+
}
71+
}
72+
73+
/// Suggest a missing `crate::` if that resolves to an correct module.
74+
///
75+
/// ```
76+
/// |
77+
/// LL | use foo::Bar;
78+
/// | ^^^ Did you mean `crate::foo`?
79+
/// ```
80+
fn make_missing_crate_suggestion(
81+
&mut self,
82+
span: Span,
83+
mut path: Vec<Ident>
84+
) -> Option<Vec<Ident>> {
85+
// Replace first ident with `crate` and check if that is valid.
86+
path[0].name = keywords::Crate.name();
87+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
88+
debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result);
89+
if let PathResult::Module(..) = result {
90+
Some(path)
91+
} else {
92+
None
93+
}
94+
}
95+
96+
/// Suggest a missing `super::` if that resolves to an correct module.
97+
///
98+
/// ```
99+
/// |
100+
/// LL | use foo::Bar;
101+
/// | ^^^ Did you mean `super::foo`?
102+
/// ```
103+
fn make_missing_super_suggestion(
104+
&mut self,
105+
span: Span,
106+
mut path: Vec<Ident>
107+
) -> Option<Vec<Ident>> {
108+
// Replace first ident with `crate` and check if that is valid.
109+
path[0].name = keywords::Super.name();
110+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
111+
debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result);
112+
if let PathResult::Module(..) = result {
113+
Some(path)
114+
} else {
115+
None
116+
}
117+
}
118+
119+
/// Suggest a missing external crate name if that resolves to an correct module.
120+
///
121+
/// ```
122+
/// |
123+
/// LL | use foobar::Baz;
124+
/// | ^^^^^^ Did you mean `baz::foobar`?
125+
/// ```
126+
///
127+
/// Used when importing a submodule of an external crate but missing that crate's
128+
/// name as the first part of path.
129+
fn make_external_crate_suggestion(
130+
&mut self,
131+
span: Span,
132+
mut path: Vec<Ident>
133+
) -> Option<Vec<Ident>> {
134+
// Need to clone else we can't call `resolve_path` without a borrow error.
135+
let external_crate_names = self.extern_prelude.clone();
136+
137+
// Insert a new path segment that we can replace.
138+
let new_path_segment = path[0].clone();
139+
path.insert(1, new_path_segment);
140+
141+
for name in &external_crate_names {
142+
let ident = Ident::with_empty_ctxt(*name);
143+
// Calling `maybe_process_path_extern` ensures that we're only running `resolve_path`
144+
// on a crate name that won't ICE.
145+
if let Some(_) = self.crate_loader.maybe_process_path_extern(*name, ident.span) {
146+
// Replace the first after root (a placeholder we inserted) with a crate name
147+
// and check if that is valid.
148+
path[1].name = *name;
149+
let result = self.resolve_path(None, &path, None, false, span, CrateLint::No);
150+
debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
151+
name, path, result);
152+
if let PathResult::Module(..) = result {
153+
return Some(path)
154+
}
155+
}
156+
}
157+
158+
// Remove our placeholder segment.
159+
path.remove(1);
160+
None
161+
}
162+
}

src/librustc_resolve/lib.rs

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

src/librustc_resolve/resolve_imports.rs

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

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)