diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 40e6b1b579f5b..5df6cf19d3ee4 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -532,8 +532,14 @@ lint_unknown_gated_lint = lint_unknown_lint = unknown lint: `{$name}` - .suggestion = did you mean - .help = did you mean: `{$replace}` + .suggestion = {$from_rustc -> + [true] a lint with the similar name exists in `rustc` lints + *[false] did you mean + } + .help = {$from_rustc -> + [true] a lint with the similar name exists in `rustc`, did you mean: `{$replace}` + *[false] did you mean: `{$replace}` + } lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d0fd019a8b122..82883d7a62f50 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -117,7 +117,7 @@ struct LintGroup { pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. - NoLint(Option), + NoLint(Option<(Symbol, bool)>), /// The lint refers to a tool that has not been registered. NoTool, /// The lint has been renamed to a new name. @@ -377,7 +377,7 @@ impl LintStore { debug!("lints={:?}", self.by_name.keys().collect::>()); let tool_prefix = format!("{tool_name}::"); return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) { - self.no_lint_suggestion(&complete_name) + self.no_lint_suggestion(&complete_name, tool_name.as_str()) } else { // 2. The tool isn't currently running, so no lints will be registered. // To avoid giving a false positive, ignore all unknown lints. @@ -419,29 +419,49 @@ impl LintStore { } } - fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> { + fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> { let name_lower = lint_name.to_lowercase(); if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() { // First check if the lint name is (partly) in upper case instead of lower case... - return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower))); + return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false))); } + // ...if not, search for lints with a similar name - // Note: find_best_match_for_name depends on the sort order of its input vector. - // To ensure deterministic output, sort elements of the lint_groups hash map. // Also, never suggest deprecated lint groups. - // We will soon sort, so the initial order does not matter. #[allow(rustc::potential_query_instability)] - let mut groups: Vec<_> = self + let groups: Vec<_> = self .lint_groups .iter() .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k)) .collect(); - groups.sort(); let groups = groups.iter().map(|k| Symbol::intern(k)); let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower())); - let names: Vec = groups.chain(lints).collect(); - let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None); + let mut names: Vec = groups.chain(lints).collect(); + // Here, a little bit of extra hack + // we use the lint names from rustc and to mock the tool name, + // if it's selected we then strip to the right suggestion with `true` for from_rustc + // so we can handle these cases: + // 1. suggest `missing_docs` from rustc for `clippy::missing_docs` + // 2. suggest `dead_code` from rustc for `clippy::dead_cod` + let rustc_names = self + .by_name + .keys() + .map(|k| Symbol::intern(&format!("{tool_name}::{k}"))) + .collect::>(); + names.extend(rustc_names.clone()); + // Note: find_best_match_for_name depends on the sort order of its input vector. + // Sort elements here to ensure deterministic output + names.sort(); + let suggestion = + find_best_match_for_name(&names, Symbol::intern(&name_lower), None).map(|s| { + if rustc_names.contains(&s) { + let stripped = s.as_str().strip_prefix(&format!("{tool_name}::")).unwrap(); + (Symbol::intern(stripped), true) + } else { + (s, false) + } + }); CheckLintNameResult::NoLint(suggestion) } @@ -454,7 +474,7 @@ 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 => self.no_lint_suggestion(lint_name), + None => self.no_lint_suggestion(lint_name, tool_name), Some(LintGroup { lint_ids, depr, .. }) => { // Reaching this would be weird, but let's cover this case anyway if let Some(LintAlias { name, silent }) = depr { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 49821437b7657..3c2d0c7b20536 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -582,8 +582,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } CheckLintNameResult::NoLint(suggestion) => { let name = lint_name.clone(); - let suggestion = - suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace }); + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithoutSpan { replace, from_rustc } + }); let requested_level = RequestedLevel { level, lint_name }; let lint = UnknownLintFromCommandLine { name, suggestion, requested_level }; self.emit_lint(UNKNOWN_LINTS, lint); @@ -990,8 +991,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } else { name.to_string() }; - let suggestion = suggestion.map(|replace| { - UnknownLintSuggestion::WithSpan { suggestion: sp, replace } + let suggestion = suggestion.map(|(replace, from_rustc)| { + UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc } }); let lint = UnknownLint { name, suggestion }; self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index bc9a9d7b7452d..f370c4392b384 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1050,9 +1050,10 @@ pub enum UnknownLintSuggestion { #[primary_span] suggestion: Span, replace: Symbol, + from_rustc: bool, }, #[help(lint_help)] - WithoutSpan { replace: Symbol }, + WithoutSpan { replace: Symbol, from_rustc: bool }, } #[derive(LintDiagnostic)] diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed index cba32ce6b772a..c0ccd41c19ab6 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed @@ -7,10 +7,12 @@ #[warn(clippy::if_not_else)] #[warn(clippy::unnecessary_cast)] #[warn(clippy::useless_transmute)] -// Shouldn't suggest rustc lint name(`dead_code`) -#[warn(clippy::eq_op)] +// Should suggest rustc lint name(`dead_code`) +#[warn(dead_code)] // Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) #[warn(clippy::unused_self)] // Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) #[warn(clippy::redundant_static_lifetimes)] +// issue #118183, should report `missing_docs` from rustc lint +#[warn(missing_docs)] fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs index c15d541974f00..7a59ad364aa7f 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs @@ -7,10 +7,12 @@ #[warn(clippy::if_not_els)] #[warn(clippy::UNNecsaRy_cAst)] #[warn(clippy::useles_transute)] -// Shouldn't suggest rustc lint name(`dead_code`) +// Should suggest rustc lint name(`dead_code`) #[warn(clippy::dead_cod)] // Shouldn't suggest removed/deprecated clippy lint name(`unused_collect`) #[warn(clippy::unused_colle)] // Shouldn't suggest renamed clippy lint name(`const_static_lifetime`) #[warn(clippy::const_static_lifetim)] +// issue #118183, should report `missing_docs` from rustc lint +#[warn(clippy::missing_docs)] fn main() {} diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr index ee82db31c2c2a..d838f09529184 100644 --- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr +++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr @@ -35,7 +35,12 @@ error: unknown lint: `clippy::dead_cod` --> $DIR/unknown_clippy_lints.rs:11:8 | LL | #[warn(clippy::dead_cod)] - | ^^^^^^^^^^^^^^^^ help: did you mean: `clippy::eq_op` + | ^^^^^^^^^^^^^^^^ + | +help: a lint with the similar name exists in `rustc` lints + | +LL | #[warn(dead_code)] + | ~~~~~~~~~ error: unknown lint: `clippy::unused_colle` --> $DIR/unknown_clippy_lints.rs:13:8 @@ -49,5 +54,16 @@ error: unknown lint: `clippy::const_static_lifetim` LL | #[warn(clippy::const_static_lifetim)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::redundant_static_lifetimes` -error: aborting due to 8 previous errors +error: unknown lint: `clippy::missing_docs` + --> $DIR/unknown_clippy_lints.rs:17:8 + | +LL | #[warn(clippy::missing_docs)] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: a lint with the similar name exists in `rustc` lints + | +LL | #[warn(missing_docs)] + | ~~~~~~~~~~~~ + +error: aborting due to 9 previous errors diff --git a/tests/rustdoc-ui/lints/unknown-renamed-lints.stderr b/tests/rustdoc-ui/lints/unknown-renamed-lints.stderr index bf529b9f8e263..591c9152f75b4 100644 --- a/tests/rustdoc-ui/lints/unknown-renamed-lints.stderr +++ b/tests/rustdoc-ui/lints/unknown-renamed-lints.stderr @@ -57,6 +57,11 @@ error: unknown lint: `rustdoc::intra_doc_link_resolution_failure` | LL | #![deny(rustdoc::intra_doc_link_resolution_failure)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: a lint with the similar name exists in `rustc` lints + | +LL | #![deny(intra_doc_link_resolution_failure)] + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 8 previous errors diff --git a/tests/ui/lint/issue-83477.rs b/tests/ui/lint/issue-83477.rs index 4262a28799db2..0cd27bef3446a 100644 --- a/tests/ui/lint/issue-83477.rs +++ b/tests/ui/lint/issue-83477.rs @@ -4,7 +4,7 @@ #[allow(rustc::foo::bar::default_hash_types)] //~^ WARN unknown lint: `rustc::foo::bar::default_hash_types` -//~| HELP did you mean +//~| HELP a lint with the similar name exists in `rustc` lints //~| SUGGESTION rustc::default_hash_types #[allow(rustc::foo::default_hash_types)] //~^ WARN unknown lint: `rustc::foo::default_hash_types` diff --git a/tests/ui/lint/issue-83477.stderr b/tests/ui/lint/issue-83477.stderr index f824fc09e7240..4d6a6520d51fe 100644 --- a/tests/ui/lint/issue-83477.stderr +++ b/tests/ui/lint/issue-83477.stderr @@ -2,9 +2,13 @@ warning: unknown lint: `rustc::foo::bar::default_hash_types` --> $DIR/issue-83477.rs:5:9 | LL | #[allow(rustc::foo::bar::default_hash_types)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `rustc::default_hash_types` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unknown_lints)]` on by default +help: a lint with the similar name exists in `rustc` lints + | +LL | #[allow(rustc::default_hash_types)] + | ~~~~~~~~~~~~~~~~~~~~~~~~~ warning: unknown lint: `rustc::foo::default_hash_types` --> $DIR/issue-83477.rs:9:9