about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLuqman Aden <laden@csclub.uwaterloo.ca>2013-02-23 00:22:51 -0800
committerLuqman Aden <laden@csclub.uwaterloo.ca>2013-02-26 17:23:30 -0800
commit0a0fcdb018ebee4aa8acb138418ff53c37ce5051 (patch)
treeb32683cae5c65d4cdda792db712a1130f1b397f8
parenta8f07dc9df24af3cf06171bfde1a68b3058e2ec5 (diff)
librustc: offer suggestions for unresolved names.
-rw-r--r--src/librustc/middle/resolve.rs84
1 files changed, 82 insertions, 2 deletions
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 7058f802bc4..3db328ceb91 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -4816,6 +4816,75 @@ pub impl Resolver {
         }
     }
 
+    fn find_best_match_for_name(@mut self, name: &str) -> Option<~str> {
+        let mut maybes: ~[~str] = ~[];
+        let mut values: ~[uint] = ~[];
+
+        let mut j = self.value_ribs.len();
+        while j != 0 {
+            j -= 1;
+            let rib = self.value_ribs.get_elt(j);
+            for rib.bindings.each_entry |e| {
+                vec::push(&mut maybes, copy *self.session.str_of(e.key));
+                vec::push(&mut values, uint::max_value);
+            }
+        }
+
+        // Levenshtein Distance between two strings
+        fn distance(s: &str, t: &str) -> uint {
+
+            let slen = str::len(s);
+            let tlen = str::len(t);
+
+            if slen == 0 { return tlen; }
+            if tlen == 0 { return slen; }
+
+            let mut dcol = vec::from_fn(tlen + 1, |x| x);
+
+            for str::each_chari(s) |i, sc| {
+
+                let mut current = i;
+                dcol[0] = current + 1;
+
+                for str::each_chari(t) |j, tc| {
+
+                    let mut next = dcol[j + 1];
+
+                    if sc == tc {
+                        dcol[j + 1] = current;
+                    } else {
+                        dcol[j + 1] = cmp::min(current, next);
+                        dcol[j + 1] = cmp::min(dcol[j + 1], dcol[j]) + 1;
+                    }
+
+                    current = next;
+                }
+            }
+
+            return dcol[tlen];
+        }
+
+        let mut smallest = 0;
+        for vec::eachi(maybes) |i, &other| {
+
+            values[i] = distance(name, other);
+
+            if values[i] <= values[smallest] {
+                smallest = i;
+            }
+        }
+
+        if vec::len(values) > 0 &&
+            values[smallest] != uint::max_value &&
+            values[smallest] < str::len(name) + 2 {
+
+            Some(vec::swap_remove(&mut maybes, smallest))
+
+        } else {
+            None
+        }
+    }
+
     fn name_exists_in_scope_struct(@mut self, name: &str) -> bool {
         let mut i = self.type_ribs.len();
         while i != 0 {
@@ -4882,9 +4951,20 @@ pub impl Resolver {
                                         wrong_name));
                         }
                         else {
-                            self.session.span_err(expr.span,
-                                                fmt!("unresolved name: %s",
+                            match self.find_best_match_for_name(wrong_name) {
+
+                                Some(m) => {
+                                    self.session.span_err(expr.span,
+                                            fmt!("unresolved name: `%s`. \
+                                                Did you mean: `%s`?",
+                                                wrong_name, m));
+                                }
+                                None => {
+                                    self.session.span_err(expr.span,
+                                            fmt!("unresolved name: `%s`.",
                                                 wrong_name));
+                                }
+                            }
                         }
                     }
                 }