Skip to content

Commit 4963394

Browse files
author
Thomas Jespersen
committed
Change Levensthein-based method to a single suggestion
The convention for suggesting close matches is to provide at most one match (the closest one). Change the suggestions for misspelt method names to obey that.
1 parent 09defbc commit 4963394

File tree

5 files changed

+39
-26
lines changed

5 files changed

+39
-26
lines changed

src/librustc_typeck/check/method/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,24 @@ pub enum MethodError<'tcx> {
6868
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
6969
pub struct NoMatchData<'tcx> {
7070
pub static_candidates: Vec<CandidateSource>,
71-
pub lev_candidates: Vec<ty::AssociatedItem>,
7271
pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
7372
pub out_of_scope_traits: Vec<DefId>,
73+
pub lev_candidate: Option<ty::AssociatedItem>,
7474
pub mode: probe::Mode,
7575
}
7676

7777
impl<'tcx> NoMatchData<'tcx> {
7878
pub fn new(static_candidates: Vec<CandidateSource>,
79-
lev_candidates: Vec<ty::AssociatedItem>,
8079
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
8180
out_of_scope_traits: Vec<DefId>,
81+
lev_candidate: Option<ty::AssociatedItem>,
8282
mode: probe::Mode)
8383
-> Self {
8484
NoMatchData {
8585
static_candidates,
86-
lev_candidates,
8786
unsatisfied_predicates,
8887
out_of_scope_traits,
88+
lev_candidate,
8989
mode,
9090
}
9191
}

src/librustc_typeck/check/method/probe.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
2323
use rustc::util::nodemap::FxHashSet;
2424
use rustc::infer::{self, InferOk};
2525
use syntax::ast;
26-
use syntax::util::lev_distance::lev_distance;
26+
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
2727
use syntax_pos::Span;
2828
use rustc::hir;
2929
use std::mem;
@@ -248,7 +248,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
248248
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
249249
Vec::new(),
250250
Vec::new(),
251-
Vec::new(),
251+
None,
252252
mode)))
253253
}
254254
}
@@ -806,12 +806,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
806806
if let Some(def) = private_candidate {
807807
return Err(MethodError::PrivateMatch(def, out_of_scope_traits));
808808
}
809-
let lev_candidates = self.probe_for_lev_candidates()?;
809+
let lev_candidate = self.probe_for_lev_candidate()?;
810810

811811
Err(MethodError::NoMatch(NoMatchData::new(static_candidates,
812-
lev_candidates,
813812
unsatisfied_predicates,
814813
out_of_scope_traits,
814+
lev_candidate,
815815
self.mode)))
816816
}
817817

@@ -1133,9 +1133,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
11331133
})
11341134
}
11351135

1136-
/// Similarly to `probe_for_return_type`, this method attempts to find candidate methods where
1137-
/// the method name may have been misspelt.
1138-
fn probe_for_lev_candidates(&mut self) -> Result<Vec<ty::AssociatedItem>, MethodError<'tcx>> {
1136+
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
1137+
/// candidate method where the method name may have been misspelt. Similarly to other
1138+
/// Levenshtein based suggestions, we provide at most one such suggestion.
1139+
fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssociatedItem>, MethodError<'tcx>> {
11391140
debug!("Probing for method names similar to {:?}",
11401141
self.method_name);
11411142

@@ -1149,7 +1150,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
11491150

11501151
let method_names = pcx.candidate_method_names();
11511152
pcx.allow_similar_names = false;
1152-
Ok(method_names
1153+
let applicable_close_candidates: Vec<ty::AssociatedItem> = method_names
11531154
.iter()
11541155
.filter_map(|&method_name| {
11551156
pcx.reset();
@@ -1162,7 +1163,21 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
11621163
.and_then(|pick| Some(pick.item))
11631164
})
11641165
})
1165-
.collect())
1166+
.collect();
1167+
1168+
if applicable_close_candidates.is_empty() {
1169+
Ok(None)
1170+
} else {
1171+
let best_name = {
1172+
let names = applicable_close_candidates.iter().map(|cand| &cand.name);
1173+
find_best_match_for_name(names,
1174+
&self.method_name.unwrap().as_str(),
1175+
None)
1176+
}.unwrap();
1177+
Ok(applicable_close_candidates
1178+
.into_iter()
1179+
.find(|method| method.name == best_name))
1180+
}
11661181
})
11671182
}
11681183

src/librustc_typeck/check/method/suggest.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
162162

163163
match error {
164164
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
165-
lev_candidates,
166165
unsatisfied_predicates,
167166
out_of_scope_traits,
167+
lev_candidate,
168168
mode,
169169
.. }) => {
170170
let tcx = self.tcx;
@@ -284,10 +284,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
284284
rcvr_expr,
285285
out_of_scope_traits);
286286

287-
if !lev_candidates.is_empty() {
288-
for meth in lev_candidates.iter().take(5) {
289-
err.help(&format!("did you mean `{}`?", meth.name));
290-
}
287+
if let Some(lev_candidate) = lev_candidate {
288+
err.help(&format!("did you mean `{}`?", lev_candidate.name));
291289
}
292290
err.emit();
293291
}

src/test/ui/suggestions/suggest-methods.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ fn main() {
3030
let s = "foo".to_string();
3131
let _ = s.is_emtpy();
3232

33-
// Generates a warning, both for count_ones and count_zeros
33+
// Generates a warning for `count_zeros()`. `count_ones()` is also a close
34+
// match, but the former is closer.
3435
let _ = 63u32.count_eos();
35-
let _ = 63u32.count_o(); // Does not generate a warning
3636

37-
}
37+
// Does not generate a warning
38+
let _ = 63u32.count_o();
3839

40+
}

src/test/ui/suggestions/suggest-methods.stderr

+4-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ error[E0599]: no method named `bat` found for type `Foo` in the current scope
55
| ^^^
66
|
77
= help: did you mean `bar`?
8-
= help: did you mean `baz`?
98

109
error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
1110
--> $DIR/suggest-methods.rs:31:15
@@ -16,18 +15,17 @@ error[E0599]: no method named `is_emtpy` found for type `std::string::String` in
1615
= help: did you mean `is_empty`?
1716

1817
error[E0599]: no method named `count_eos` found for type `u32` in the current scope
19-
--> $DIR/suggest-methods.rs:34:19
18+
--> $DIR/suggest-methods.rs:35:19
2019
|
21-
34 | let _ = 63u32.count_eos();
20+
35 | let _ = 63u32.count_eos();
2221
| ^^^^^^^^^
2322
|
24-
= help: did you mean `count_ones`?
2523
= help: did you mean `count_zeros`?
2624

2725
error[E0599]: no method named `count_o` found for type `u32` in the current scope
28-
--> $DIR/suggest-methods.rs:35:19
26+
--> $DIR/suggest-methods.rs:38:19
2927
|
30-
35 | let _ = 63u32.count_o(); // Does not generate a warning
28+
38 | let _ = 63u32.count_o();
3129
| ^^^^^^^
3230

3331
error: aborting due to 4 previous errors

0 commit comments

Comments
 (0)