Skip to content

Commit cfe8155

Browse files
committed
resolve: Recover "did you mean" suggestions in imports
1 parent 4c5d822 commit cfe8155

File tree

5 files changed

+36
-50
lines changed

5 files changed

+36
-50
lines changed

src/librustc_resolve/error_reporting.rs

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,40 @@
1111
use {CrateLint, PathResult, Segment};
1212
use macros::ParentScope;
1313

14-
use std::collections::BTreeSet;
15-
1614
use syntax::ast::Ident;
17-
use syntax::symbol::{keywords, Symbol};
15+
use syntax::symbol::keywords;
1816
use syntax_pos::Span;
1917

2018
use resolve_imports::ImportResolver;
19+
use std::cmp::Reverse;
2120

2221
impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
2322
/// Add suggestions for a path that cannot be resolved.
2423
pub(crate) fn make_path_suggestion(
2524
&mut self,
2625
span: Span,
27-
path: Vec<Segment>,
26+
mut path: Vec<Segment>,
2827
parent_scope: &ParentScope<'b>,
2928
) -> Option<(Vec<Segment>, Option<String>)> {
3029
debug!("make_path_suggestion: span={:?} path={:?}", span, path);
31-
// If we don't have a path to suggest changes to, then return.
32-
if path.is_empty() {
33-
return None;
34-
}
35-
36-
// Check whether a ident is a path segment that is not root.
37-
let is_special = |ident: Ident| ident.is_path_segment_keyword() &&
38-
ident.name != keywords::CrateRoot.name();
3930

4031
match (path.get(0), path.get(1)) {
41-
// Make suggestions that require at least two non-special path segments.
42-
(Some(fst), Some(snd)) if !is_special(fst.ident) && !is_special(snd.ident) => {
43-
debug!("make_path_suggestion: fst={:?} snd={:?}", fst, snd);
44-
45-
self.make_missing_self_suggestion(span, path.clone(), parent_scope)
46-
.or_else(|| self.make_missing_crate_suggestion(span, path.clone(),
47-
parent_scope))
48-
.or_else(|| self.make_missing_super_suggestion(span, path.clone(),
49-
parent_scope))
50-
.or_else(|| self.make_external_crate_suggestion(span, path, parent_scope))
51-
},
52-
_ => None,
32+
// `{{root}}::ident::...` on both editions.
33+
// On 2015 `{{root}}` is usually added implicitly.
34+
(Some(fst), Some(snd)) if fst.name == keywords::CrateRoot.name() &&
35+
!snd.is_path_segment_keyword() => {}
36+
// `ident::...` on 2018
37+
(Some(fst), _) if self.session.rust_2018() && !fst.is_path_segment_keyword() => {
38+
// Insert a placeholder that's later replaced by `self`/`super`/etc.
39+
path.insert(0, keywords::Invalid.ident());
40+
}
41+
_ => return None,
5342
}
43+
44+
self.make_missing_self_suggestion(span, path.clone(), parent_scope)
45+
.or_else(|| self.make_missing_crate_suggestion(span, path.clone(), parent_scope))
46+
.or_else(|| self.make_missing_super_suggestion(span, path.clone(), parent_scope))
47+
.or_else(|| self.make_external_crate_suggestion(span, path, parent_scope))
5448
}
5549

5650
/// Suggest a missing `self::` if that resolves to an correct module.
@@ -148,22 +142,20 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
148142
mut path: Vec<Segment>,
149143
parent_scope: &ParentScope<'b>,
150144
) -> Option<(Vec<Segment>, Option<String>)> {
151-
// Need to clone else we can't call `resolve_path` without a borrow error. We also store
152-
// into a `BTreeMap` so we can get consistent ordering (and therefore the same diagnostic)
153-
// each time.
154-
let external_crate_names: BTreeSet<Symbol> = self.resolver.extern_prelude
155-
.iter().map(|(ident, _)| ident.name).collect();
145+
if !self.session.rust_2018() {
146+
return None;
147+
}
156148

157-
// Insert a new path segment that we can replace.
158-
let new_path_segment = path[0].clone();
159-
path.insert(1, new_path_segment);
149+
// Sort extern crate names in reverse order to get
150+
// 1) some consistent ordering for emitted dignostics and
151+
// 2) `std` suggestions before `core` suggestions.
152+
let mut extern_crate_names =
153+
self.resolver.extern_prelude.iter().map(|(ident, _)| ident.name).collect::<Vec<_>>();
154+
extern_crate_names.sort_by_key(|name| Reverse(name.as_str()));
160155

161-
// Iterate in reverse so that we start with crates at the end of the alphabet. This means
162-
// that we'll always get `std` before `core`.
163-
for name in external_crate_names.iter().rev() {
164-
// Replace the first after root (a placeholder we inserted) with a crate name
165-
// and check if that is valid.
166-
path[1].ident.name = *name;
156+
for name in extern_crate_names.into_iter() {
157+
// Replace first ident with a crate name and check if that is valid.
158+
path[0].ident.name = name;
167159
let result = self.resolve_path(&path, None, parent_scope, false, span, CrateLint::No);
168160
debug!("make_external_crate_suggestion: name={:?} path={:?} result={:?}",
169161
name, path, result);

src/test/ui/resolve_self_super_hint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ mod a {
2323
mod c {
2424
use alloc::HashMap;
2525
//~^ ERROR unresolved import `alloc` [E0432]
26-
//~| Did you mean `std::alloc`?
26+
//~| Did you mean `a::alloc`?
2727
mod d {
2828
use alloc::HashMap;
2929
//~^ ERROR unresolved import `alloc` [E0432]
30-
//~| Did you mean `std::alloc`?
30+
//~| Did you mean `a::alloc`?
3131
}
3232
}
3333
}

src/test/ui/resolve_self_super_hint.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ error[E0432]: unresolved import `alloc`
1414
--> $DIR/resolve_self_super_hint.rs:24:17
1515
|
1616
LL | use alloc::HashMap;
17-
| ^^^^^ Did you mean `std::alloc`?
17+
| ^^^^^ Did you mean `a::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 `std::alloc`?
23+
| ^^^^^ Did you mean `a::alloc`?
2424

2525
error: aborting due to 4 previous errors
2626

src/test/ui/rust-2018/local-path-suggestions-2018.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod foo {
1616
pub type Bar = u32;
1717
}
1818

19-
mod baz {
19+
mod bazz {
2020
use foo::Bar;
2121

2222
fn baz() {

src/test/ui/rust-2018/local-path-suggestions-2018.stderr

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,12 @@ LL | use foo::Bar;
66
|
77
= note: `use` statements changed in Rust 2018; read more at <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-clarity.html>
88

9-
error[E0432]: unresolved import `foo`
10-
--> $DIR/local-path-suggestions-2018.rs:27:5
11-
|
12-
LL | use foo::Bar;
13-
| ^^^ Did you mean `self::foo`?
14-
159
error[E0432]: unresolved import `foobar`
1610
--> $DIR/local-path-suggestions-2018.rs:29:5
1711
|
1812
LL | use foobar::Baz;
1913
| ^^^^^^ Did you mean `baz::foobar`?
2014

21-
error: aborting due to 3 previous errors
15+
error: aborting due to 2 previous errors
2216

2317
For more information about this error, try `rustc --explain E0432`.

0 commit comments

Comments
 (0)