about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThomas Jespersen <laumann.thomas@gmail.com>2017-09-21 13:04:11 +0200
committerThomas Jespersen <laumann.thomas@gmail.com>2017-09-24 20:00:02 +0200
commit4963394f86719f3315239a900e557982a829adae (patch)
treefb9919fad86152e727cafff6bbb83c5362ded4c4
parent09defbcb5b733773c516a31f0206d344e6555f72 (diff)
downloadrust-4963394f86719f3315239a900e557982a829adae.tar.gz
rust-4963394f86719f3315239a900e557982a829adae.zip
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.
-rw-r--r--src/librustc_typeck/check/method/mod.rs6
-rw-r--r--src/librustc_typeck/check/method/probe.rs33
-rw-r--r--src/librustc_typeck/check/method/suggest.rs8
-rw-r--r--src/test/ui/suggestions/suggest-methods.rs8
-rw-r--r--src/test/ui/suggestions/suggest-methods.stderr10
5 files changed, 39 insertions, 26 deletions
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<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,
         }
     }
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<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))
+            }
         })
     }
 
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