Skip to content

Showcase nightly lints with macro #8444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
176 changes: 136 additions & 40 deletions clippy_dev/src/update_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
r#"(?x)
declare_clippy_lint!\s*[\{(]
(?:\s+///.*)*
(?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
(?:\s*\#\[clippy::version\s*=\s*"(?P<version>[^"]*)"\])?
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
(?P<cat>[a-z_]+)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
Expand All @@ -32,7 +32,7 @@ static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
r#"(?x)
declare_deprecated_lint!\s*[{(]\s*
(?:\s+///.*)*
(?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
(?:\s*\#\[clippy::version\s*=\s*"(?P<version>[^"]*)"\])?
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
"#,
Expand Down Expand Up @@ -200,17 +200,26 @@ struct Lint {
desc: String,
deprecation: Option<String>,
module: String,
version: Option<String>,
}

impl Lint {
#[must_use]
fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
fn new(
name: &str,
group: &str,
desc: &str,
deprecation: Option<&str>,
module: &str,
version: Option<&str>,
) -> Self {
Self {
name: name.to_lowercase(),
group: group.to_string(),
desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
deprecation: deprecation.map(ToString::to_string),
module: module.to_string(),
version: version.map(str::to_string),
}
}

Expand Down Expand Up @@ -245,19 +254,26 @@ impl Lint {

/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase(), &l.version)).collect();
details.sort_unstable();

let mut output = GENERATED_FILE_COMMENT.to_string();

output.push_str(&format!(
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
"store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), [\n",
group_name
));
for (module, name) in details {
output.push_str(&format!(" LintId::of({}::{}),\n", module, name));
for (module, name, version) in details {
if version.as_ref().map_or(false, |v| v == "nightly") {
output.push_str(&format!(
" clippy_utils::nightly::is_nightly_run().then_some(LintId::of({}::{})),\n",
module, name
));
} else {
output.push_str(&format!(" Some(LintId::of({}::{})),\n", module, name));
}
}
output.push_str("])\n");
output.push_str("].iter().copied().flatten().collect::<Vec<_>>())\n");

output
}
Expand Down Expand Up @@ -304,6 +320,39 @@ fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
output
}

/// This will be used to create a lint that warns when a nightly lint is
#[allow(dead_code)]
fn gen_nightly_lint_list<'a>(
internal_lints: impl Iterator<Item = &'a Lint>,
usable_lints: impl Iterator<Item = &'a Lint>,
) -> String {
let mut details: Vec<_> = internal_lints
.map(|l| (false, l))
.chain(usable_lints.map(|l| (true, l)))
.filter(|(_, l)| l.version.as_ref().map_or(false, |v| v == "nightly"))
.map(|(p, l)| (p, &l.module, l.name.to_uppercase()))
.collect();
details.sort_unstable();

let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("clippy_utils::nightly::set_nightly_lints([\n");
// The test lint "FOREVER_NIGHTLY_LINT" is in the `internal_warn` group which is
// not processed by `update_lints`. For testing purposes we still need the lint to be
// registered in the `nightly_lints` list. This manually adds this one lint.
output.push_str(" #[cfg(feature = \"internal\")]\n");
output.push_str(" LintId::of(utils::internal_lints::FOREVER_NIGHTLY_LINT),\n");

for (is_public, module_name, lint_name) in details {
if !is_public {
output.push_str(" #[cfg(feature = \"internal\")]\n");
}
output.push_str(&format!(" LintId::of({}::{}),\n", module_name, lint_name));
}
output.push_str("])\n");

output
}

/// Generates the code for registering lints
#[must_use]
fn gen_register_lint_list<'a>(
Expand Down Expand Up @@ -359,12 +408,26 @@ fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint>
}

fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
let lints = DEC_CLIPPY_LINT_RE
.captures_iter(content)
.map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
let deprecated = DEC_DEPRECATED_LINT_RE
.captures_iter(content)
.map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
let lints = DEC_CLIPPY_LINT_RE.captures_iter(content).map(|m| {
Lint::new(
&m["name"],
&m["cat"],
&m["desc"],
None,
module,
m.name("version").map(|v| v.as_str()),
)
});
let deprecated = DEC_DEPRECATED_LINT_RE.captures_iter(content).map(|m| {
Lint::new(
&m["name"],
"Deprecated",
&m["desc"],
Some(&m["desc"]),
module,
m.name("version").map(|v| v.as_str()),
)
});
// Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
}
Expand Down Expand Up @@ -505,7 +568,6 @@ declare_clippy_lint! {
}

declare_clippy_lint!{
#[clippy::version = "Test version"]
pub DOC_MARKDOWN,
pedantic,
"single line"
Expand All @@ -523,14 +585,22 @@ declare_deprecated_lint! {
.collect();

let expected = vec![
Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
Lint::new(
"ptr_arg",
"style",
"really long text",
None,
"module_name",
Some("Hello Clippy!"),
),
Lint::new("doc_markdown", "pedantic", "single line", None, "module_name", None),
Lint::new(
"should_assert_eq",
"Deprecated",
"`assert!()` will be more flexible with RFC 2011",
Some("`assert!()` will be more flexible with RFC 2011"),
"module_name",
Some("I'm a version"),
),
];
assert_eq!(expected, result);
Expand Down Expand Up @@ -580,48 +650,63 @@ mod tests {
#[test]
fn test_usable_lints() {
let lints = vec![
Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
Lint::new(
"should_assert_eq",
"Deprecated",
"abc",
Some("Reason"),
"module_name",
None,
),
Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name", None),
Lint::new("should_assert_eq2", "internal", "abc", None, "module_name", None),
Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name", None),
];
let expected = vec![Lint::new(
"should_assert_eq2",
"Not Deprecated",
"abc",
None,
"module_name",
None,
)];
assert_eq!(expected, Lint::usable_lints(&lints));
}

#[test]
fn test_by_lint_group() {
let lints = vec![
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name", None),
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name", None),
Lint::new("incorrect_match", "group1", "abc", None, "module_name", None),
];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert(
"group1".to_string(),
vec![
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name", None),
Lint::new("incorrect_match", "group1", "abc", None, "module_name", None),
],
);
expected.insert(
"group2".to_string(),
vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
vec![Lint::new(
"should_assert_eq2",
"group2",
"abc",
None,
"module_name",
None,
)],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}

#[test]
fn test_gen_changelog_lint_list() {
let lints = vec![
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name", None),
Lint::new("should_assert_eq2", "group2", "abc", None, "module_name", None),
];
let expected = vec![
format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK),
Expand All @@ -639,13 +724,15 @@ mod tests {
"abc",
Some("has been superseded by should_assert_eq2"),
"module_name",
None,
),
Lint::new(
"another_deprecated",
"group2",
"abc",
Some("will be removed"),
"module_name",
None,
),
];

Expand All @@ -671,15 +758,22 @@ mod tests {
#[test]
#[should_panic]
fn test_gen_deprecated_fail() {
let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
let lints = vec![Lint::new(
"should_assert_eq2",
"group2",
"abc",
None,
"module_name",
None,
)];
let _deprecated_lints = gen_deprecated(lints.iter());
}

#[test]
fn test_gen_modules_list() {
let lints = vec![
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name", None),
Lint::new("incorrect_stuff", "group3", "abc", None, "another_module", None),
];
let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
assert_eq!(expected, gen_modules_list(lints.iter()));
Expand All @@ -688,17 +782,19 @@ mod tests {
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
Lint::new("abc", "group1", "abc", None, "module_name"),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
Lint::new("internal", "internal_style", "abc", None, "module_name"),
Lint::new("abc", "group1", "abc", None, "module_name", Some("1.0.1")),
Lint::new("should_assert_eq", "group1", "abc", None, "module_name", Some("1.60.0")),
Lint::new("internal", "internal_style", "abc", None, "module_name", None),
Lint::new("nightly_lint", "nightly", "abc", None, "module_name", Some("nightly")),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[
"store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
" LintId::of(module_name::ABC),",
" LintId::of(module_name::INTERNAL),",
" LintId::of(module_name::SHOULD_ASSERT_EQ),",
"])",
"store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), [",
" Some(LintId::of(module_name::ABC)),",
" Some(LintId::of(module_name::INTERNAL)),",
" clippy_utils::nightly::is_nightly_run().then_some(LintId::of(module_name::NIGHTLY_LINT)),",
" Some(LintId::of(module_name::SHOULD_ASSERT_EQ)),",
"].iter().copied().flatten().collect::<Vec<_>>())",
]
.join("\n")
+ "\n";
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/absurd_extreme_comparisons.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::declare_lint_pass;

use clippy_utils::comparisons::{normalize_comparison, Rel};
use clippy_utils::consts::{constant, Constant};
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/approx_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_session::impl_lint_pass;
use rustc_span::symbol;
use std::f64::consts as f64;

Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clippy_utils::consts::constant_simple;
use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_session::impl_lint_pass;
use rustc_span::source_map::Span;

declare_clippy_lint! {
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/as_conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/asm_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt;
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::declare_lint_pass;

#[derive(Clone, Copy, PartialEq, Eq)]
enum AsmStyle {
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/assertions_on_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::declare_lint_pass;
use rustc_span::sym;

declare_clippy_lint! {
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/assign_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
Expand Down
Loading