Skip to content

Commit 8aceb60

Browse files
committed
check rust lints when an unknown lint is detected
1 parent e927184 commit 8aceb60

File tree

10 files changed

+83
-26
lines changed

10 files changed

+83
-26
lines changed

compiler/rustc_lint/messages.ftl

+8-2
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,14 @@ lint_unknown_gated_lint =
532532
533533
lint_unknown_lint =
534534
unknown lint: `{$name}`
535-
.suggestion = did you mean
536-
.help = did you mean: `{$replace}`
535+
.suggestion = {$from_rustc ->
536+
[true] a lint with the similar name exists in `rustc` lints
537+
*[false] did you mean
538+
}
539+
.help = {$from_rustc ->
540+
[true] a lint with the similar name exists in `rustc`, did you mean: `{$replace}`
541+
*[false] did you mean: `{$replace}`
542+
}
537543
538544
lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
539545
.help = add `#![register_tool({$tool_name})]` to the crate root

compiler/rustc_lint/src/context.rs

+32-12
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ struct LintGroup {
117117
pub enum CheckLintNameResult<'a> {
118118
Ok(&'a [LintId]),
119119
/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
120-
NoLint(Option<Symbol>),
120+
NoLint(Option<(Symbol, bool)>),
121121
/// The lint refers to a tool that has not been registered.
122122
NoTool,
123123
/// The lint has been renamed to a new name.
@@ -377,7 +377,7 @@ impl LintStore {
377377
debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
378378
let tool_prefix = format!("{tool_name}::");
379379
return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
380-
self.no_lint_suggestion(&complete_name)
380+
self.no_lint_suggestion(&complete_name, tool_name.as_str())
381381
} else {
382382
// 2. The tool isn't currently running, so no lints will be registered.
383383
// To avoid giving a false positive, ignore all unknown lints.
@@ -419,29 +419,49 @@ impl LintStore {
419419
}
420420
}
421421

422-
fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> {
422+
fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> {
423423
let name_lower = lint_name.to_lowercase();
424424

425425
if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() {
426426
// First check if the lint name is (partly) in upper case instead of lower case...
427-
return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower)));
427+
return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false)));
428428
}
429+
429430
// ...if not, search for lints with a similar name
430-
// Note: find_best_match_for_name depends on the sort order of its input vector.
431-
// To ensure deterministic output, sort elements of the lint_groups hash map.
432431
// Also, never suggest deprecated lint groups.
433-
// We will soon sort, so the initial order does not matter.
434432
#[allow(rustc::potential_query_instability)]
435-
let mut groups: Vec<_> = self
433+
let groups: Vec<_> = self
436434
.lint_groups
437435
.iter()
438436
.filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
439437
.collect();
440-
groups.sort();
441438
let groups = groups.iter().map(|k| Symbol::intern(k));
442439
let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
443-
let names: Vec<Symbol> = groups.chain(lints).collect();
444-
let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None);
440+
let mut names: Vec<Symbol> = groups.chain(lints).collect();
441+
// Here, a little bit of extra hack
442+
// we use the lint names from rustc and to mock the tool name,
443+
// if it's selected we then strip to the right suggestion with `true` for from_rustc
444+
// so we can handle these cases:
445+
// 1. suggest `missing_docs` from rustc for `clippy::missing_docs`
446+
// 2. suggest `dead_code` from rustc for `clippy::dead_cod`
447+
let rustc_names = self
448+
.by_name
449+
.keys()
450+
.map(|k| Symbol::intern(&format!("{tool_name}::{k}")))
451+
.collect::<Vec<_>>();
452+
names.extend(rustc_names.clone());
453+
// Note: find_best_match_for_name depends on the sort order of its input vector.
454+
// Sort elements here to ensure deterministic output
455+
names.sort();
456+
let suggestion =
457+
find_best_match_for_name(&names, Symbol::intern(&name_lower), None).map(|s| {
458+
if rustc_names.contains(&s) {
459+
let stripped = s.as_str().strip_prefix(&format!("{tool_name}::")).unwrap();
460+
(Symbol::intern(stripped), true)
461+
} else {
462+
(s, false)
463+
}
464+
});
445465
CheckLintNameResult::NoLint(suggestion)
446466
}
447467

@@ -454,7 +474,7 @@ impl LintStore {
454474
match self.by_name.get(&complete_name) {
455475
None => match self.lint_groups.get(&*complete_name) {
456476
// Now we are sure, that this lint exists nowhere
457-
None => self.no_lint_suggestion(lint_name),
477+
None => self.no_lint_suggestion(lint_name, tool_name),
458478
Some(LintGroup { lint_ids, depr, .. }) => {
459479
// Reaching this would be weird, but let's cover this case anyway
460480
if let Some(LintAlias { name, silent }) = depr {

compiler/rustc_lint/src/levels.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
582582
}
583583
CheckLintNameResult::NoLint(suggestion) => {
584584
let name = lint_name.clone();
585-
let suggestion =
586-
suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace });
585+
let suggestion = suggestion.map(|(replace, from_rustc)| {
586+
UnknownLintSuggestion::WithoutSpan { replace, from_rustc }
587+
});
587588
let requested_level = RequestedLevel { level, lint_name };
588589
let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
589590
self.emit_lint(UNKNOWN_LINTS, lint);
@@ -990,8 +991,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
990991
} else {
991992
name.to_string()
992993
};
993-
let suggestion = suggestion.map(|replace| {
994-
UnknownLintSuggestion::WithSpan { suggestion: sp, replace }
994+
let suggestion = suggestion.map(|(replace, from_rustc)| {
995+
UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc }
995996
});
996997
let lint = UnknownLint { name, suggestion };
997998
self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint);

compiler/rustc_lint/src/lints.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1050,9 +1050,10 @@ pub enum UnknownLintSuggestion {
10501050
#[primary_span]
10511051
suggestion: Span,
10521052
replace: Symbol,
1053+
from_rustc: bool,
10531054
},
10541055
#[help(lint_help)]
1055-
WithoutSpan { replace: Symbol },
1056+
WithoutSpan { replace: Symbol, from_rustc: bool },
10561057
}
10571058

10581059
#[derive(LintDiagnostic)]

src/tools/clippy/tests/ui/unknown_clippy_lints.fixed

+4-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#[warn(clippy::if_not_else)]
88
#[warn(clippy::unnecessary_cast)]
99
#[warn(clippy::useless_transmute)]
10-
// Shouldn't suggest rustc lint name(`dead_code`)
11-
#[warn(clippy::eq_op)]
10+
// Should suggest rustc lint name(`dead_code`)
11+
#[warn(dead_code)]
1212
// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
1313
#[warn(clippy::unused_self)]
1414
// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
1515
#[warn(clippy::redundant_static_lifetimes)]
16+
// issue #118183, should report `missing_docs` from rustc lint
17+
#[warn(missing_docs)]
1618
fn main() {}

src/tools/clippy/tests/ui/unknown_clippy_lints.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#[warn(clippy::if_not_els)]
88
#[warn(clippy::UNNecsaRy_cAst)]
99
#[warn(clippy::useles_transute)]
10-
// Shouldn't suggest rustc lint name(`dead_code`)
10+
// Should suggest rustc lint name(`dead_code`)
1111
#[warn(clippy::dead_cod)]
1212
// Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`)
1313
#[warn(clippy::unused_colle)]
1414
// Shouldn't suggest renamed clippy lint name(`const_static_lifetime`)
1515
#[warn(clippy::const_static_lifetim)]
16+
// issue #118183, should report `missing_docs` from rustc lint
17+
#[warn(clippy::missing_docs)]
1618
fn main() {}

src/tools/clippy/tests/ui/unknown_clippy_lints.stderr

+18-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ error: unknown lint: `clippy::dead_cod`
3535
--> $DIR/unknown_clippy_lints.rs:11:8
3636
|
3737
LL | #[warn(clippy::dead_cod)]
38-
| ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::eq_op`
38+
| ^^^^^^^^^^^^^^^^
39+
|
40+
help: a lint with the similar name exists in `rustc` lints
41+
|
42+
LL | #[warn(dead_code)]
43+
| ~~~~~~~~~
3944

4045
error: unknown lint: `clippy::unused_colle`
4146
--> $DIR/unknown_clippy_lints.rs:13:8
@@ -49,5 +54,16 @@ error: unknown lint: `clippy::const_static_lifetim`
4954
LL | #[warn(clippy::const_static_lifetim)]
5055
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes`
5156

52-
error: aborting due to 8 previous errors
57+
error: unknown lint: `clippy::missing_docs`
58+
--> $DIR/unknown_clippy_lints.rs:17:8
59+
|
60+
LL | #[warn(clippy::missing_docs)]
61+
| ^^^^^^^^^^^^^^^^^^^^
62+
|
63+
help: a lint with the similar name exists in `rustc` lints
64+
|
65+
LL | #[warn(missing_docs)]
66+
| ~~~~~~~~~~~~
67+
68+
error: aborting due to 9 previous errors
5369

tests/rustdoc-ui/lints/unknown-renamed-lints.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ error: unknown lint: `rustdoc::intra_doc_link_resolution_failure`
5757
|
5858
LL | #![deny(rustdoc::intra_doc_link_resolution_failure)]
5959
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
60+
|
61+
help: a lint with the similar name exists in `rustc` lints
62+
|
63+
LL | #![deny(intra_doc_link_resolution_failure)]
64+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6065

6166
error: aborting due to 8 previous errors
6267

tests/ui/lint/issue-83477.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#[allow(rustc::foo::bar::default_hash_types)]
66
//~^ WARN unknown lint: `rustc::foo::bar::default_hash_types`
7-
//~| HELP did you mean
7+
//~| HELP a lint with the similar name exists in `rustc` lints
88
//~| SUGGESTION rustc::default_hash_types
99
#[allow(rustc::foo::default_hash_types)]
1010
//~^ WARN unknown lint: `rustc::foo::default_hash_types`

tests/ui/lint/issue-83477.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ warning: unknown lint: `rustc::foo::bar::default_hash_types`
22
--> $DIR/issue-83477.rs:5:9
33
|
44
LL | #[allow(rustc::foo::bar::default_hash_types)]
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: `#[warn(unknown_lints)]` on by default
8+
help: a lint with the similar name exists in `rustc` lints
9+
|
10+
LL | #[allow(rustc::default_hash_types)]
11+
| ~~~~~~~~~~~~~~~~~~~~~~~~~
812

913
warning: unknown lint: `rustc::foo::default_hash_types`
1014
--> $DIR/issue-83477.rs:9:9

0 commit comments

Comments
 (0)