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.
This commit is contained in:
Thomas Jespersen 2017-09-21 13:04:11 +02:00
parent 09defbcb5b
commit 4963394f86
5 changed files with 39 additions and 26 deletions

View File

@ -68,24 +68,24 @@ pub enum MethodError<'tcx> {
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
pub static_candidates: Vec<CandidateSource>,
pub lev_candidates: Vec<ty::AssociatedItem>,
pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
pub out_of_scope_traits: Vec<DefId>,
pub lev_candidate: Option<ty::AssociatedItem>,
pub mode: probe::Mode,
}
impl<'tcx> NoMatchData<'tcx> {
pub fn new(static_candidates: Vec<CandidateSource>,
lev_candidates: Vec<ty::AssociatedItem>,
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
out_of_scope_traits: Vec<DefId>,
lev_candidate: Option<ty::AssociatedItem>,
mode: probe::Mode)
-> Self {
NoMatchData {
static_candidates,
lev_candidates,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
mode,
}
}

View File

@ -23,7 +23,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::util::nodemap::FxHashSet;
use rustc::infer::{self, InferOk};
use syntax::ast;
use syntax::util::lev_distance::lev_distance;
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
use syntax_pos::Span;
use rustc::hir;
use std::mem;
@ -248,7 +248,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
None,
mode)))
}
}
@ -806,12 +806,12 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
if let Some(def) = private_candidate {
return Err(MethodError::PrivateMatch(def, out_of_scope_traits));
}
let lev_candidates = self.probe_for_lev_candidates()?;
let lev_candidate = self.probe_for_lev_candidate()?;
Err(MethodError::NoMatch(NoMatchData::new(static_candidates,
lev_candidates,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
self.mode)))
}
@ -1133,9 +1133,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
})
}
/// Similarly to `probe_for_return_type`, this method attempts to find candidate methods where
/// the method name may have been misspelt.
fn probe_for_lev_candidates(&mut self) -> Result<Vec<ty::AssociatedItem>, MethodError<'tcx>> {
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
/// candidate method where the method name may have been misspelt. Similarly to other
/// Levenshtein based suggestions, we provide at most one such suggestion.
fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssociatedItem>, MethodError<'tcx>> {
debug!("Probing for method names similar to {:?}",
self.method_name);
@ -1149,7 +1150,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
let method_names = pcx.candidate_method_names();
pcx.allow_similar_names = false;
Ok(method_names
let applicable_close_candidates: Vec<ty::AssociatedItem> = method_names
.iter()
.filter_map(|&method_name| {
pcx.reset();
@ -1162,7 +1163,21 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
.and_then(|pick| Some(pick.item))
})
})
.collect())
.collect();
if applicable_close_candidates.is_empty() {
Ok(None)
} else {
let best_name = {
let names = applicable_close_candidates.iter().map(|cand| &cand.name);
find_best_match_for_name(names,
&self.method_name.unwrap().as_str(),
None)
}.unwrap();
Ok(applicable_close_candidates
.into_iter()
.find(|method| method.name == best_name))
}
})
}

View File

@ -162,9 +162,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
match error {
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
lev_candidates,
unsatisfied_predicates,
out_of_scope_traits,
lev_candidate,
mode,
.. }) => {
let tcx = self.tcx;
@ -284,10 +284,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
rcvr_expr,
out_of_scope_traits);
if !lev_candidates.is_empty() {
for meth in lev_candidates.iter().take(5) {
err.help(&format!("did you mean `{}`?", meth.name));
}
if let Some(lev_candidate) = lev_candidate {
err.help(&format!("did you mean `{}`?", lev_candidate.name));
}
err.emit();
}

View File

@ -30,9 +30,11 @@ fn main() {
let s = "foo".to_string();
let _ = s.is_emtpy();
// Generates a warning, both for count_ones and count_zeros
// Generates a warning for `count_zeros()`. `count_ones()` is also a close
// match, but the former is closer.
let _ = 63u32.count_eos();
let _ = 63u32.count_o(); // Does not generate a warning
// Does not generate a warning
let _ = 63u32.count_o();
}

View File

@ -5,7 +5,6 @@ error[E0599]: no method named `bat` found for type `Foo` in the current scope
| ^^^
|
= help: did you mean `bar`?
= help: did you mean `baz`?
error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope
--> $DIR/suggest-methods.rs:31:15
@ -16,18 +15,17 @@ error[E0599]: no method named `is_emtpy` found for type `std::string::String` in
= help: did you mean `is_empty`?
error[E0599]: no method named `count_eos` found for type `u32` in the current scope
--> $DIR/suggest-methods.rs:34:19
--> $DIR/suggest-methods.rs:35:19
|
34 | let _ = 63u32.count_eos();
35 | let _ = 63u32.count_eos();
| ^^^^^^^^^
|
= help: did you mean `count_ones`?
= help: did you mean `count_zeros`?
error[E0599]: no method named `count_o` found for type `u32` in the current scope
--> $DIR/suggest-methods.rs:35:19
--> $DIR/suggest-methods.rs:38:19
|
35 | let _ = 63u32.count_o(); // Does not generate a warning
38 | let _ = 63u32.count_o();
| ^^^^^^^
error: aborting due to 4 previous errors