diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 449f8e0a2db67..131dedb988832 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -42,11 +42,12 @@ use util::nodemap::FxHashMap; use std::default::Default as StdDefault; use syntax::ast; use syntax::edition; -use syntax_pos::{MultiSpan, Span, symbol::LocalInternedString}; +use syntax_pos::{MultiSpan, Span, symbol::{LocalInternedString, Symbol}}; use errors::DiagnosticBuilder; use hir; use hir::def_id::LOCAL_CRATE; use hir::intravisit as hir_visit; +use syntax::util::lev_distance::find_best_match_for_name; use syntax::visit as ast_visit; /// Information about the registered lints. @@ -139,8 +140,8 @@ struct LintGroup { pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), - /// Lint doesn't exist - NoLint, + /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. + NoLint(Option), /// The lint is either renamed or removed. This is the warning /// message, and an optional new name (`None` if removed). Warning(String, Option), @@ -359,8 +360,14 @@ impl LintStore { CheckLintNameResult::Warning(ref msg, _) => { Some(sess.struct_warn(msg)) }, - CheckLintNameResult::NoLint => { - Some(struct_err!(sess, E0602, "unknown lint: `{}`", lint_name)) + CheckLintNameResult::NoLint(suggestion) => { + let mut err = struct_err!(sess, E0602, "unknown lint: `{}`", lint_name); + + if let Some(suggestion) = suggestion { + err.help(&format!("did you mean: `{}`", suggestion)); + } + + Some(err) } CheckLintNameResult::Tool(result) => match result { Err((Some(_), new_name)) => Some(sess.struct_warn(&format!( @@ -464,7 +471,16 @@ impl LintStore { match self.by_name.get(&complete_name) { None => match self.lint_groups.get(&*complete_name) { // Now we are sure, that this lint exists nowhere - None => CheckLintNameResult::NoLint, + None => { + let symbols = self.by_name.keys() + .map(|name| Symbol::intern(&name)) + .collect::>(); + + let suggestion = + find_best_match_for_name(symbols.iter(), &lint_name.to_lowercase(), None); + + CheckLintNameResult::NoLint(suggestion) + } Some(LintGroup { lint_ids, depr, .. }) => { // Reaching this would be weird, but let's cover this case anyway if let Some(LintAlias { name, silent }) = depr { @@ -484,7 +500,7 @@ impl LintStore { Some(&Id(ref id)) => { CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name))) } - _ => CheckLintNameResult::NoLint, + _ => CheckLintNameResult::NoLint(None), } } } diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 06e3e0bab4f10..9ab8e89df72e6 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -385,7 +385,7 @@ impl<'a> LintLevelsBuilder<'a> { } err.emit(); } - CheckLintNameResult::NoLint => { + CheckLintNameResult::NoLint(suggestion) => { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.sets.get_lint_level(lint, self.cur, @@ -398,22 +398,17 @@ impl<'a> LintLevelsBuilder<'a> { src, Some(li.span.into()), &msg); - if name.as_str().chars().any(|c| c.is_uppercase()) { - let name_lower = name.as_str().to_lowercase().to_string(); - if let CheckLintNameResult::NoLint = - store.check_lint_name(&name_lower, tool_name) { - db.emit(); - } else { - db.span_suggestion_with_applicability( - li.span, - "lowercase the lint name", - name_lower, - Applicability::MachineApplicable - ).emit(); - } - } else { - db.emit(); + + if let Some(suggestion) = suggestion { + db.span_suggestion_with_applicability( + li.span, + "did you mean", + suggestion.to_string(), + Applicability::MachineApplicable, + ); } + + db.emit(); } } } diff --git a/src/test/ui/lint/lint-unknown-lint-cmdline.rs b/src/test/ui/lint/lint-unknown-lint-cmdline.rs index 0c41959f8a710..33fb46dce6e38 100644 --- a/src/test/ui/lint/lint-unknown-lint-cmdline.rs +++ b/src/test/ui/lint/lint-unknown-lint-cmdline.rs @@ -8,9 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-D bogus +// compile-flags:-D bogus -D dead_cod -// error-pattern:unknown lint +// error-pattern:unknown lint: `bogus` // error-pattern:requested on the command line with `-D bogus` +// error-pattern:unknown lint: `dead_cod` +// error-pattern:requested on the command line with `-D dead_cod` +// error-pattern:did you mean: `dead_code` fn main() { } diff --git a/src/test/ui/lint/lint-unknown-lint-cmdline.stderr b/src/test/ui/lint/lint-unknown-lint-cmdline.stderr index 8636004102618..58fdae3333ca1 100644 --- a/src/test/ui/lint/lint-unknown-lint-cmdline.stderr +++ b/src/test/ui/lint/lint-unknown-lint-cmdline.stderr @@ -2,6 +2,11 @@ error[E0602]: unknown lint: `bogus` | = note: requested on the command line with `-D bogus` -error: aborting due to previous error +error[E0602]: unknown lint: `dead_cod` + | + = help: did you mean: `dead_code` + = note: requested on the command line with `-D dead_cod` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0602`. diff --git a/src/test/ui/lint/lint-unknown-lint.rs b/src/test/ui/lint/lint-unknown-lint.rs index 2de8d849d1915..5d3fd0f19721f 100644 --- a/src/test/ui/lint/lint-unknown-lint.rs +++ b/src/test/ui/lint/lint-unknown-lint.rs @@ -8,6 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(not_a_real_lint)] //~ WARN unknown lint -#![deny(unused)] -fn main() { let unused = (); } //~ ERROR unused variable +#![deny(unknown_lints)] + +#![allow(not_a_real_lint)] //~ ERROR unknown lint + +#![deny(dead_cod)] //~ ERROR unknown lint + //~| HELP did you mean + //~| SUGGESTION dead_code + +fn main() {} diff --git a/src/test/ui/lint/lint-unknown-lint.stderr b/src/test/ui/lint/lint-unknown-lint.stderr index b398a2f3690fb..6fc2da39a7c60 100644 --- a/src/test/ui/lint/lint-unknown-lint.stderr +++ b/src/test/ui/lint/lint-unknown-lint.stderr @@ -1,23 +1,20 @@ -warning: unknown lint: `not_a_real_lint` - --> $DIR/lint-unknown-lint.rs:11:10 +error: unknown lint: `not_a_real_lint` + --> $DIR/lint-unknown-lint.rs:13:10 | -LL | #![allow(not_a_real_lint)] //~ WARN unknown lint +LL | #![allow(not_a_real_lint)] //~ ERROR unknown lint | ^^^^^^^^^^^^^^^ | - = note: #[warn(unknown_lints)] on by default - -error: unused variable: `unused` - --> $DIR/lint-unknown-lint.rs:13:17 - | -LL | fn main() { let unused = (); } //~ ERROR unused variable - | ^^^^^^ help: consider using `_unused` instead - | note: lint level defined here - --> $DIR/lint-unknown-lint.rs:12:9 + --> $DIR/lint-unknown-lint.rs:11:9 + | +LL | #![deny(unknown_lints)] + | ^^^^^^^^^^^^^ + +error: unknown lint: `dead_cod` + --> $DIR/lint-unknown-lint.rs:15:9 | -LL | #![deny(unused)] - | ^^^^^^ - = note: #[deny(unused_variables)] implied by #[deny(unused)] +LL | #![deny(dead_cod)] //~ ERROR unknown lint + | ^^^^^^^^ help: did you mean: `dead_code` -error: aborting due to previous error +error: aborting due to 2 previous errors diff --git a/src/test/ui/lint/not_found.stderr b/src/test/ui/lint/not_found.stderr index 603b5410444cd..dedbc829913a5 100644 --- a/src/test/ui/lint/not_found.stderr +++ b/src/test/ui/lint/not_found.stderr @@ -10,11 +10,11 @@ warning: unknown lint: `DEAD_CODE` --> $DIR/not_found.rs:18:8 | LL | #[warn(DEAD_CODE)] - | ^^^^^^^^^ help: lowercase the lint name: `dead_code` + | ^^^^^^^^^ help: did you mean: `dead_code` warning: unknown lint: `Warnings` --> $DIR/not_found.rs:20:8 | LL | #[deny(Warnings)] - | ^^^^^^^^ help: lowercase the lint name: `warnings` + | ^^^^^^^^ help: did you mean: `warnings`