diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index f344b636220..4ee0b4cb46f 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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, - pub lev_candidates: Vec, pub unsatisfied_predicates: Vec>, pub out_of_scope_traits: Vec, + pub lev_candidate: Option, pub mode: probe::Mode, } impl<'tcx> NoMatchData<'tcx> { pub fn new(static_candidates: Vec, - lev_candidates: Vec, unsatisfied_predicates: Vec>, out_of_scope_traits: Vec, + lev_candidate: Option, mode: probe::Mode) -> Self { NoMatchData { static_candidates, - lev_candidates, unsatisfied_predicates, out_of_scope_traits, + lev_candidate, mode, } } diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 2041ff58861..a3b196f99d6 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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, 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, 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 = 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)) + } }) } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index d65ea5f7fb5..90c5297b399 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -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(); } diff --git a/src/test/ui/suggestions/suggest-methods.rs b/src/test/ui/suggestions/suggest-methods.rs index 36b9976ae56..b02881dc7ee 100644 --- a/src/test/ui/suggestions/suggest-methods.rs +++ b/src/test/ui/suggestions/suggest-methods.rs @@ -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(); } - diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index d1a5ebcdef4..41beb73b1bc 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -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