Skip to content

Commit 404ad50

Browse files
committed
clarify resolve typo suggestion
Include the kind of the binding that we're suggesting, and use a structured suggestion.
1 parent b8c8f0b commit 404ad50

40 files changed

+249
-125
lines changed

src/librustc/hir/def.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl Def {
284284
}
285285
}
286286

287-
/// A human readable kind name
287+
/// A human readable name for the def kind ("function", "module", etc.).
288288
pub fn kind_name(&self) -> &'static str {
289289
match *self {
290290
Def::Fn(..) => "function",
@@ -324,6 +324,7 @@ impl Def {
324324
}
325325
}
326326

327+
/// An English article for the def.
327328
pub fn article(&self) -> &'static str {
328329
match *self {
329330
Def::AssociatedTy(..) | Def::AssociatedConst(..) | Def::AssociatedExistential(..) |

src/librustc_resolve/lib.rs

+68-18
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ struct BindingError {
120120
target: BTreeSet<Span>,
121121
}
122122

123+
struct TypoSuggestion {
124+
candidate: Symbol,
125+
126+
/// The kind of the binding ("crate", "module", etc.)
127+
kind: &'static str,
128+
129+
/// An appropriate article to refer to the binding ("a", "an", etc.)
130+
article: &'static str,
131+
}
132+
123133
impl PartialOrd for BindingError {
124134
fn partial_cmp(&self, other: &BindingError) -> Option<cmp::Ordering> {
125135
Some(self.cmp(other))
@@ -1448,7 +1458,7 @@ impl PrimitiveTypeTable {
14481458
}
14491459
}
14501460

1451-
#[derive(Default, Clone)]
1461+
#[derive(Debug, Default, Clone)]
14521462
pub struct ExternPreludeEntry<'a> {
14531463
extern_crate_item: Option<&'a NameBinding<'a>>,
14541464
pub introduced_by_item: bool,
@@ -3277,8 +3287,19 @@ impl<'a> Resolver<'a> {
32773287
let mut levenshtein_worked = false;
32783288

32793289
// Try Levenshtein algorithm.
3280-
if let Some(candidate) = this.lookup_typo_candidate(path, ns, is_expected, span) {
3281-
err.span_label(ident_span, format!("did you mean `{}`?", candidate));
3290+
let suggestion = this.lookup_typo_candidate(path, ns, is_expected, span);
3291+
if let Some(suggestion) = suggestion {
3292+
let msg = format!(
3293+
"{} {} with a similar name exists",
3294+
suggestion.article, suggestion.kind
3295+
);
3296+
err.span_suggestion_with_applicability(
3297+
ident_span,
3298+
&msg,
3299+
suggestion.candidate.to_string(),
3300+
Applicability::MaybeIncorrect,
3301+
);
3302+
32823303
levenshtein_worked = true;
32833304
}
32843305

@@ -4162,19 +4183,25 @@ impl<'a> Resolver<'a> {
41624183
None
41634184
}
41644185

4165-
fn lookup_typo_candidate<FilterFn>(&mut self,
4166-
path: &[Segment],
4167-
ns: Namespace,
4168-
filter_fn: FilterFn,
4169-
span: Span)
4170-
-> Option<Symbol>
4171-
where FilterFn: Fn(Def) -> bool
4186+
fn lookup_typo_candidate<FilterFn>(
4187+
&mut self,
4188+
path: &[Segment],
4189+
ns: Namespace,
4190+
filter_fn: FilterFn,
4191+
span: Span,
4192+
) -> Option<TypoSuggestion>
4193+
where
4194+
FilterFn: Fn(Def) -> bool,
41724195
{
4173-
let add_module_candidates = |module: Module, names: &mut Vec<Name>| {
4196+
let add_module_candidates = |module: Module, names: &mut Vec<TypoSuggestion>| {
41744197
for (&(ident, _), resolution) in module.resolutions.borrow().iter() {
41754198
if let Some(binding) = resolution.borrow().binding {
41764199
if filter_fn(binding.def()) {
4177-
names.push(ident.name);
4200+
names.push(TypoSuggestion {
4201+
candidate: ident.name,
4202+
article: binding.def().article(),
4203+
kind: binding.def().kind_name(),
4204+
});
41784205
}
41794206
}
41804207
}
@@ -4188,7 +4215,11 @@ impl<'a> Resolver<'a> {
41884215
// Locals and type parameters
41894216
for (ident, def) in &rib.bindings {
41904217
if filter_fn(*def) {
4191-
names.push(ident.name);
4218+
names.push(TypoSuggestion {
4219+
candidate: ident.name,
4220+
article: def.article(),
4221+
kind: def.kind_name(),
4222+
});
41924223
}
41934224
}
41944225
// Items in scope
@@ -4201,7 +4232,13 @@ impl<'a> Resolver<'a> {
42014232
} else {
42024233
// Items from the prelude
42034234
if !module.no_implicit_prelude {
4204-
names.extend(self.extern_prelude.iter().map(|(ident, _)| ident.name));
4235+
names.extend(self.extern_prelude.iter().map(|(ident, _)| {
4236+
TypoSuggestion {
4237+
candidate: ident.name,
4238+
article: "a",
4239+
kind: "crate",
4240+
}
4241+
}));
42054242
if let Some(prelude) = self.prelude {
42064243
add_module_candidates(prelude, &mut names);
42074244
}
@@ -4213,7 +4250,13 @@ impl<'a> Resolver<'a> {
42134250
// Add primitive types to the mix
42144251
if filter_fn(Def::PrimTy(Bool)) {
42154252
names.extend(
4216-
self.primitive_type_table.primitive_types.iter().map(|(name, _)| name)
4253+
self.primitive_type_table.primitive_types.iter().map(|(name, _)| {
4254+
TypoSuggestion {
4255+
candidate: *name,
4256+
article: "a",
4257+
kind: "primitive type",
4258+
}
4259+
})
42174260
)
42184261
}
42194262
} else {
@@ -4230,9 +4273,16 @@ impl<'a> Resolver<'a> {
42304273

42314274
let name = path[path.len() - 1].ident.name;
42324275
// Make sure error reporting is deterministic.
4233-
names.sort_by_cached_key(|name| name.as_str());
4234-
match find_best_match_for_name(names.iter(), &name.as_str(), None) {
4235-
Some(found) if found != name => Some(found),
4276+
names.sort_by_cached_key(|suggestion| suggestion.candidate.as_str());
4277+
4278+
match find_best_match_for_name(
4279+
names.iter().map(|suggestion| &suggestion.candidate),
4280+
&name.as_str(),
4281+
None,
4282+
) {
4283+
Some(found) if found != name => names
4284+
.into_iter()
4285+
.find(|suggestion| suggestion.candidate == found),
42364286
_ => None,
42374287
}
42384288
}

src/librustc_resolve/macros.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,7 @@ impl<'a> Resolver<'a> {
10021002
};
10031003
let ident = Ident::new(Symbol::intern(name), span);
10041004
self.lookup_typo_candidate(&[Segment::from_ident(ident)], MacroNS, is_macro, span)
1005+
.map(|suggestion| suggestion.candidate)
10051006
});
10061007

10071008
if let Some(suggestion) = suggestion {

src/test/ui/associated-types/associated-types-eq-1.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0412]: cannot find type `A` in this scope
22
--> $DIR/associated-types-eq-1.rs:10:12
33
|
44
LL | let _: A = x.boo(); //~ ERROR cannot find type `A` in this scope
5-
| ^ did you mean `I`?
5+
| ^ help: a type parameter with a similar name exists: `I`
66

77
error: aborting due to previous error
88

src/test/ui/empty/empty-struct-braces-expr.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ error[E0423]: expected value, found struct `Empty1`
44
LL | let e1 = Empty1; //~ ERROR expected value, found struct `Empty1`
55
| ^^^^^^
66
| |
7-
| did you mean `XEmpty2`?
87
| did you mean `Empty1 { /* fields */ }`?
8+
| help: a unit struct with a similar name exists: `XEmpty2`
99

1010
error[E0423]: expected function, found struct `Empty1`
1111
--> $DIR/empty-struct-braces-expr.rs:16:14
1212
|
1313
LL | let e1 = Empty1(); //~ ERROR expected function, found struct `Empty1`
1414
| ^^^^^^
1515
| |
16-
| did you mean `XEmpty2`?
1716
| did you mean `Empty1 { /* fields */ }`?
17+
| help: a unit struct with a similar name exists: `XEmpty2`
1818

1919
error[E0423]: expected value, found struct variant `E::Empty3`
2020
--> $DIR/empty-struct-braces-expr.rs:17:14
@@ -34,17 +34,17 @@ error[E0423]: expected value, found struct `XEmpty1`
3434
LL | let xe1 = XEmpty1; //~ ERROR expected value, found struct `XEmpty1`
3535
| ^^^^^^^
3636
| |
37-
| did you mean `XEmpty2`?
3837
| did you mean `XEmpty1 { /* fields */ }`?
38+
| help: a unit struct with a similar name exists: `XEmpty2`
3939

4040
error[E0423]: expected function, found struct `XEmpty1`
4141
--> $DIR/empty-struct-braces-expr.rs:21:15
4242
|
4343
LL | let xe1 = XEmpty1(); //~ ERROR expected function, found struct `XEmpty1`
4444
| ^^^^^^^
4545
| |
46-
| did you mean `XEmpty2`?
4746
| did you mean `XEmpty1 { /* fields */ }`?
47+
| help: a unit struct with a similar name exists: `XEmpty2`
4848

4949
error[E0599]: no variant named `Empty3` found for type `empty_struct::XE` in the current scope
5050
--> $DIR/empty-struct-braces-expr.rs:22:19

src/test/ui/empty/empty-struct-braces-pat-1.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error[E0532]: expected unit struct/variant or constant, found struct variant `XE
1010
LL | XE::XEmpty3 => ()
1111
| ^^^^-------
1212
| | |
13-
| | did you mean `XEmpty4`?
13+
| | help: a unit variant with a similar name exists: `XEmpty4`
1414
| did you mean `XE::XEmpty3 { /* fields */ }`?
1515

1616
error: aborting due to 2 previous errors

src/test/ui/empty/empty-struct-braces-pat-2.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,35 @@ error[E0532]: expected tuple struct/variant, found struct `Empty1`
44
LL | Empty1() => () //~ ERROR expected tuple struct/variant, found struct `Empty1`
55
| ^^^^^^
66
| |
7-
| did you mean `XEmpty6`?
87
| did you mean `Empty1 { /* fields */ }`?
8+
| help: a tuple struct with a similar name exists: `XEmpty6`
99

1010
error[E0532]: expected tuple struct/variant, found struct `XEmpty1`
1111
--> $DIR/empty-struct-braces-pat-2.rs:18:9
1212
|
1313
LL | XEmpty1() => () //~ ERROR expected tuple struct/variant, found struct `XEmpty1`
1414
| ^^^^^^^
1515
| |
16-
| did you mean `XEmpty6`?
1716
| did you mean `XEmpty1 { /* fields */ }`?
17+
| help: a tuple struct with a similar name exists: `XEmpty6`
1818

1919
error[E0532]: expected tuple struct/variant, found struct `Empty1`
2020
--> $DIR/empty-struct-braces-pat-2.rs:21:9
2121
|
2222
LL | Empty1(..) => () //~ ERROR expected tuple struct/variant, found struct `Empty1`
2323
| ^^^^^^
2424
| |
25-
| did you mean `XEmpty6`?
2625
| did you mean `Empty1 { /* fields */ }`?
26+
| help: a tuple struct with a similar name exists: `XEmpty6`
2727

2828
error[E0532]: expected tuple struct/variant, found struct `XEmpty1`
2929
--> $DIR/empty-struct-braces-pat-2.rs:24:9
3030
|
3131
LL | XEmpty1(..) => () //~ ERROR expected tuple struct/variant, found struct `XEmpty1`
3232
| ^^^^^^^
3333
| |
34-
| did you mean `XEmpty6`?
3534
| did you mean `XEmpty1 { /* fields */ }`?
35+
| help: a tuple struct with a similar name exists: `XEmpty6`
3636

3737
error: aborting due to 4 previous errors
3838

src/test/ui/empty/empty-struct-braces-pat-3.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error[E0532]: expected tuple struct/variant, found struct variant `XE::XEmpty3`
1010
LL | XE::XEmpty3() => ()
1111
| ^^^^-------
1212
| | |
13-
| | did you mean `XEmpty5`?
13+
| | help: a tuple variant with a similar name exists: `XEmpty5`
1414
| did you mean `XE::XEmpty3 { /* fields */ }`?
1515

1616
error[E0532]: expected tuple struct/variant, found struct variant `E::Empty3`
@@ -25,7 +25,7 @@ error[E0532]: expected tuple struct/variant, found struct variant `XE::XEmpty3`
2525
LL | XE::XEmpty3(..) => ()
2626
| ^^^^-------
2727
| | |
28-
| | did you mean `XEmpty5`?
28+
| | help: a tuple variant with a similar name exists: `XEmpty5`
2929
| did you mean `XE::XEmpty3 { /* fields */ }`?
3030

3131
error: aborting due to 4 previous errors

src/test/ui/empty/empty-struct-tuple-pat.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ error[E0532]: expected unit struct/variant or constant, found tuple variant `XE:
2828
LL | XE::XEmpty5 => (),
2929
| ^^^^-------
3030
| |
31-
| did you mean `XEmpty4`?
31+
| help: a unit variant with a similar name exists: `XEmpty4`
3232

3333
error: aborting due to 4 previous errors
3434

src/test/ui/empty/empty-struct-unit-pat.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@ error[E0532]: expected tuple struct/variant, found unit struct `Empty2`
22
--> $DIR/empty-struct-unit-pat.rs:21:9
33
|
44
LL | Empty2() => () //~ ERROR expected tuple struct/variant, found unit struct `Empty2`
5-
| ^^^^^^ did you mean `XEmpty6`?
5+
| ^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
66

77
error[E0532]: expected tuple struct/variant, found unit struct `XEmpty2`
88
--> $DIR/empty-struct-unit-pat.rs:24:9
99
|
1010
LL | XEmpty2() => () //~ ERROR expected tuple struct/variant, found unit struct `XEmpty2`
11-
| ^^^^^^^ did you mean `XEmpty6`?
11+
| ^^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
1212

1313
error[E0532]: expected tuple struct/variant, found unit struct `Empty2`
1414
--> $DIR/empty-struct-unit-pat.rs:27:9
1515
|
1616
LL | Empty2(..) => () //~ ERROR expected tuple struct/variant, found unit struct `Empty2`
17-
| ^^^^^^ did you mean `XEmpty6`?
17+
| ^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
1818

1919
error[E0532]: expected tuple struct/variant, found unit struct `XEmpty2`
2020
--> $DIR/empty-struct-unit-pat.rs:30:9
2121
|
2222
LL | XEmpty2(..) => () //~ ERROR expected tuple struct/variant, found unit struct `XEmpty2`
23-
| ^^^^^^^ did you mean `XEmpty6`?
23+
| ^^^^^^^ help: a tuple struct with a similar name exists: `XEmpty6`
2424

2525
error[E0532]: expected tuple struct/variant, found unit variant `E::Empty4`
2626
--> $DIR/empty-struct-unit-pat.rs:34:9
@@ -34,7 +34,7 @@ error[E0532]: expected tuple struct/variant, found unit variant `XE::XEmpty4`
3434
LL | XE::XEmpty4() => (),
3535
| ^^^^-------
3636
| |
37-
| did you mean `XEmpty5`?
37+
| help: a tuple variant with a similar name exists: `XEmpty5`
3838

3939
error[E0532]: expected tuple struct/variant, found unit variant `E::Empty4`
4040
--> $DIR/empty-struct-unit-pat.rs:42:9
@@ -48,7 +48,7 @@ error[E0532]: expected tuple struct/variant, found unit variant `XE::XEmpty4`
4848
LL | XE::XEmpty4(..) => (),
4949
| ^^^^-------
5050
| |
51-
| did you mean `XEmpty5`?
51+
| help: a tuple variant with a similar name exists: `XEmpty5`
5252

5353
error: aborting due to 8 previous errors
5454

src/test/ui/error-codes/E0423.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ error[E0423]: expected function, found struct `Foo`
2222
LL | let f = Foo(); //~ ERROR E0423
2323
| ^^^
2424
| |
25-
| did you mean `foo`?
2625
| did you mean `Foo { /* fields */ }`?
26+
| help: a function with a similar name exists: `foo`
2727

2828
error[E0423]: expected value, found struct `S`
2929
--> $DIR/E0423.rs:12:32

src/test/ui/error-festival.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0425]: cannot find value `y` in this scope
22
--> $DIR/error-festival.rs:14:5
33
|
44
LL | y = 2;
5-
| ^ did you mean `x`?
5+
| ^ help: a local variable with a similar name exists: `x`
66

77
error[E0603]: constant `FOO` is private
88
--> $DIR/error-festival.rs:22:10

0 commit comments

Comments
 (0)