Skip to content

Commit

Permalink
suggest similar lint names for unknown lints
Browse files Browse the repository at this point in the history
  • Loading branch information
euclio committed Dec 19, 2018
1 parent adbfec2 commit 90726e1
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 47 deletions.
30 changes: 23 additions & 7 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Symbol>),
/// The lint is either renamed or removed. This is the warning
/// message, and an optional new name (`None` if removed).
Warning(String, Option<String>),
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -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::<Vec<_>>();

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 {
Expand All @@ -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),
}
}
}
Expand Down
27 changes: 11 additions & 16 deletions src/librustc/lint/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/test/ui/lint/lint-unknown-lint-cmdline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() { }
7 changes: 6 additions & 1 deletion src/test/ui/lint/lint-unknown-lint-cmdline.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
12 changes: 9 additions & 3 deletions src/test/ui/lint/lint-unknown-lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
29 changes: 13 additions & 16 deletions src/test/ui/lint/lint-unknown-lint.stderr
Original file line number Diff line number Diff line change
@@ -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

4 changes: 2 additions & 2 deletions src/test/ui/lint/not_found.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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`

0 comments on commit 90726e1

Please sign in to comment.