Skip to content

Commit

Permalink
Change Levensthein-based method to a single suggestion
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Thomas Jespersen committed Sep 24, 2017
1 parent 09defbc commit 4963394
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 26 deletions.
6 changes: 3 additions & 3 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down
33 changes: 24 additions & 9 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)))
}
}
Expand Down Expand Up @@ -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)))
}

Expand Down Expand Up @@ -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);

Expand All @@ -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();
Expand All @@ -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))
}
})
}

Expand Down
8 changes: 3 additions & 5 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down
8 changes: 5 additions & 3 deletions src/test/ui/suggestions/suggest-methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

}
10 changes: 4 additions & 6 deletions src/test/ui/suggestions/suggest-methods.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 4963394

Please sign in to comment.