From a5e5c6775629b912fd058e2a595f70e5457b3d46 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Thu, 26 Nov 2015 00:21:38 +0100 Subject: [PATCH 1/4] Add suggestion of similar macro names to `macro undefined` error message --- src/librustc/lib.rs | 1 - src/librustc_resolve/lib.rs | 2 +- src/librustc_typeck/check/mod.rs | 2 +- src/libsyntax/ext/base.rs | 17 +++++++++++++++++ src/libsyntax/ext/expand.rs | 1 + src/libsyntax/lib.rs | 1 + .../util/lev_distance.rs | 0 7 files changed, 21 insertions(+), 3 deletions(-) rename src/{librustc => libsyntax}/util/lev_distance.rs (100%) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 393329b4214..454b70da8f2 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -152,7 +152,6 @@ pub mod util { pub mod common; pub mod ppaux; pub mod nodemap; - pub mod lev_distance; pub mod num; pub mod fs; } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 51d7ceb946d..5cdce22d826 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -63,7 +63,6 @@ use rustc::middle::privacy::*; use rustc::middle::subst::{ParamSpace, FnSpace, TypeSpace}; use rustc::middle::ty::{Freevar, FreevarMap, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, DefIdSet, FnvHashMap}; -use rustc::util::lev_distance::lev_distance; use syntax::ast; use syntax::ast::{CRATE_NODE_ID, Ident, Name, NodeId, CrateNum, TyIs, TyI8, TyI16, TyI32, TyI64}; @@ -73,6 +72,7 @@ use syntax::ext::mtwt; use syntax::parse::token::{self, special_names, special_idents}; use syntax::ptr::P; use syntax::codemap::{self, Span, Pos}; +use syntax::util::lev_distance::lev_distance; use rustc_front::intravisit::{self, FnKind, Visitor}; use rustc_front::hir; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index bac85e4b700..df91db36089 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -110,7 +110,6 @@ use TypeAndSubsts; use lint; use util::common::{block_query, ErrorReported, indenter, loop_query}; use util::nodemap::{DefIdMap, FnvHashMap, NodeMap}; -use util::lev_distance::lev_distance; use std::cell::{Cell, Ref, RefCell}; use std::collections::{HashSet}; @@ -123,6 +122,7 @@ use syntax::codemap::{self, Span, Spanned}; use syntax::owned_slice::OwnedSlice; use syntax::parse::token::{self, InternedString}; use syntax::ptr::P; +use syntax::util::lev_distance::lev_distance; use rustc_front::intravisit::{self, Visitor}; use rustc_front::hir; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 55f0fa5675a..7db9dd9fbee 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -24,6 +24,7 @@ use parse::token; use parse::token::{InternedString, intern, str_to_ident}; use ptr::P; use util::small_vector::SmallVector; +use util::lev_distance::lev_distance; use ext::mtwt; use fold::Folder; @@ -776,6 +777,22 @@ impl<'a> ExtCtxt<'a> { pub fn name_of(&self, st: &str) -> ast::Name { token::intern(st) } + + pub fn suggest_macro_name(&mut self, name: &str, span: Span) { + use std::cmp::max; + + let mut min: Option<(Name, usize)> = None; + let max_dist = max(name.len() / 3, 1); + for macro_name in self.syntax_env.names.iter() { + let dist = lev_distance(name, ¯o_name.as_str()); + if dist <= max_dist && (min.is_none() || min.unwrap().1 > dist) { + min = Some((*macro_name, dist)); + } + } + if let Some((suggestion, _)) = min { + self.span_help(span, &format!("did you mean `{}`?", suggestion)); + } + } } /// Extract a string literal from the macro expanded version of `expr`, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9b1a7a50201..01e72cf2a8d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -191,6 +191,7 @@ fn expand_mac_invoc(mac: ast::Mac, pth.span, &format!("macro undefined: '{}!'", &extname)); + fld.cx.suggest_macro_name(&extname.as_str(), pth.span); // let compilation continue None diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 59cc380b0ec..d2455995921 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -65,6 +65,7 @@ macro_rules! panictry { pub mod util { pub mod interner; + pub mod lev_distance; pub mod node_count; pub mod parser; #[cfg(test)] diff --git a/src/librustc/util/lev_distance.rs b/src/libsyntax/util/lev_distance.rs similarity index 100% rename from src/librustc/util/lev_distance.rs rename to src/libsyntax/util/lev_distance.rs From ac0220cf4c1c31ee74fccee0cd546b8ec4d29edf Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Thu, 26 Nov 2015 00:36:30 +0100 Subject: [PATCH 2/4] Add test for macro suggestions --- src/test/compile-fail/macro-name-typo.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/test/compile-fail/macro-name-typo.rs diff --git a/src/test/compile-fail/macro-name-typo.rs b/src/test/compile-fail/macro-name-typo.rs new file mode 100644 index 00000000000..0274afe2995 --- /dev/null +++ b/src/test/compile-fail/macro-name-typo.rs @@ -0,0 +1,14 @@ +// Copyright 2015 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. + +fn main() { + printlx!("oh noes!"); //~ ERROR macro undefined + //~^ HELP did you mean `println`? +} From 9ba657cad5f0fdeebfc3637f2cbc31af24e944f7 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Thu, 26 Nov 2015 11:59:41 +0100 Subject: [PATCH 3/4] Add '!' to macro name suggestion, use fileline_help instead of span_help --- src/libsyntax/ext/base.rs | 2 +- src/test/compile-fail/macro-name-typo.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 7db9dd9fbee..09c98c3e547 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -790,7 +790,7 @@ impl<'a> ExtCtxt<'a> { } } if let Some((suggestion, _)) = min { - self.span_help(span, &format!("did you mean `{}`?", suggestion)); + self.fileline_help(span, &format!("did you mean `{}!`?", suggestion)); } } } diff --git a/src/test/compile-fail/macro-name-typo.rs b/src/test/compile-fail/macro-name-typo.rs index 0274afe2995..e1f7d9b65d1 100644 --- a/src/test/compile-fail/macro-name-typo.rs +++ b/src/test/compile-fail/macro-name-typo.rs @@ -10,5 +10,5 @@ fn main() { printlx!("oh noes!"); //~ ERROR macro undefined - //~^ HELP did you mean `println`? + //~^ HELP did you mean `println!`? } From 4bb7cf11dcb7b4d106456b9c34c2e30638615f47 Mon Sep 17 00:00:00 2001 From: Florian Hartwig Date: Fri, 27 Nov 2015 17:52:29 +0100 Subject: [PATCH 4/4] Introduce max_suggestion_distance function to avoid duplicating the heuristic --- src/librustc_resolve/lib.rs | 8 ++------ src/libsyntax/ext/base.rs | 6 ++---- src/libsyntax/util/lev_distance.rs | 8 ++++++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5cdce22d826..b5a8d5a13fe 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -72,7 +72,7 @@ use syntax::ext::mtwt; use syntax::parse::token::{self, special_names, special_idents}; use syntax::ptr::P; use syntax::codemap::{self, Span, Pos}; -use syntax::util::lev_distance::lev_distance; +use syntax::util::lev_distance::{lev_distance, max_suggestion_distance}; use rustc_front::intravisit::{self, FnKind, Visitor}; use rustc_front::hir; @@ -3384,11 +3384,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - // As a loose rule to avoid obviously incorrect suggestions, clamp the - // maximum edit distance we will accept for a suggestion to one third of - // the typo'd name's length. - let max_distance = std::cmp::max(name.len(), 3) / 3; - + let max_distance = max_suggestion_distance(name); if !values.is_empty() && values[smallest] <= max_distance && name != &maybes[smallest][..] { SuggestionType::Function(maybes[smallest].to_string()) diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 09c98c3e547..cf6881ab650 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -24,7 +24,7 @@ use parse::token; use parse::token::{InternedString, intern, str_to_ident}; use ptr::P; use util::small_vector::SmallVector; -use util::lev_distance::lev_distance; +use util::lev_distance::{lev_distance, max_suggestion_distance}; use ext::mtwt; use fold::Folder; @@ -779,10 +779,8 @@ impl<'a> ExtCtxt<'a> { } pub fn suggest_macro_name(&mut self, name: &str, span: Span) { - use std::cmp::max; - let mut min: Option<(Name, usize)> = None; - let max_dist = max(name.len() / 3, 1); + let max_dist = max_suggestion_distance(name); for macro_name in self.syntax_env.names.iter() { let dist = lev_distance(name, ¯o_name.as_str()); if dist <= max_dist && (min.is_none() || min.unwrap().1 > dist) { diff --git a/src/libsyntax/util/lev_distance.rs b/src/libsyntax/util/lev_distance.rs index 28f8510ce3f..9bf96311122 100644 --- a/src/libsyntax/util/lev_distance.rs +++ b/src/libsyntax/util/lev_distance.rs @@ -41,6 +41,14 @@ pub fn lev_distance(me: &str, t: &str) -> usize { dcol[t_last + 1] } +pub fn max_suggestion_distance(name: &str) -> usize { + use std::cmp::max; + // As a loose rule to avoid obviously incorrect suggestions, clamp the + // maximum edit distance we will accept for a suggestion to one third of + // the typo'd name's length. + max(name.len(), 3) / 3 +} + #[test] fn test_lev_distance() { use std::char::{ from_u32, MAX };