diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 87f130daa42b4..56e8c75b859a1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2191,11 +2191,9 @@ impl<'a> Resolver<'a> { } // Try Levenshtein if nothing else worked. - if path.len() == 1 { - if let Some(candidate) = this.lookup_typo_candidate(name, ns, is_expected) { - err.span_label(span, &format!("did you mean `{}`?", candidate)); - return err; - } + if let Some(candidate) = this.lookup_typo_candidate(path, ns, is_expected) { + err.span_label(span, &format!("did you mean `{}`?", candidate)); + return err; } // Fallback label. @@ -2649,21 +2647,72 @@ impl<'a> Resolver<'a> { } fn lookup_typo_candidate(&mut self, - name: Name, + path: &[Ident], ns: Namespace, filter_fn: FilterFn) - -> Option + -> Option where FilterFn: Fn(Def) -> bool { - // FIXME: bindings in ribs provide quite modest set of candidates, - // extend it with other names in scope. - let names = self.ribs[ns].iter().rev().flat_map(|rib| { - rib.bindings.iter().filter_map(|(ident, def)| { - if filter_fn(*def) { Some(&ident.name) } else { None } - }) - }); - match find_best_match_for_name(names, &name.as_str(), None) { - Some(found) if found != name => Some(found), + let add_module_candidates = |module: Module, names: &mut Vec| { + for (&(ident, _), resolution) in module.resolutions.borrow().iter() { + if let Some(binding) = resolution.borrow().binding { + if filter_fn(binding.def()) { + names.push(ident.name); + } + } + } + }; + + let mut names = Vec::new(); + let prefix_str = if path.len() == 1 { + // Search in lexical scope. + // Walk backwards up the ribs in scope and collect candidates. + for rib in self.ribs[ns].iter().rev() { + // Locals and type parameters + for (ident, def) in &rib.bindings { + if filter_fn(*def) { + names.push(ident.name); + } + } + // Items in scope + if let ModuleRibKind(module) = rib.kind { + // Items from this module + add_module_candidates(module, &mut names); + + if let ModuleKind::Block(..) = module.kind { + // We can see through blocks + } else { + // Items from the prelude + if let Some(prelude) = self.prelude { + if !module.no_implicit_prelude { + add_module_candidates(prelude, &mut names); + } + } + break; + } + } + } + // Add primitive types to the mix + if filter_fn(Def::PrimTy(TyBool)) { + for (name, _) in &self.primitive_type_table.primitive_types { + names.push(*name); + } + } + String::new() + } else { + // Search in module. + let mod_path = &path[..path.len() - 1]; + if let PathResult::Module(module) = self.resolve_path(mod_path, Some(TypeNS), None) { + add_module_candidates(module, &mut names); + } + names_to_string(mod_path) + "::" + }; + + let name = path[path.len() - 1].name; + // Make sure error reporting is deterministic. + names.sort_by_key(|name| name.as_str()); + match find_best_match_for_name(names.iter(), &name.as_str(), None) { + Some(found) if found != name => Some(format!("{}{}", prefix_str, found)), _ => None, } } diff --git a/src/test/ui/resolve/levenshtein.rs b/src/test/ui/resolve/levenshtein.rs new file mode 100644 index 0000000000000..53b6372d8eb40 --- /dev/null +++ b/src/test/ui/resolve/levenshtein.rs @@ -0,0 +1,33 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const MAX_ITEM: usize = 10; + +fn foo_bar() {} + +fn foo(c: esize) {} // Misspelled primitive type name. + +enum Bar { } + +type A = Baz; // Misspelled type name. +type B = Opiton; // Misspelled type name from the prelude. + +mod m { + type A = Baz; // No suggestion here, Bar is not visible + + pub struct First; + pub struct Second; +} + +fn main() { + let v = [0u32; MAXITEM]; // Misspelled constant name. + foobar(); // Misspelled function name. + let b: m::first = m::second; // Misspelled item in module. +} diff --git a/src/test/ui/resolve/levenshtein.stderr b/src/test/ui/resolve/levenshtein.stderr new file mode 100644 index 0000000000000..d01ffcb2839b4 --- /dev/null +++ b/src/test/ui/resolve/levenshtein.stderr @@ -0,0 +1,56 @@ +error[E0412]: cannot find type `esize` in this scope + --> $DIR/levenshtein.rs:15:11 + | +15 | fn foo(c: esize) {} // Misspelled primitive type name. + | ^^^^^ did you mean `isize`? + +error[E0412]: cannot find type `Baz` in this scope + --> $DIR/levenshtein.rs:19:10 + | +19 | type A = Baz; // Misspelled type name. + | ^^^ did you mean `Bar`? + +error[E0412]: cannot find type `Opiton` in this scope + --> $DIR/levenshtein.rs:20:10 + | +20 | type B = Opiton; // Misspelled type name from the prelude. + | ^^^^^^^^^^ did you mean `Option`? + +error[E0412]: cannot find type `Baz` in this scope + --> $DIR/levenshtein.rs:23:14 + | +23 | type A = Baz; // No suggestion here, Bar is not visible + | ^^^ not found in this scope + +error[E0425]: cannot find value `MAXITEM` in this scope + --> $DIR/levenshtein.rs:30:20 + | +30 | let v = [0u32; MAXITEM]; // Misspelled constant name. + | ^^^^^^^ did you mean `MAX_ITEM`? + +error[E0425]: cannot find function `foobar` in this scope + --> $DIR/levenshtein.rs:31:5 + | +31 | foobar(); // Misspelled function name. + | ^^^^^^ did you mean `foo_bar`? + +error[E0412]: cannot find type `first` in module `m` + --> $DIR/levenshtein.rs:32:12 + | +32 | let b: m::first = m::second; // Misspelled item in module. + | ^^^^^^^^ did you mean `m::First`? + +error[E0425]: cannot find value `second` in module `m` + --> $DIR/levenshtein.rs:32:23 + | +32 | let b: m::first = m::second; // Misspelled item in module. + | ^^^^^^^^^ did you mean `m::Second`? + +error[E0080]: constant evaluation error + --> $DIR/levenshtein.rs:30:20 + | +30 | let v = [0u32; MAXITEM]; // Misspelled constant name. + | ^^^^^^^ unresolved path in constant expression + +error: aborting due to previous error + diff --git a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr index 8ace738ad6d81..57c0ecc813505 100644 --- a/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr +++ b/src/test/ui/resolve/suggest-path-instead-of-mod-dot-item.stderr @@ -32,7 +32,7 @@ error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:55:12 | 55 | v.push(a::b); - | ^^^^ not a value + | ^^^^ did you mean `a::I`? error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:61:5 @@ -44,13 +44,13 @@ error[E0423]: expected value, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:67:5 | 67 | a::b - | ^^^^ not a value + | ^^^^ did you mean `a::I`? error[E0423]: expected function, found module `a::b` --> $DIR/suggest-path-instead-of-mod-dot-item.rs:73:5 | 73 | a::b() - | ^^^^ not a function + | ^^^^ did you mean `a::I`? error: main function not found