From 5aeb08ed70b4bb40696f4c0d3216c749b7175a25 Mon Sep 17 00:00:00 2001
From: y21 <30553356+y21@users.noreply.github.com>
Date: Sat, 5 Oct 2024 02:23:59 +0200
Subject: [PATCH] add a book page for lint subgroups
---
book/src/SUMMARY.md | 1 +
book/src/lint_subgroups.md | 218 +++++++++++++++++
clippy_dev/src/lib.rs | 2 +-
clippy_dev/src/update_lints.rs | 221 +++++++++++++++---
clippy_lints/src/assigning_clones.rs | 3 +-
clippy_lints/src/dbg_macro.rs | 3 +-
clippy_lints/src/declare_clippy_lint.rs | 15 +-
clippy_lints/src/format_push_string.rs | 3 +-
clippy_lints/src/indexing_slicing.rs | 3 +-
clippy_lints/src/methods/mod.rs | 24 +-
.../src/missing_asserts_for_indexing.rs | 3 +-
clippy_lints/src/mutex_atomic.rs | 3 +-
clippy_lints/src/operators/mod.rs | 6 +-
clippy_lints/src/panic_unimplemented.rs | 12 +-
clippy_lints/src/pass_by_ref_or_value.rs | 3 +-
clippy_lints/src/redundant_clone.rs | 3 +-
clippy_lints/src/regex.rs | 3 +-
.../src/significant_drop_tightening.rs | 3 +-
clippy_lints/src/strings.rs | 3 +-
clippy_lints/src/unnecessary_box_returns.rs | 3 +-
20 files changed, 479 insertions(+), 56 deletions(-)
create mode 100644 book/src/lint_subgroups.md
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index be13fcbe260f..79de3a700a9b 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -7,6 +7,7 @@
- [Configuration](configuration.md)
- [Lint Configuration](lint_configuration.md)
- [Clippy's Lints](lints.md)
+- [Lint subgroups](lint_subgroups.md)
- [Continuous Integration](continuous_integration/README.md)
- [GitHub Actions](continuous_integration/github_actions.md)
- [GitLab CI](continuous_integration/gitlab.md)
diff --git a/book/src/lint_subgroups.md b/book/src/lint_subgroups.md
new file mode 100644
index 000000000000..41692ecc8fc4
--- /dev/null
+++ b/book/src/lint_subgroups.md
@@ -0,0 +1,218 @@
+# Lint subgroups
+
+Clippy groups its lints into [9 primary categories](lints.md),
+two of which are allow-by-default (pedantic and restriction).
+
+One downside of having such few but broad categories for allow-by-default lints
+is that it significantly decreases discoverability, as `restriction` often acts as a blanket category
+for any lint that most users likely would not want enforced on their codebase.
+
+This page should help with that, by defining more granular, unofficial lint (sub)groups.
+For example, some people might not be interested in all the style-related `pedantic` lints,
+but *are* interested in the `perf`-related ones, so these lints
+can additionally be added to the [`perf_pedantic`](#perf_pedantic) subgroup.
+
+
+
+## `perf_pedantic`
+
+These are `pedantic` lints that look for code patterns that could be expressed in a more efficient way.
+These would be candidates for the `perf` category, however suggestions made by them can also sometimes hurt readability
+and obfuscate the meaning, so occasional `#[allow]`s are expected to be used.
+
+
+Lints: `assigning_clones`, `inefficient_to_string`, `naive_bytecount`, `needless_bitwise_bool`, `trivially_copy_pass_by_ref`, `unnecessary_box_returns`, `unnecessary_join`
+
+
+#![warn] attribute
+
+```
+#![warn(
+ clippy::assigning_clones,
+ clippy::inefficient_to_string,
+ clippy::naive_bytecount,
+ clippy::needless_bitwise_bool,
+ clippy::trivially_copy_pass_by_ref,
+ clippy::unnecessary_box_returns,
+ clippy::unnecessary_join
+)]
+```
+
+
+
+Lint table
+
+```
+[lints.clippy]
+assigning_clones = "warn"
+inefficient_to_string = "warn"
+naive_bytecount = "warn"
+needless_bitwise_bool = "warn"
+trivially_copy_pass_by_ref = "warn"
+unnecessary_box_returns = "warn"
+unnecessary_join = "warn"
+```
+
+
+
+
+## `perf_restriction`
+
+These are `restriction` lints that can improve the performance of code, but are very specific
+and sometimes *significantly* hurt readability with very little gain in the usual case.
+These should ideally only be applied to specific functions or modules that were profiled
+and where it is very clear that any performance gain matters.
+
+As always (but especially here), you should double-check that applying these actually helps
+and that any performance wins are worth the introduced complexity.
+
+
+Lints: `format_push_string`, `missing_asserts_for_indexing`
+
+
+#![warn] attribute
+
+```
+#![warn(
+ clippy::format_push_string,
+ clippy::missing_asserts_for_indexing
+)]
+```
+
+
+
+Lint table
+
+```
+[lints.clippy]
+format_push_string = "warn"
+missing_asserts_for_indexing = "warn"
+```
+
+
+
+## `perf_nursery`
+
+These are `nursery` lints that either were previously in the `perf` category or are intended to be in `perf`
+but have too many false positives.
+Some of them may also be simply wrong in certain situations and end up slower,
+so you should make sure to read the description to learn about possible edge cases.
+
+
+Lints: `iter_with_drain`, `mutex_integer`, `needless_collect`, `or_fun_call`, `redundant_clone`, `significant_drop_tightening`, `trivial_regex`
+
+
+#![warn] attribute
+
+```
+#![warn(
+ clippy::iter_with_drain,
+ clippy::mutex_integer,
+ clippy::needless_collect,
+ clippy::or_fun_call,
+ clippy::redundant_clone,
+ clippy::significant_drop_tightening,
+ clippy::trivial_regex
+)]
+```
+
+
+
+Lint table
+
+```
+[lints.clippy]
+iter_with_drain = "warn"
+mutex_integer = "warn"
+needless_collect = "warn"
+or_fun_call = "warn"
+redundant_clone = "warn"
+significant_drop_tightening = "warn"
+trivial_regex = "warn"
+```
+
+
+
+## `panic`
+
+These are `restriction` lints that look for patterns that can introduce panics.
+
+Usually panics are not something that one should want to avoid and most of the time panicking is perfectly valid
+(hence why these lints are in the `restriction` category),
+but users may want to forbid any use of panicky functions altogether in specific contexts.
+
+One use case could be to annotate `GlobalAlloc` impls in which unwinding is Undefined Behavior.
+
+
+Lints: `arithmetic_side_effects`, `expect_used`, `indexing_slicing`, `panic`, `string_slice`, `todo`, `unimplemented`, `unreachable`, `unwrap_used`
+
+
+#![warn] attribute
+
+```
+#![warn(
+ clippy::arithmetic_side_effects,
+ clippy::expect_used,
+ clippy::indexing_slicing,
+ clippy::panic,
+ clippy::string_slice,
+ clippy::todo,
+ clippy::unimplemented,
+ clippy::unreachable,
+ clippy::unwrap_used
+)]
+```
+
+
+
+Lint table
+
+```
+[lints.clippy]
+arithmetic_side_effects = "warn"
+expect_used = "warn"
+indexing_slicing = "warn"
+panic = "warn"
+string_slice = "warn"
+todo = "warn"
+unimplemented = "warn"
+unreachable = "warn"
+unwrap_used = "warn"
+```
+
+
+
+## `debug`
+
+These are lints that can be useful to disable in CI, as they might indicate that code needs more work
+or has remaining debugging artifacts.
+
+
+Lints: `dbg_macro`, `todo`
+
+
+#![warn] attribute
+
+```
+#![warn(
+ clippy::dbg_macro,
+ clippy::todo
+)]
+```
+
+
+
+Lint table
+
+```
+[lints.clippy]
+dbg_macro = "warn"
+todo = "warn"
+```
+
+
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index ad385d5fbd29..fe3ef11f2ef8 100644
--- a/clippy_dev/src/lib.rs
+++ b/clippy_dev/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(let_chains)]
+#![feature(let_chains, debug_closure_helpers)]
#![feature(rustc_private)]
#![warn(
trivial_casts,
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index d6ed36d52f4e..31af479b6a6d 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -37,6 +37,81 @@ pub fn update(update_mode: UpdateMode) {
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
}
+/// Processes the `book/src/lint_subgroups.md` file
+fn process_subgroups(update_mode: UpdateMode, usable_lints: &[Lint]) {
+ fn lint_formatter<'a>(
+ lint_names: &'a [&Lint],
+ delim: &'a str,
+ write: impl Fn(&str, &mut fmt::Formatter<'_>) -> fmt::Result + 'a,
+ ) -> impl fmt::Display + 'a {
+ fmt::from_fn(move |f| {
+ for (i, lint) in lint_names.iter().copied().enumerate() {
+ if i > 0 {
+ f.write_str(delim)?;
+ }
+ write(&lint.name, f)?;
+ }
+
+ Ok(())
+ })
+ }
+
+ let mut grouped_lints = usable_lints
+ .iter()
+ .flat_map(|lint| lint.subgroups.iter().map(move |subgroup| (&**subgroup, lint)))
+ .into_group_map();
+
+ replace_region_in_file(
+ update_mode,
+ Path::new("book/src/lint_subgroups.md"),
+ "",
+ |res, middle| {
+ let (group_name, _) = middle.trim_start().split_once(' ').unwrap();
+
+ // Iteratively remove them so that we ideally end up with an empty map at the end if all sections
+ // exist in the book. This is where we'll also catch any typos in the subgroup names.
+ let group_lints = grouped_lints.remove(group_name).unwrap_or_default();
+
+ indoc::writedoc!(
+ res,
+ "
+ {group_name} -->
+ Lints: {lint_names}
+
+
+ #![warn] attribute
+
+ ```
+ #![warn(
+ {warn_attrs}
+ )]
+ ```
+
+
+
+ Lint table
+
+ ```
+ [lints.clippy]
+ {lint_table}
+ ```
+
+ ",
+ lint_names = lint_formatter(&group_lints, ", ", |lint, f| write!(f, "`{lint}`")),
+ warn_attrs = lint_formatter(&group_lints, ",\n ", |lint, f| write!(f, "clippy::{lint}")),
+ lint_table = lint_formatter(&group_lints, "\n", |lint, f| write!(f, r#"{lint} = "warn""#))
+ )
+ .unwrap();
+ },
+ );
+
+ assert!(
+ grouped_lints.is_empty(),
+ "subgroup book page is missing sections for: {grouped_lints:?}"
+ );
+}
+
fn generate_lint_files(
update_mode: UpdateMode,
lints: &[Lint],
@@ -47,12 +122,14 @@ fn generate_lint_files(
let mut usable_lints = Lint::usable_lints(lints);
usable_lints.sort_by_key(|lint| lint.name.clone());
+ process_subgroups(update_mode, &usable_lints);
+
replace_region_in_file(
update_mode,
Path::new("README.md"),
"[There are over ",
" lints included in this crate!]",
- |res| {
+ |res, _| {
write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
},
);
@@ -62,7 +139,7 @@ fn generate_lint_files(
Path::new("book/src/README.md"),
"[There are over ",
" lints included in this crate!]",
- |res| {
+ |res, _| {
write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
},
);
@@ -72,7 +149,7 @@ fn generate_lint_files(
Path::new("CHANGELOG.md"),
"\n",
"",
- |res| {
+ |res, _| {
for lint in usable_lints
.iter()
.map(|l| &*l.name)
@@ -91,7 +168,7 @@ fn generate_lint_files(
Path::new("clippy_lints/src/lib.rs"),
"// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
"// end lints modules, do not remove this comment, it’s used in `update_lints`",
- |res| {
+ |res, _| {
for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
writeln!(res, "mod {lint_mod};").unwrap();
}
@@ -527,16 +604,25 @@ struct Lint {
desc: String,
module: String,
declaration_range: Range,
+ subgroups: Vec,
}
impl Lint {
#[must_use]
- fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range) -> Self {
+ fn new(
+ name: &str,
+ group: &str,
+ desc: &str,
+ module: &str,
+ declaration_range: Range,
+ subgroups: Vec,
+ ) -> Self {
Self {
name: name.to_lowercase(),
group: group.into(),
desc: remove_line_splices(desc),
module: module.into(),
+ subgroups,
declaration_range,
}
}
@@ -733,7 +819,9 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) {
let start = range.start;
let mut iter = iter
.by_ref()
- .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+ .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }))
+ .peekable();
+
// matches `!{`
match_tokens!(iter, Bang OpenBrace);
match iter.next() {
@@ -762,13 +850,36 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec) {
Literal{..}(desc)
);
+ // If we see a comma now, then a number of subgroups follow, e.g.:
+ // [perf_pedantic, panic]
+ let mut subgroups = Vec::new();
+
+ if iter.next_if(|s| s.token_kind == TokenKind::Comma).is_some() {
+ match_tokens!(iter, OpenBracket);
+
+ while !matches!(
+ iter.peek(),
+ Some(LintDeclSearchResult {
+ token_kind: TokenKind::CloseBracket,
+ ..
+ })
+ ) {
+ if !subgroups.is_empty() {
+ match_tokens!(iter, Comma);
+ }
+ subgroups.push(match_tokens!(iter, Ident(name)).0.to_owned());
+ }
+
+ match_tokens!(iter, CloseBracket);
+ }
+
if let Some(LintDeclSearchResult {
token_kind: TokenKind::CloseBrace,
range,
..
}) = iter.next()
{
- lints.push(Lint::new(name, group, desc, module, start..range.end));
+ lints.push(Lint::new(name, group, desc, module, start..range.end, subgroups));
}
}
}
@@ -854,13 +965,13 @@ fn remove_line_splices(s: &str) -> String {
///
/// # Panics
///
-/// Panics if the path could not read or then written
+/// Panics if the path could not be read or then written, or if the start/end patterns are not found
fn replace_region_in_file(
update_mode: UpdateMode,
path: &Path,
start: &str,
end: &str,
- write_replacement: impl FnMut(&mut String),
+ write_replacement: impl FnMut(&mut String, &str),
) {
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
@@ -879,25 +990,31 @@ fn replace_region_in_file(
}
}
-/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
-/// were found, or the missing delimiter if not.
+/// Repeatedly replaces a region in a text delimited by two strings. Returns the new text if both
+/// delimiters were found, or the missing delimiter if not.
fn replace_region_in_text<'a>(
- text: &str,
+ mut text: &str,
start: &'a str,
end: &'a str,
- mut write_replacement: impl FnMut(&mut String),
+ mut write_replacement: impl FnMut(&mut String, &str),
) -> Result {
- let (text_start, rest) = text.split_once(start).ok_or(start)?;
- let (_, text_end) = rest.split_once(end).ok_or(end)?;
-
let mut res = String::with_capacity(text.len() + 4096);
- res.push_str(text_start);
- res.push_str(start);
- write_replacement(&mut res);
- res.push_str(end);
- res.push_str(text_end);
+ let mut found_start = false;
+
+ while let Some((text_start, rest)) = text.split_once(start) {
+ let (middle, text_end) = rest.split_once(end).ok_or(end)?;
+
+ res.push_str(text_start);
+ res.push_str(start);
+ write_replacement(&mut res, middle);
+ res.push_str(end);
+
+ text = text_end;
+ found_start = true;
+ }
- Ok(res)
+ res.push_str(text);
+ if found_start { Ok(res) } else { Err(start) }
}
fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
@@ -973,6 +1090,14 @@ mod tests {
pedantic,
"single line"
}
+
+ declare_clippy_lint!{
+ #[clippy::version = "Test version"]
+ pub DOC_MARKDOWN,
+ pedantic,
+ "single line",
+ [perf_pedantic, panic]
+ }
"#;
let mut result = Vec::new();
parse_contents(CONTENTS, "module_name", &mut result);
@@ -987,6 +1112,7 @@ mod tests {
"\"really long text\"",
"module_name",
Range::default(),
+ Vec::new(),
),
Lint::new(
"doc_markdown",
@@ -994,6 +1120,15 @@ mod tests {
"\"single line\"",
"module_name",
Range::default(),
+ Vec::new(),
+ ),
+ Lint::new(
+ "doc_markdown",
+ "pedantic",
+ "\"single line\"",
+ "module_name",
+ Range::default(),
+ vec!["perf_pedantic".into(), "panic".into()],
),
];
assert_eq!(expected, result);
@@ -1008,6 +1143,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
),
Lint::new(
"should_assert_eq2",
@@ -1015,6 +1151,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
),
Lint::new(
"should_assert_eq2",
@@ -1022,6 +1159,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
),
];
let expected = vec![Lint::new(
@@ -1030,6 +1168,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
)];
assert_eq!(expected, Lint::usable_lints(&lints));
}
@@ -1037,20 +1176,49 @@ mod tests {
#[test]
fn test_by_lint_group() {
let lints = vec![
- Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+ Lint::new(
+ "should_assert_eq",
+ "group1",
+ "\"abc\"",
+ "module_name",
+ Range::default(),
+ Vec::new(),
+ ),
Lint::new(
"should_assert_eq2",
"group2",
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
+ ),
+ Lint::new(
+ "incorrect_match",
+ "group1",
+ "\"abc\"",
+ "module_name",
+ Range::default(),
+ Vec::new(),
),
- Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
];
let mut expected: HashMap> = HashMap::new();
expected.insert("group1".to_string(), vec![
- Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
- Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
+ Lint::new(
+ "should_assert_eq",
+ "group1",
+ "\"abc\"",
+ "module_name",
+ Range::default(),
+ Vec::new(),
+ ),
+ Lint::new(
+ "incorrect_match",
+ "group1",
+ "\"abc\"",
+ "module_name",
+ Range::default(),
+ Vec::new(),
+ ),
]);
expected.insert("group2".to_string(), vec![Lint::new(
"should_assert_eq2",
@@ -1058,6 +1226,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
+ Vec::new(),
)]);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
}
diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs
index 0b82c0cd04c1..08a7009e4be3 100644
--- a/clippy_lints/src/assigning_clones.rs
+++ b/clippy_lints/src/assigning_clones.rs
@@ -50,7 +50,8 @@ declare_clippy_lint! {
#[clippy::version = "1.78.0"]
pub ASSIGNING_CLONES,
pedantic,
- "assigning the result of cloning may be inefficient"
+ "assigning the result of cloning may be inefficient",
+ [perf_pedantic]
}
pub struct AssigningClones {
diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs
index a96c86f07657..f425392a2039 100644
--- a/clippy_lints/src/dbg_macro.rs
+++ b/clippy_lints/src/dbg_macro.rs
@@ -31,7 +31,8 @@ declare_clippy_lint! {
#[clippy::version = "1.34.0"]
pub DBG_MACRO,
restriction,
- "`dbg!` macro is intended as a debugging tool"
+ "`dbg!` macro is intended as a debugging tool",
+ [debug]
}
pub struct DbgMacro {
diff --git a/clippy_lints/src/declare_clippy_lint.rs b/clippy_lints/src/declare_clippy_lint.rs
index b1e39c70baa2..4836d5893e53 100644
--- a/clippy_lints/src/declare_clippy_lint.rs
+++ b/clippy_lints/src/declare_clippy_lint.rs
@@ -1,3 +1,5 @@
+// The $subgroup metavariable is parsed out externally in `clippy_dev/src/update_lints.rs` and just
+// ignored here.
#[macro_export]
#[allow(clippy::crate_in_macro_def)]
macro_rules! declare_clippy_lint {
@@ -24,7 +26,7 @@ macro_rules! declare_clippy_lint {
category: $lintcategory,
explanation: concat!($($lit,"\n",)*),
location: concat!(file!(), "#L", line!()),
- version: $version_expr
+ version: $version_expr,
};
};
(
@@ -33,6 +35,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
restriction,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -46,12 +49,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
style,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Warn, crate::LintCategory::Style, $desc,
Some($version), $version
-
}
};
(
@@ -60,12 +63,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
correctness,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
Some($version), $version
-
}
};
(
@@ -74,6 +77,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
perf,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -87,6 +91,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
complexity,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -100,6 +105,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
suspicious,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -113,6 +119,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
nursery,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -126,6 +133,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
pedantic,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
@@ -139,6 +147,7 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
cargo,
$desc:literal
+ $(,[$($subgroup:ident),+])?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs
index 8b1f86cbb913..78d24572f1f7 100644
--- a/clippy_lints/src/format_push_string.rs
+++ b/clippy_lints/src/format_push_string.rs
@@ -36,7 +36,8 @@ declare_clippy_lint! {
#[clippy::version = "1.62.0"]
pub FORMAT_PUSH_STRING,
restriction,
- "`format!(..)` appended to existing `String`"
+ "`format!(..)` appended to existing `String`",
+ [perf_restriction]
}
declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index 22e9674714fe..c5752323c43a 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -79,7 +79,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub INDEXING_SLICING,
restriction,
- "indexing/slicing usage"
+ "indexing/slicing usage",
+ [panic]
}
impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 722290fb68e5..d1c93a918f33 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -303,7 +303,8 @@ declare_clippy_lint! {
#[clippy::version = "1.45.0"]
pub UNWRAP_USED,
restriction,
- "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
+ "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`",
+ [panic]
}
declare_clippy_lint! {
@@ -366,7 +367,8 @@ declare_clippy_lint! {
#[clippy::version = "1.45.0"]
pub EXPECT_USED,
restriction,
- "using `.expect()` on `Result` or `Option`, which might be better handled"
+ "using `.expect()` on `Result` or `Option`, which might be better handled",
+ [panic]
}
declare_clippy_lint! {
@@ -937,7 +939,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub OR_FUN_CALL,
nursery,
- "using any `*or` method with a function call, which suggests `*or_else`"
+ "using any `*or` method with a function call, which suggests `*or_else`",
+ [perf_nursery]
}
declare_clippy_lint! {
@@ -1084,7 +1087,8 @@ declare_clippy_lint! {
#[clippy::version = "1.40.0"]
pub INEFFICIENT_TO_STRING,
pedantic,
- "using `to_string` on `&&T` where `T: ToString`"
+ "using `to_string` on `&&T` where `T: ToString`",
+ [perf_pedantic]
}
declare_clippy_lint! {
@@ -1295,7 +1299,8 @@ declare_clippy_lint! {
#[clippy::version = "1.61.0"]
pub ITER_WITH_DRAIN,
nursery,
- "replace `.drain(..)` with `.into_iter()`"
+ "replace `.drain(..)` with `.into_iter()`",
+ [perf_nursery]
}
declare_clippy_lint! {
@@ -2313,7 +2318,8 @@ declare_clippy_lint! {
#[clippy::version = "1.61.0"]
pub UNNECESSARY_JOIN,
pedantic,
- "using `.collect::>().join(\"\")` on an iterator"
+ "using `.collect::>().join(\"\")` on an iterator",
+ [perf_pedantic]
}
declare_clippy_lint! {
@@ -2530,7 +2536,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub NAIVE_BYTECOUNT,
pedantic,
- "use of naive `.filter(|&x| x == y).count()` to count byte values"
+ "use of naive `.filter(|&x| x == y).count()` to count byte values",
+ [perf_pedantic]
}
declare_clippy_lint! {
@@ -3279,7 +3286,8 @@ declare_clippy_lint! {
#[clippy::version = "1.30.0"]
pub NEEDLESS_COLLECT,
nursery,
- "collecting an iterator when collect is not needed"
+ "collecting an iterator when collect is not needed",
+ [perf_nursery]
}
declare_clippy_lint! {
diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs
index d78299fe08be..12e68fabaf22 100644
--- a/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -61,7 +61,8 @@ declare_clippy_lint! {
#[clippy::version = "1.74.0"]
pub MISSING_ASSERTS_FOR_INDEXING,
restriction,
- "indexing into a slice multiple times without an `assert`"
+ "indexing into a slice multiple times without an `assert`",
+ [perf_restriction]
}
declare_lint_pass!(MissingAssertsForIndexing => [MISSING_ASSERTS_FOR_INDEXING]);
diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs
index 388414964588..c0d39e07ffc0 100644
--- a/clippy_lints/src/mutex_atomic.rs
+++ b/clippy_lints/src/mutex_atomic.rs
@@ -71,7 +71,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub MUTEX_INTEGER,
nursery,
- "using a mutex for an integer type"
+ "using a mutex for an integer type",
+ [perf_nursery]
}
declare_lint_pass!(Mutex => [MUTEX_ATOMIC, MUTEX_INTEGER]);
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
index 9e8a821c3f4e..a1675e426ed8 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -94,7 +94,8 @@ declare_clippy_lint! {
#[clippy::version = "1.64.0"]
pub ARITHMETIC_SIDE_EFFECTS,
restriction,
- "any arithmetic expression that can cause side effects like overflows or panics"
+ "any arithmetic expression that can cause side effects like overflows or panics",
+ [panic]
}
declare_clippy_lint! {
@@ -766,7 +767,8 @@ declare_clippy_lint! {
#[clippy::version = "1.54.0"]
pub NEEDLESS_BITWISE_BOOL,
pedantic,
- "Boolean expressions that use bitwise rather than lazy operators"
+ "Boolean expressions that use bitwise rather than lazy operators",
+ [perf_pedantic]
}
declare_clippy_lint! {
diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs
index fa5b02a5a41b..a1c4455e5223 100644
--- a/clippy_lints/src/panic_unimplemented.rs
+++ b/clippy_lints/src/panic_unimplemented.rs
@@ -33,7 +33,8 @@ declare_clippy_lint! {
#[clippy::version = "1.40.0"]
pub PANIC,
restriction,
- "usage of the `panic!` macro"
+ "usage of the `panic!` macro",
+ [panic]
}
declare_clippy_lint! {
@@ -50,7 +51,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub UNIMPLEMENTED,
restriction,
- "`unimplemented!` should not be present in production code"
+ "`unimplemented!` should not be present in production code",
+ [panic]
}
declare_clippy_lint! {
@@ -72,7 +74,8 @@ declare_clippy_lint! {
#[clippy::version = "1.40.0"]
pub TODO,
restriction,
- "`todo!` should not be present in production code"
+ "`todo!` should not be present in production code",
+ [panic, debug]
}
declare_clippy_lint! {
@@ -89,7 +92,8 @@ declare_clippy_lint! {
#[clippy::version = "1.40.0"]
pub UNREACHABLE,
restriction,
- "usage of the `unreachable!` macro"
+ "usage of the `unreachable!` macro",
+ [panic]
}
impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs
index 1bddfab39c69..f33ea998c391 100644
--- a/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/clippy_lints/src/pass_by_ref_or_value.rs
@@ -68,7 +68,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub TRIVIALLY_COPY_PASS_BY_REF,
pedantic,
- "functions taking small copyable arguments by reference"
+ "functions taking small copyable arguments by reference",
+ [perf_pedantic]
}
declare_clippy_lint! {
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index b9e0106fc86b..4bd368458164 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -56,7 +56,8 @@ declare_clippy_lint! {
#[clippy::version = "1.32.0"]
pub REDUNDANT_CLONE,
nursery,
- "`clone()` of an owned value that is going to be dropped immediately"
+ "`clone()` of an owned value that is going to be dropped immediately",
+ [perf_nursery]
}
declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs
index 6a5bf1b8045d..54d80d013ade 100644
--- a/clippy_lints/src/regex.rs
+++ b/clippy_lints/src/regex.rs
@@ -52,7 +52,8 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub TRIVIAL_REGEX,
nursery,
- "trivial regular expressions"
+ "trivial regular expressions",
+ [perf_nursery]
}
declare_clippy_lint! {
diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs
index abd8363456df..41d7d7dc1dbb 100644
--- a/clippy_lints/src/significant_drop_tightening.rs
+++ b/clippy_lints/src/significant_drop_tightening.rs
@@ -49,7 +49,8 @@ declare_clippy_lint! {
#[clippy::version = "1.69.0"]
pub SIGNIFICANT_DROP_TIGHTENING,
nursery,
- "Searches for elements marked with `#[clippy::has_significant_drop]` that could be early dropped but are in fact dropped at the end of their scopes"
+ "Searches for elements marked with `#[clippy::has_significant_drop]` that could be early dropped but are in fact dropped at the end of their scopes",
+ [perf_nursery]
}
impl_lint_pass!(SignificantDropTightening<'_> => [SIGNIFICANT_DROP_TIGHTENING]);
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index e09c07060065..016b71edcdc1 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -140,7 +140,8 @@ declare_clippy_lint! {
#[clippy::version = "1.58.0"]
pub STRING_SLICE,
restriction,
- "slicing a string"
+ "slicing a string",
+ [panic]
}
declare_lint_pass!(StringAdd => [STRING_ADD, STRING_ADD_ASSIGN, STRING_SLICE]);
diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs
index 14f4aa6676b6..7583ef77314c 100644
--- a/clippy_lints/src/unnecessary_box_returns.rs
+++ b/clippy_lints/src/unnecessary_box_returns.rs
@@ -37,7 +37,8 @@ declare_clippy_lint! {
#[clippy::version = "1.70.0"]
pub UNNECESSARY_BOX_RETURNS,
pedantic,
- "Needlessly returning a Box"
+ "Needlessly returning a Box",
+ [perf_pedantic]
}
pub struct UnnecessaryBoxReturns {