From be82601e854a37b687bd80a994a903f62ffe54de Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 11 Oct 2025 23:48:20 -0400 Subject: [PATCH 01/10] `clippy_dev`: Move lint uplifting into its own command. --- clippy_dev/src/deprecate_lint.rs | 54 ++++++++++++++++++++++++++++- clippy_dev/src/main.rs | 36 +++++++++----------- clippy_dev/src/rename_lint.rs | 58 +++++--------------------------- 3 files changed, 78 insertions(+), 70 deletions(-) diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 0401cfda7080..bee7508dabb9 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -1,4 +1,4 @@ -use crate::parse::{DeprecatedLint, Lint, ParseCx}; +use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; @@ -61,6 +61,58 @@ pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, } } +pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { + let mut lints = cx.find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == old_name) else { + eprintln!("error: failed to find lint `{old_name}`"); + return; + }; + + let old_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }); + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name = new_name; + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` is already deprecated"); + return; + }, + Err(idx) => renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }, + ), + } + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(old_name, &mod_path, &mut lints).unwrap_or(false) { + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{old_name}` has successfully been uplifted"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) -> io::Result { fn remove_lint(name: &str, lints: &mut Vec>) { lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 392c3aabf193..9571dfde1877 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -74,18 +74,11 @@ fn main() { }, DevCommand::Serve { port, lint } => serve::run(port, lint), DevCommand::Lint { path, edition, args } => lint::run(&path, &edition, args.iter()), - DevCommand::RenameLint { - old_name, - new_name, - uplift, - } => new_parse_cx(|cx| { - rename_lint::rename( - cx, - clippy.version, - &old_name, - new_name.as_ref().unwrap_or(&old_name), - uplift, - ); + DevCommand::RenameLint { old_name, new_name } => new_parse_cx(|cx| { + rename_lint::rename(cx, clippy.version, &old_name, &new_name); + }), + DevCommand::Uplift { old_name, new_name } => new_parse_cx(|cx| { + deprecate_lint::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name)); }), DevCommand::Deprecate { name, reason } => { new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason)); @@ -243,15 +236,9 @@ enum DevCommand { /// The name of the lint to rename #[arg(value_parser = lint_name)] old_name: String, - #[arg( - required_unless_present = "uplift", - value_parser = lint_name, - )] + #[arg(value_parser = lint_name)] /// The new name of the lint - new_name: Option, - #[arg(long)] - /// This lint will be uplifted into rustc - uplift: bool, + new_name: String, }, /// Deprecate the given lint Deprecate { @@ -266,6 +253,15 @@ enum DevCommand { Sync(SyncCommand), /// Manage Clippy releases Release(ReleaseCommand), + /// Marks a lint as uplifted into rustc and removes its code + Uplift { + /// The name of the lint to uplift + #[arg(value_parser = lint_name)] + old_name: String, + /// The name of the lint in rustc + #[arg(value_parser = lint_name)] + new_name: Option, + }, } #[derive(Args)] diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index 8e30eb7ce95b..9be4f2bdc970 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -25,8 +25,7 @@ use std::path::Path; /// * If either lint name has a prefix /// * If `old_name` doesn't name an existing lint. /// * If `old_name` names a deprecated or renamed lint. -#[expect(clippy::too_many_lines)] -pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str, uplift: bool) { +pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str) { let mut updater = FileUpdater::default(); let mut lints = cx.find_lint_decls(); let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); @@ -34,20 +33,15 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { panic!("could not find lint `{old_name}`"); }; - let lint = &lints[lint_idx]; let old_name_prefixed = cx.str_buf.with(|buf| { buf.extend(["clippy::", old_name]); cx.arena.alloc_str(buf) }); - let new_name_prefixed = if uplift { - new_name - } else { - cx.str_buf.with(|buf| { - buf.extend(["clippy::", new_name]); - cx.arena.alloc_str(buf) - }) - }; + let new_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", new_name]); + cx.arena.alloc_str(buf) + }); for lint in &mut renamed_lints { if lint.new_name == old_name_prefixed { @@ -77,31 +71,7 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); let mut mod_edit = ModEdit::None; - if uplift { - let is_unique_mod = lints[..lint_idx].iter().any(|l| l.module == lint.module) - || lints[lint_idx + 1..].iter().any(|l| l.module == lint.module); - if is_unique_mod { - if delete_file_if_exists(lint.path.as_ref()) { - mod_edit = ModEdit::Delete; - } - } else { - updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { - let mut start = &src[..lint.declaration_range.start]; - if start.ends_with("\n\n") { - start = &start[..start.len() - 1]; - } - let mut end = &src[lint.declaration_range.end..]; - if end.starts_with("\n\n") { - end = &end[1..]; - } - dst.push_str(start); - dst.push_str(end); - UpdateStatus::Changed - }); - } - delete_test_files(old_name, change_prefixed_tests); - lints.remove(lint_idx); - } else if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { + if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { let lint = &mut lints[lint_idx]; if lint.module.ends_with(old_name) && lint @@ -139,19 +109,9 @@ pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - if uplift { - println!("Uplifted `clippy::{old_name}` as `{new_name}`"); - if matches!(mod_edit, ModEdit::None) { - println!("Only the rename has been registered, the code will need to be edited manually"); - } else { - println!("All the lint's code has been deleted"); - println!("Make sure to inspect the results as some things may have been missed"); - } - } else { - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); - println!("All code referencing the old name has been updated"); - println!("Make sure to inspect the results as some things may have been missed"); - } + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); println!("note: `cargo uibless` still needs to be run to update the test results"); } From b4c3f17a5aa0a072de6dac94348d36e478281821 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 12 Oct 2025 14:43:21 -0400 Subject: [PATCH 02/10] `clippy_dev`: Rename `deprecate_lint` module to `edit_lints`. --- clippy_dev/src/edit_lints.rs | 213 +++++++++++++++++++++++++++++++++++ clippy_dev/src/lib.rs | 2 +- clippy_dev/src/main.rs | 8 +- 3 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 clippy_dev/src/edit_lints.rs diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs new file mode 100644 index 000000000000..573bd1e44535 --- /dev/null +++ b/clippy_dev/src/edit_lints.rs @@ -0,0 +1,213 @@ +use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::update_lints::generate_lint_files; +use crate::utils::{UpdateMode, Version}; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +/// Runs the `deprecate` command +/// +/// This does the following: +/// * Adds an entry to `deprecated_lints.rs`. +/// * Removes the lint declaration (and the entire file if applicable) +/// +/// # Panics +/// +/// If a file path could not read from or written to +pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) { + let mut lints = cx.find_lint_decls(); + let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == name) else { + eprintln!("error: failed to find lint `{name}`"); + return; + }; + + let prefixed_name = cx.str_buf.with(|buf| { + buf.extend(["clippy::", name]); + cx.arena.alloc_str(buf) + }); + match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { + Ok(_) => { + println!("`{name}` is already deprecated"); + return; + }, + Err(idx) => deprecated_lints.insert( + idx, + DeprecatedLint { + name: prefixed_name, + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }, + ), + } + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { + let mut lints = cx.find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + + let Some(lint) = lints.iter().find(|l| l.name == old_name) else { + eprintln!("error: failed to find lint `{old_name}`"); + return; + }; + + let old_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }); + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name = new_name; + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` is already deprecated"); + return; + }, + Err(idx) => renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }, + ), + } + + let mod_path = { + let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); + if mod_path.is_dir() { + mod_path = mod_path.join("mod"); + } + + mod_path.set_extension("rs"); + mod_path + }; + + if remove_lint_declaration(old_name, &mod_path, &mut lints).unwrap_or(false) { + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{old_name}` has successfully been uplifted"); + println!("note: you must run `cargo uitest` to update the test results"); + } else { + eprintln!("error: lint not found"); + } +} + +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) -> io::Result { + fn remove_lint(name: &str, lints: &mut Vec>) { + lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); + } + + fn remove_test_assets(name: &str) { + let test_file_stem = format!("tests/ui/{name}"); + let path = Path::new(&test_file_stem); + + // Some lints have their own directories, delete them + if path.is_dir() { + let _ = fs::remove_dir_all(path); + return; + } + + // Remove all related test files + let _ = fs::remove_file(path.with_extension("rs")); + let _ = fs::remove_file(path.with_extension("stderr")); + let _ = fs::remove_file(path.with_extension("fixed")); + } + + fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { + let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { + content + .find("declare_lint_pass!") + .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) + }); + let mut impl_lint_pass_end = content[impl_lint_pass_start..] + .find(']') + .expect("failed to find `impl_lint_pass` terminator"); + + impl_lint_pass_end += impl_lint_pass_start; + if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { + let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); + for c in content[lint_name_end..impl_lint_pass_end].chars() { + // Remove trailing whitespace + if c == ',' || c.is_whitespace() { + lint_name_end += 1; + } else { + break; + } + } + + content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); + } + } + + if path.exists() + && let Some(lint) = lints.iter().find(|l| l.name == name) + { + if lint.module == name { + // The lint name is the same as the file, we can just delete the entire file + fs::remove_file(path)?; + } else { + // We can't delete the entire file, just remove the declaration + + if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { + // Remove clippy_lints/src/some_mod/some_lint.rs + let mut lint_mod_path = path.to_path_buf(); + lint_mod_path.set_file_name(name); + lint_mod_path.set_extension("rs"); + + let _ = fs::remove_file(lint_mod_path); + } + + let mut content = + fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); + + eprintln!( + "warn: you will have to manually remove any code related to `{name}` from `{}`", + path.display() + ); + + assert!( + content[lint.declaration_range].contains(&name.to_uppercase()), + "error: `{}` does not contain lint `{}`'s declaration", + path.display(), + lint.name + ); + + // Remove lint declaration (declare_clippy_lint!) + content.replace_range(lint.declaration_range, ""); + + // Remove the module declaration (mod xyz;) + let mod_decl = format!("\nmod {name};"); + content = content.replacen(&mod_decl, "", 1); + + remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); + fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); + } + + remove_test_assets(name); + remove_lint(name, lints); + return Ok(true); + } + + Ok(false) +} diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index dcca08aee7e6..4a93860121fa 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -24,8 +24,8 @@ extern crate rustc_driver; extern crate rustc_lexer; extern crate rustc_literal_escaper; -pub mod deprecate_lint; pub mod dogfood; +pub mod edit_lints; pub mod fmt; pub mod lint; pub mod new_lint; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 9571dfde1877..e4e19a100c6e 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,8 +4,8 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, - setup, sync, update_lints, + ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, setup, + sync, update_lints, }; use std::env; @@ -78,10 +78,10 @@ fn main() { rename_lint::rename(cx, clippy.version, &old_name, &new_name); }), DevCommand::Uplift { old_name, new_name } => new_parse_cx(|cx| { - deprecate_lint::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name)); + edit_lints::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name)); }), DevCommand::Deprecate { name, reason } => { - new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason)); + new_parse_cx(|cx| edit_lints::deprecate(cx, clippy.version, &name, &reason)); }, DevCommand::Sync(SyncCommand { subcommand }) => match subcommand { SyncSubcommand::UpdateNightly => sync::update_nightly(), From 53ca7d8dae08cfb42555bb552334e1c88080e8e8 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 12 Oct 2025 14:44:25 -0400 Subject: [PATCH 03/10] `clippy_dev`: Move `rename_lint` command to `edit_lints` module. --- clippy_dev/src/edit_lints.rs | 351 ++++++++++++++++++++++++++++++++- clippy_dev/src/lib.rs | 1 - clippy_dev/src/main.rs | 6 +- clippy_dev/src/rename_lint.rs | 353 ---------------------------------- 4 files changed, 352 insertions(+), 359 deletions(-) delete mode 100644 clippy_dev/src/rename_lint.rs diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 573bd1e44535..8a5ba26fd215 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,7 +1,12 @@ +use crate::parse::cursor::{self, Capture, Cursor}; use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; -use crate::utils::{UpdateMode, Version}; -use std::ffi::OsStr; +use crate::utils::{ + ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, + expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, +}; +use rustc_lexer::TokenKind; +use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::{fs, io}; @@ -113,6 +118,111 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam } } +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { + let mut updater = FileUpdater::default(); + let mut lints = cx.find_lint_decls(); + let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + + let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { + panic!("could not find lint `{old_name}`"); + }; + + let old_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }); + let new_name_prefixed = cx.str_buf.with(|buf| { + buf.extend(["clippy::", new_name]); + cx.arena.alloc_str(buf) + }); + + for lint in &mut renamed_lints { + if lint.new_name == old_name_prefixed { + lint.new_name = new_name_prefixed; + } + } + match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { + Ok(_) => { + println!("`{old_name}` already has a rename registered"); + return; + }, + Err(idx) => { + renamed_lints.insert( + idx, + RenamedLint { + old_name: old_name_prefixed, + new_name: new_name_prefixed, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }, + ); + }, + } + + // Some tests are named `lint_name_suffix` which should also be renamed, + // but we can't do that if the renamed lint's name overlaps with another + // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. + let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); + + let mut mod_edit = ModEdit::None; + if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { + let lint = &mut lints[lint_idx]; + if lint.module.ends_with(old_name) + && lint + .path + .file_stem() + .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) + { + let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + new_path.push(".rs"); + if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + mod_edit = ModEdit::Rename; + } + + lint.module = cx.str_buf.with(|buf| { + buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + buf.push_str(new_name); + cx.arena.alloc_str(buf) + }); + } + rename_test_files(old_name, new_name, change_prefixed_tests); + lints[lint_idx].name = new_name; + lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + } else { + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Since `{new_name}` already exists the existing code has not been changed"); + return; + } + + let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + for e in walk_dir_no_dot_or_target(".") { + let e = expect_action(e, ErrAction::Read, "."); + if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(e.path(), &mut update_fn); + } + } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + + println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("All code referencing the old name has been updated"); + println!("Make sure to inspect the results as some things may have been missed"); + println!("note: `cargo uibless` still needs to be run to update the test results"); +} + fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) -> io::Result { fn remove_lint(name: &str, lints: &mut Vec>) { lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); @@ -211,3 +321,240 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) - Ok(false) } + +#[derive(Clone, Copy)] +enum ModEdit { + None, + Delete, + Rename, +} + +fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { + let e = e.expect("error reading `tests/ui`"); + let name = e.file_name(); + if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { + if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { + dst.push((name, true)); + } + } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) + { + dst.push((name, false)); + } + } +} + +fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { + if rename_prefixed { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { + dst.push((name, false)); + } + } + } else { + dst.push((lint.into(), false)); + } +} + +/// Renames all test files for the given lint. +/// +/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. +fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut old_buf = OsString::from("tests/ui/"); + let mut new_buf = OsString::from("tests/ui/"); + collect_ui_test_names(old_name, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + if is_file { + try_rename_file(old_buf.as_ref(), new_buf.as_ref()); + } else { + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + } + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } + + tests.clear(); + old_buf.truncate("tests/ui".len()); + new_buf.truncate("tests/ui".len()); + old_buf.push("-toml/"); + new_buf.push("-toml/"); + collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + for (name, _) in &tests { + old_buf.push(name); + new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); + try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); + old_buf.truncate("tests/ui/".len()); + new_buf.truncate("tests/ui/".len()); + } +} + +fn delete_test_files(lint: &str, rename_prefixed: bool) { + let mut tests = Vec::new(); + + let mut buf = OsString::from("tests/ui/"); + collect_ui_test_names(lint, rename_prefixed, &mut tests); + for &(ref name, is_file) in &tests { + buf.push(name); + if is_file { + delete_file_if_exists(buf.as_ref()); + } else { + delete_dir_if_exists(buf.as_ref()); + } + buf.truncate("tests/ui/".len()); + } + + buf.truncate("tests/ui".len()); + buf.push("-toml/"); + + tests.clear(); + collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + for (name, _) in &tests { + buf.push(name); + delete_dir_if_exists(buf.as_ref()); + buf.truncate("tests/ui/".len()); + } +} + +fn snake_to_pascal(s: &str) -> String { + let mut dst = Vec::with_capacity(s.len()); + let mut iter = s.bytes(); + || -> Option<()> { + dst.push(iter.next()?.to_ascii_uppercase()); + while let Some(c) = iter.next() { + if c == b'_' { + dst.push(iter.next()?.to_ascii_uppercase()); + } else { + dst.push(c); + } + } + Some(()) + }(); + String::from_utf8(dst).unwrap() +} + +#[expect(clippy::too_many_lines)] +fn file_update_fn<'a, 'b>( + old_name: &'a str, + new_name: &'b str, + mod_edit: ModEdit, +) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + let old_name_pascal = snake_to_pascal(old_name); + let new_name_pascal = snake_to_pascal(new_name); + let old_name_upper = old_name.to_ascii_uppercase(); + let new_name_upper = new_name.to_ascii_uppercase(); + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut cursor = Cursor::new(src); + let mut captures = [Capture::EMPTY]; + loop { + match cursor.peek() { + TokenKind::Eof => break, + TokenKind::Ident => { + let match_start = cursor.pos(); + let text = cursor.peek_text(); + cursor.step(); + match text { + // clippy::line_name or clippy::lint-name + "clippy" => { + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name + { + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + changed = true; + } + }, + // mod lint_name + "mod" => { + if !matches!(mod_edit, ModEdit::None) + && let Some(pos) = cursor.find_ident(old_name) + { + match mod_edit { + ModEdit::Rename => { + dst.push_str(&src[copy_pos as usize..pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + changed = true; + }, + ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => { + let mut start = &src[copy_pos as usize..match_start as usize]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; + } + dst.push_str(start); + copy_pos = cursor.pos(); + if src[copy_pos as usize..].starts_with("\n\n") { + copy_pos += 1; + } + changed = true; + }, + ModEdit::Delete | ModEdit::None => {}, + } + } + }, + // lint_name:: + name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + let name_end = cursor.pos(); + if cursor.match_pat(cursor::Pat::DoubleColon) { + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(new_name); + copy_pos = name_end; + changed = true; + } + }, + // LINT_NAME or LintName + name => { + let replacement = if name == old_name_upper { + &new_name_upper + } else if name == old_name_pascal { + &new_name_pascal + } else { + continue; + }; + dst.push_str(&src[copy_pos as usize..match_start as usize]); + dst.push_str(replacement); + copy_pos = cursor.pos(); + changed = true; + }, + } + }, + // //~ lint_name + TokenKind::LineComment { doc_style: None } => { + let text = cursor.peek_text(); + if text.starts_with("//~") + && let Some(text) = text.strip_suffix(old_name) + && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) + { + dst.push_str(&src[copy_pos as usize..cursor.pos() as usize + text.len()]); + dst.push_str(new_name); + copy_pos = cursor.pos() + cursor.peek_len(); + changed = true; + } + cursor.step(); + }, + // ::lint_name + TokenKind::Colon + if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) + && cursor.get_text(captures[0]) == old_name => + { + dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + changed = true; + }, + _ => cursor.step(), + } + } + + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } +} diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 4a93860121fa..fdab4235a97c 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -30,7 +30,6 @@ pub mod fmt; pub mod lint; pub mod new_lint; pub mod release; -pub mod rename_lint; pub mod serve; pub mod setup; pub mod sync; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index e4e19a100c6e..8dc2290df8e4 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,8 +4,8 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve, setup, - sync, update_lints, + ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync, + update_lints, }; use std::env; @@ -75,7 +75,7 @@ fn main() { DevCommand::Serve { port, lint } => serve::run(port, lint), DevCommand::Lint { path, edition, args } => lint::run(&path, &edition, args.iter()), DevCommand::RenameLint { old_name, new_name } => new_parse_cx(|cx| { - rename_lint::rename(cx, clippy.version, &old_name, &new_name); + edit_lints::rename(cx, clippy.version, &old_name, &new_name); }), DevCommand::Uplift { old_name, new_name } => new_parse_cx(|cx| { edit_lints::uplift(cx, clippy.version, &old_name, new_name.as_deref().unwrap_or(&old_name)); diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs deleted file mode 100644 index 9be4f2bdc970..000000000000 --- a/clippy_dev/src/rename_lint.rs +++ /dev/null @@ -1,353 +0,0 @@ -use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{ParseCx, RenamedLint}; -use crate::update_lints::generate_lint_files; -use crate::utils::{ - ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, - expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, -}; -use rustc_lexer::TokenKind; -use std::ffi::OsString; -use std::fs; -use std::path::Path; - -/// Runs the `rename_lint` command. -/// -/// This does the following: -/// * Adds an entry to `renamed_lints.rs`. -/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). -/// * Renames the lint struct to the new name. -/// * Renames the module containing the lint struct to the new name if it shares a name with the -/// lint. -/// -/// # Panics -/// Panics for the following conditions: -/// * If a file path could not read from or then written to -/// * If either lint name has a prefix -/// * If `old_name` doesn't name an existing lint. -/// * If `old_name` names a deprecated or renamed lint. -pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str) { - let mut updater = FileUpdater::default(); - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); - - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { - panic!("could not find lint `{old_name}`"); - }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); - let new_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", new_name]); - cx.arena.alloc_str(buf) - }); - - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name_prefixed; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` already has a rename registered"); - return; - }, - Err(idx) => { - renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name: new_name_prefixed, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ); - }, - } - - // Some tests are named `lint_name_suffix` which should also be renamed, - // but we can't do that if the renamed lint's name overlaps with another - // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. - let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); - - let mut mod_edit = ModEdit::None; - if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { - let lint = &mut lints[lint_idx]; - if lint.module.ends_with(old_name) - && lint - .path - .file_stem() - .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) - { - let mut new_path = lint.path.with_file_name(new_name).into_os_string(); - new_path.push(".rs"); - if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { - mod_edit = ModEdit::Rename; - } - - lint.module = cx.str_buf.with(|buf| { - buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); - buf.push_str(new_name); - cx.arena.alloc_str(buf) - }); - } - rename_test_files(old_name, new_name, change_prefixed_tests); - lints[lint_idx].name = new_name; - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - } else { - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); - println!("Since `{new_name}` already exists the existing code has not been changed"); - return; - } - - let mut update_fn = file_update_fn(old_name, new_name, mod_edit); - for e in walk_dir_no_dot_or_target(".") { - let e = expect_action(e, ErrAction::Read, "."); - if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { - updater.update_file(e.path(), &mut update_fn); - } - } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); - println!("All code referencing the old name has been updated"); - println!("Make sure to inspect the results as some things may have been missed"); - println!("note: `cargo uibless` still needs to be run to update the test results"); -} - -#[derive(Clone, Copy)] -enum ModEdit { - None, - Delete, - Rename, -} - -fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { - for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { - let e = e.expect("error reading `tests/ui`"); - let name = e.file_name(); - if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { - if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { - dst.push((name, true)); - } - } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) - { - dst.push((name, false)); - } - } -} - -fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { - if rename_prefixed { - for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { - let e = e.expect("error reading `tests/ui-toml`"); - let name = e.file_name(); - if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { - dst.push((name, false)); - } - } - } else { - dst.push((lint.into(), false)); - } -} - -/// Renames all test files for the given lint. -/// -/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. -fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { - let mut tests = Vec::new(); - - let mut old_buf = OsString::from("tests/ui/"); - let mut new_buf = OsString::from("tests/ui/"); - collect_ui_test_names(old_name, rename_prefixed, &mut tests); - for &(ref name, is_file) in &tests { - old_buf.push(name); - new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); - if is_file { - try_rename_file(old_buf.as_ref(), new_buf.as_ref()); - } else { - try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); - } - old_buf.truncate("tests/ui/".len()); - new_buf.truncate("tests/ui/".len()); - } - - tests.clear(); - old_buf.truncate("tests/ui".len()); - new_buf.truncate("tests/ui".len()); - old_buf.push("-toml/"); - new_buf.push("-toml/"); - collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); - for (name, _) in &tests { - old_buf.push(name); - new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); - try_rename_dir(old_buf.as_ref(), new_buf.as_ref()); - old_buf.truncate("tests/ui/".len()); - new_buf.truncate("tests/ui/".len()); - } -} - -fn delete_test_files(lint: &str, rename_prefixed: bool) { - let mut tests = Vec::new(); - - let mut buf = OsString::from("tests/ui/"); - collect_ui_test_names(lint, rename_prefixed, &mut tests); - for &(ref name, is_file) in &tests { - buf.push(name); - if is_file { - delete_file_if_exists(buf.as_ref()); - } else { - delete_dir_if_exists(buf.as_ref()); - } - buf.truncate("tests/ui/".len()); - } - - buf.truncate("tests/ui".len()); - buf.push("-toml/"); - - tests.clear(); - collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); - for (name, _) in &tests { - buf.push(name); - delete_dir_if_exists(buf.as_ref()); - buf.truncate("tests/ui/".len()); - } -} - -fn snake_to_pascal(s: &str) -> String { - let mut dst = Vec::with_capacity(s.len()); - let mut iter = s.bytes(); - || -> Option<()> { - dst.push(iter.next()?.to_ascii_uppercase()); - while let Some(c) = iter.next() { - if c == b'_' { - dst.push(iter.next()?.to_ascii_uppercase()); - } else { - dst.push(c); - } - } - Some(()) - }(); - String::from_utf8(dst).unwrap() -} - -#[expect(clippy::too_many_lines)] -fn file_update_fn<'a, 'b>( - old_name: &'a str, - new_name: &'b str, - mod_edit: ModEdit, -) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { - let old_name_pascal = snake_to_pascal(old_name); - let new_name_pascal = snake_to_pascal(new_name); - let old_name_upper = old_name.to_ascii_uppercase(); - let new_name_upper = new_name.to_ascii_uppercase(); - move |_, src, dst| { - let mut copy_pos = 0u32; - let mut changed = false; - let mut cursor = Cursor::new(src); - let mut captures = [Capture::EMPTY]; - loop { - match cursor.peek() { - TokenKind::Eof => break, - TokenKind::Ident => { - let match_start = cursor.pos(); - let text = cursor.peek_text(); - cursor.step(); - match text { - // clippy::line_name or clippy::lint-name - "clippy" => { - if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) - && cursor.get_text(captures[0]) == old_name - { - dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); - dst.push_str(new_name); - copy_pos = cursor.pos(); - changed = true; - } - }, - // mod lint_name - "mod" => { - if !matches!(mod_edit, ModEdit::None) - && let Some(pos) = cursor.find_ident(old_name) - { - match mod_edit { - ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..pos as usize]); - dst.push_str(new_name); - copy_pos = cursor.pos(); - changed = true; - }, - ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => { - let mut start = &src[copy_pos as usize..match_start as usize]; - if start.ends_with("\n\n") { - start = &start[..start.len() - 1]; - } - dst.push_str(start); - copy_pos = cursor.pos(); - if src[copy_pos as usize..].starts_with("\n\n") { - copy_pos += 1; - } - changed = true; - }, - ModEdit::Delete | ModEdit::None => {}, - } - } - }, - // lint_name:: - name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { - let name_end = cursor.pos(); - if cursor.match_pat(cursor::Pat::DoubleColon) { - dst.push_str(&src[copy_pos as usize..match_start as usize]); - dst.push_str(new_name); - copy_pos = name_end; - changed = true; - } - }, - // LINT_NAME or LintName - name => { - let replacement = if name == old_name_upper { - &new_name_upper - } else if name == old_name_pascal { - &new_name_pascal - } else { - continue; - }; - dst.push_str(&src[copy_pos as usize..match_start as usize]); - dst.push_str(replacement); - copy_pos = cursor.pos(); - changed = true; - }, - } - }, - // //~ lint_name - TokenKind::LineComment { doc_style: None } => { - let text = cursor.peek_text(); - if text.starts_with("//~") - && let Some(text) = text.strip_suffix(old_name) - && !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')) - { - dst.push_str(&src[copy_pos as usize..cursor.pos() as usize + text.len()]); - dst.push_str(new_name); - copy_pos = cursor.pos() + cursor.peek_len(); - changed = true; - } - cursor.step(); - }, - // ::lint_name - TokenKind::Colon - if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures) - && cursor.get_text(captures[0]) == old_name => - { - dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]); - dst.push_str(new_name); - copy_pos = cursor.pos(); - changed = true; - }, - _ => cursor.step(), - } - } - - dst.push_str(&src[copy_pos as usize..]); - UpdateStatus::from_changed(changed) - } -} From 0eb8a65f6f88cc37cc8d1c6b17ce9f6c83f1f668 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sun, 12 Oct 2025 22:42:33 -0400 Subject: [PATCH 04/10] `clippy_dev`: When renaming a lint better handle the case where the renamed lint is a prefix of another lint name. --- clippy_dev/src/edit_lints.rs | 73 ++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 8a5ba26fd215..14dd8b21df9a 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -173,11 +173,6 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam }, } - // Some tests are named `lint_name_suffix` which should also be renamed, - // but we can't do that if the renamed lint's name overlaps with another - // lint. e.g. renaming 'foo' to 'bar' when a lint 'foo_bar' also exists. - let change_prefixed_tests = lints.get(lint_idx + 1).is_none_or(|l| !l.name.starts_with(old_name)); - let mut mod_edit = ModEdit::None; if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { let lint = &mut lints[lint_idx]; @@ -199,7 +194,16 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam cx.arena.alloc_str(buf) }); } - rename_test_files(old_name, new_name, change_prefixed_tests); + + rename_test_files( + old_name, + new_name, + &lints[lint_idx + 1..] + .iter() + .map(|l| l.name) + .take_while(|&n| n.starts_with(old_name)) + .collect::>(), + ); lints[lint_idx].name = new_name; lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); } else { @@ -329,44 +333,45 @@ enum ModEdit { Rename, } -fn collect_ui_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { +fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { let e = e.expect("error reading `tests/ui`"); let name = e.file_name(); - if let Some((name_only, _)) = name.as_encoded_bytes().split_once(|&x| x == b'.') { - if name_only.starts_with(lint.as_bytes()) && (rename_prefixed || name_only.len() == lint.len()) { - dst.push((name, true)); - } - } else if name.as_encoded_bytes().starts_with(lint.as_bytes()) && (rename_prefixed || name.len() == lint.len()) + if name.as_encoded_bytes().starts_with(lint.as_bytes()) + && !ignored_prefixes + .iter() + .any(|&pre| name.as_encoded_bytes().starts_with(pre.as_bytes())) + && let Ok(ty) = e.file_type() + && (ty.is_file() || ty.is_dir()) { - dst.push((name, false)); + dst.push((name, ty.is_file())); } } } -fn collect_ui_toml_test_names(lint: &str, rename_prefixed: bool, dst: &mut Vec<(OsString, bool)>) { - if rename_prefixed { - for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { - let e = e.expect("error reading `tests/ui-toml`"); - let name = e.file_name(); - if name.as_encoded_bytes().starts_with(lint.as_bytes()) && e.file_type().is_ok_and(|ty| ty.is_dir()) { - dst.push((name, false)); - } +fn collect_ui_toml_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { + for e in fs::read_dir("tests/ui-toml").expect("error reading `tests/ui-toml`") { + let e = e.expect("error reading `tests/ui-toml`"); + let name = e.file_name(); + if name.as_encoded_bytes().starts_with(lint.as_bytes()) + && !ignored_prefixes + .iter() + .any(|&pre| name.as_encoded_bytes().starts_with(pre.as_bytes())) + && e.file_type().is_ok_and(|ty| ty.is_dir()) + { + dst.push((name, false)); } - } else { - dst.push((lint.into(), false)); } } -/// Renames all test files for the given lint. -/// -/// If `rename_prefixed` is `true` this will also rename tests which have the lint name as a prefix. -fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { - let mut tests = Vec::new(); +/// Renames all test files for the given lint where the file name does not start with any +/// of the given prefixes. +fn rename_test_files(old_name: &str, new_name: &str, ignored_prefixes: &[&str]) { + let mut tests: Vec<(OsString, bool)> = Vec::new(); let mut old_buf = OsString::from("tests/ui/"); let mut new_buf = OsString::from("tests/ui/"); - collect_ui_test_names(old_name, rename_prefixed, &mut tests); + collect_ui_test_names(old_name, ignored_prefixes, &mut tests); for &(ref name, is_file) in &tests { old_buf.push(name); new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); @@ -384,7 +389,7 @@ fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { new_buf.truncate("tests/ui".len()); old_buf.push("-toml/"); new_buf.push("-toml/"); - collect_ui_toml_test_names(old_name, rename_prefixed, &mut tests); + collect_ui_toml_test_names(old_name, ignored_prefixes, &mut tests); for (name, _) in &tests { old_buf.push(name); new_buf.extend([new_name.as_ref(), name.slice_encoded_bytes(old_name.len()..)]); @@ -394,11 +399,13 @@ fn rename_test_files(old_name: &str, new_name: &str, rename_prefixed: bool) { } } -fn delete_test_files(lint: &str, rename_prefixed: bool) { +/// Deletes all test files for the given lint where the file name does not start with any +/// of the given prefixes. +fn delete_test_files(lint: &str, ignored_prefixes: &[&str]) { let mut tests = Vec::new(); let mut buf = OsString::from("tests/ui/"); - collect_ui_test_names(lint, rename_prefixed, &mut tests); + collect_ui_test_names(lint, ignored_prefixes, &mut tests); for &(ref name, is_file) in &tests { buf.push(name); if is_file { @@ -413,7 +420,7 @@ fn delete_test_files(lint: &str, rename_prefixed: bool) { buf.push("-toml/"); tests.clear(); - collect_ui_toml_test_names(lint, rename_prefixed, &mut tests); + collect_ui_toml_test_names(lint, ignored_prefixes, &mut tests); for (name, _) in &tests { buf.push(name); delete_dir_if_exists(buf.as_ref()); From 18f4ce438a248287513b582778810fe744dd0efd Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 13 Oct 2025 17:31:51 -0400 Subject: [PATCH 05/10] `clippy_dev`: Re-enable deleting the module declaration on deprecation and uplifting. --- clippy_dev/src/edit_lints.rs | 273 ++++++++++++--------------------- clippy_dev/src/parse/cursor.rs | 16 ++ 2 files changed, 117 insertions(+), 172 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 14dd8b21df9a..fb1c1458c50c 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -6,9 +6,9 @@ use crate::utils::{ expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; use rustc_lexer::TokenKind; -use std::ffi::{OsStr, OsString}; -use std::path::{Path, PathBuf}; -use std::{fs, io}; +use std::ffi::OsString; +use std::fs; +use std::path::Path; /// Runs the `deprecate` command /// @@ -23,7 +23,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name let mut lints = cx.find_lint_decls(); let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); - let Some(lint) = lints.iter().find(|l| l.name == name) else { + let Some(lint_idx) = lints.iter().position(|l| l.name == name) else { eprintln!("error: failed to find lint `{name}`"); return; }; @@ -47,30 +47,17 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name ), } - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); - } - - mod_path.set_extension("rs"); - mod_path - }; - - if remove_lint_declaration(name, &mod_path, &mut lints).unwrap_or(false) { - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{name}` has successfully been deprecated"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); - } + remove_lint_declaration(lint_idx, &mut lints, &mut FileUpdater::default()); + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{name}` has successfully been deprecated"); + println!("note: you must run `cargo uitest` to update the test results"); } pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut lints = cx.find_lint_decls(); let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); - let Some(lint) = lints.iter().find(|l| l.name == old_name) else { + let Some(lint_idx) = lints.iter().position(|l| l.name == old_name) else { eprintln!("error: failed to find lint `{old_name}`"); return; }; @@ -99,23 +86,18 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam ), } - let mod_path = { - let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module)); - if mod_path.is_dir() { - mod_path = mod_path.join("mod"); + let mut updater = FileUpdater::default(); + let remove_mod = remove_lint_declaration(lint_idx, &mut lints, &mut updater); + let mut update_fn = uplift_update_fn(old_name, new_name, remove_mod); + for e in walk_dir_no_dot_or_target(".") { + let e = expect_action(e, ErrAction::Read, "."); + if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(e.path(), &mut update_fn); } - - mod_path.set_extension("rs"); - mod_path - }; - - if remove_lint_declaration(old_name, &mod_path, &mut lints).unwrap_or(false) { - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); - println!("info: `{old_name}` has successfully been uplifted"); - println!("note: you must run `cargo uitest` to update the test results"); - } else { - eprintln!("error: lint not found"); } + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("info: `{old_name}` has successfully been uplifted as `{new_name}`"); + println!("note: you must run `cargo uitest` to update the test results"); } /// Runs the `rename_lint` command. @@ -173,7 +155,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam }, } - let mut mod_edit = ModEdit::None; + let mut rename_mod = false; if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { let lint = &mut lints[lint_idx]; if lint.module.ends_with(old_name) @@ -185,7 +167,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let mut new_path = lint.path.with_file_name(new_name).into_os_string(); new_path.push(".rs"); if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { - mod_edit = ModEdit::Rename; + rename_mod = true; } lint.module = cx.str_buf.with(|buf| { @@ -212,7 +194,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam return; } - let mut update_fn = file_update_fn(old_name, new_name, mod_edit); + let mut update_fn = rename_update_fn(old_name, new_name, rename_mod); for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { @@ -227,110 +209,38 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam println!("note: `cargo uibless` still needs to be run to update the test results"); } -fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec>) -> io::Result { - fn remove_lint(name: &str, lints: &mut Vec>) { - lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos)); - } - - fn remove_test_assets(name: &str) { - let test_file_stem = format!("tests/ui/{name}"); - let path = Path::new(&test_file_stem); - - // Some lints have their own directories, delete them - if path.is_dir() { - let _ = fs::remove_dir_all(path); - return; - } - - // Remove all related test files - let _ = fs::remove_file(path.with_extension("rs")); - let _ = fs::remove_file(path.with_extension("stderr")); - let _ = fs::remove_file(path.with_extension("fixed")); - } - - fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) { - let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| { - content - .find("declare_lint_pass!") - .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`")) - }); - let mut impl_lint_pass_end = content[impl_lint_pass_start..] - .find(']') - .expect("failed to find `impl_lint_pass` terminator"); - - impl_lint_pass_end += impl_lint_pass_start; - if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) { - let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len()); - for c in content[lint_name_end..impl_lint_pass_end].chars() { - // Remove trailing whitespace - if c == ',' || c.is_whitespace() { - lint_name_end += 1; - } else { - break; - } +/// Removes a lint's declaration and test files. Returns whether the module containing the +/// lint was deleted. +fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: &mut FileUpdater) -> bool { + let lint = lints.remove(lint_idx); + let delete_mod = if lints.iter().all(|l| l.module != lint.module) { + delete_file_if_exists(lint.path.as_ref()) + } else { + updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { + let mut start = &src[..lint.declaration_range.start]; + if start.ends_with("\n\n") { + start = &start[..start.len() - 1]; } - - content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, ""); - } - } - - if path.exists() - && let Some(lint) = lints.iter().find(|l| l.name == name) - { - if lint.module == name { - // The lint name is the same as the file, we can just delete the entire file - fs::remove_file(path)?; - } else { - // We can't delete the entire file, just remove the declaration - - if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) { - // Remove clippy_lints/src/some_mod/some_lint.rs - let mut lint_mod_path = path.to_path_buf(); - lint_mod_path.set_file_name(name); - lint_mod_path.set_extension("rs"); - - let _ = fs::remove_file(lint_mod_path); + let mut end = &src[lint.declaration_range.end..]; + if end.starts_with("\n\n") { + end = &end[1..]; } - - let mut content = - fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy())); - - eprintln!( - "warn: you will have to manually remove any code related to `{name}` from `{}`", - path.display() - ); - - assert!( - content[lint.declaration_range].contains(&name.to_uppercase()), - "error: `{}` does not contain lint `{}`'s declaration", - path.display(), - lint.name - ); - - // Remove lint declaration (declare_clippy_lint!) - content.replace_range(lint.declaration_range, ""); - - // Remove the module declaration (mod xyz;) - let mod_decl = format!("\nmod {name};"); - content = content.replacen(&mod_decl, "", 1); - - remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content); - fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy())); - } - - remove_test_assets(name); - remove_lint(name, lints); - return Ok(true); - } - - Ok(false) -} - -#[derive(Clone, Copy)] -enum ModEdit { - None, - Delete, - Rename, + dst.push_str(start); + dst.push_str(end); + UpdateStatus::Changed + }); + false + }; + delete_test_files( + lint.name, + &lints[lint_idx..] + .iter() + .map(|l| l.name) + .take_while(|&n| n.starts_with(lint.name)) + .collect::>(), + ); + + delete_mod } fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { @@ -445,12 +355,50 @@ fn snake_to_pascal(s: &str) -> String { String::from_utf8(dst).unwrap() } -#[expect(clippy::too_many_lines)] -fn file_update_fn<'a, 'b>( +/// Creates an update function which replaces all instances of `clippy::old_name` with +/// `new_name`. +fn uplift_update_fn<'a>( + old_name: &'a str, + new_name: &'a str, + remove_mod: bool, +) -> impl use<'a> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + move |_, src, dst| { + let mut copy_pos = 0u32; + let mut changed = false; + let mut cursor = Cursor::new(src); + while let Some(ident) = cursor.find_any_ident() { + match cursor.get_text(ident) { + "mod" + if remove_mod && cursor.match_all(&[cursor::Pat::Ident(old_name), cursor::Pat::Semi], &mut []) => + { + dst.push_str(&src[copy_pos as usize..ident.pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + if src[copy_pos as usize..].starts_with('\n') { + copy_pos += 1; + } + changed = true; + }, + "clippy" if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::Ident(old_name)], &mut []) => { + dst.push_str(&src[copy_pos as usize..ident.pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + changed = true; + }, + + _ => {}, + } + } + dst.push_str(&src[copy_pos as usize..]); + UpdateStatus::from_changed(changed) + } +} + +fn rename_update_fn<'a>( old_name: &'a str, - new_name: &'b str, - mod_edit: ModEdit, -) -> impl use<'a, 'b> + FnMut(&Path, &str, &mut String) -> UpdateStatus { + new_name: &'a str, + rename_mod: bool, +) -> impl use<'a> + FnMut(&Path, &str, &mut String) -> UpdateStatus { let old_name_pascal = snake_to_pascal(old_name); let new_name_pascal = snake_to_pascal(new_name); let old_name_upper = old_name.to_ascii_uppercase(); @@ -481,34 +429,15 @@ fn file_update_fn<'a, 'b>( }, // mod lint_name "mod" => { - if !matches!(mod_edit, ModEdit::None) - && let Some(pos) = cursor.find_ident(old_name) - { - match mod_edit { - ModEdit::Rename => { - dst.push_str(&src[copy_pos as usize..pos as usize]); - dst.push_str(new_name); - copy_pos = cursor.pos(); - changed = true; - }, - ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => { - let mut start = &src[copy_pos as usize..match_start as usize]; - if start.ends_with("\n\n") { - start = &start[..start.len() - 1]; - } - dst.push_str(start); - copy_pos = cursor.pos(); - if src[copy_pos as usize..].starts_with("\n\n") { - copy_pos += 1; - } - changed = true; - }, - ModEdit::Delete | ModEdit::None => {}, - } + if rename_mod && let Some(pos) = cursor.match_ident(old_name) { + dst.push_str(&src[copy_pos as usize..pos as usize]); + dst.push_str(new_name); + copy_pos = cursor.pos(); + changed = true; } }, // lint_name:: - name if matches!(mod_edit, ModEdit::Rename) && name == old_name => { + name if rename_mod && name == old_name => { let name_end = cursor.pos(); if cursor.match_pat(cursor::Pat::DoubleColon) { dst.push_str(&src[copy_pos as usize..match_start as usize]); diff --git a/clippy_dev/src/parse/cursor.rs b/clippy_dev/src/parse/cursor.rs index 6dc003f326de..2c142af4883a 100644 --- a/clippy_dev/src/parse/cursor.rs +++ b/clippy_dev/src/parse/cursor.rs @@ -219,6 +219,22 @@ impl<'txt> Cursor<'txt> { } } + /// Consume the returns the position of the next non-whitespace token if it's an + /// identifier. Returns `None` otherwise. + pub fn match_ident(&mut self, s: &str) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident if s == self.peek_text() => { + let pos = self.pos; + self.step(); + return Some(pos); + }, + TokenKind::Whitespace => self.step(), + _ => return None, + } + } + } + /// Continually attempt to match the pattern on subsequent tokens until a match is /// found. Returns whether the pattern was successfully matched. /// From f7f3328b7ab8ea06292caa25e790c87faf7b0faa Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 16 Oct 2025 01:31:12 -0400 Subject: [PATCH 06/10] `clippy_dev`: Stores parsed lints in a map indexed by name. --- clippy_dev/src/edit_lints.rs | 221 ++++++++++++++--------------- clippy_dev/src/lib.rs | 1 + clippy_dev/src/parse.rs | 106 +++++++++----- clippy_dev/src/update_lints.rs | 134 +++++++++-------- clippy_lints/src/declared_lints.rs | 30 ++-- 5 files changed, 262 insertions(+), 230 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index fb1c1458c50c..35459a1bbd70 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,11 +1,13 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; +use core::mem; use rustc_lexer::TokenKind; +use std::collections::hash_map::Entry; use std::ffi::OsString; use std::fs; use std::path::Path; @@ -20,74 +22,58 @@ use std::path::Path; /// /// If a file path could not read from or written to pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) { - let mut lints = cx.find_lint_decls(); - let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Some(lint_idx) = lints.iter().position(|l| l.name == name) else { + let Entry::Occupied(mut lint) = data.lints.entry(name) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Deprecated(DeprecatedLint { + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{name}` is already deprecated"); + return; + }; - let prefixed_name = cx.str_buf.with(|buf| { - buf.extend(["clippy::", name]); - cx.arena.alloc_str(buf) - }); - match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { - Ok(_) => { - println!("`{name}` is already deprecated"); - return; - }, - Err(idx) => deprecated_lints.insert( - idx, - DeprecatedLint { - name: prefixed_name, - reason, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } - - remove_lint_declaration(lint_idx, &mut lints, &mut FileUpdater::default()); - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default()); + generate_lint_files(UpdateMode::Change, &data); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); } pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); + + update_rename_targets( + &mut data, + cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }), + new_name, + ); - let Some(lint_idx) = lints.iter().position(|l| l.name == old_name) else { + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); return; }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` is already deprecated"); - return; - }, - Err(idx) => renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; + }; let mut updater = FileUpdater::default(); - let remove_mod = remove_lint_declaration(lint_idx, &mut lints, &mut updater); + let remove_mod = remove_lint_declaration(old_name, &prev_lint, &data, &mut updater); let mut update_fn = uplift_update_fn(old_name, new_name, remove_mod); for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); @@ -95,7 +81,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + generate_lint_files(UpdateMode::Change, &data); println!("info: `{old_name}` has successfully been uplifted as `{new_name}`"); println!("note: you must run `cargo uitest` to update the test results"); } @@ -117,77 +103,59 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// * If `old_name` names a deprecated or renamed lint. pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut updater = FileUpdater::default(); - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { - panic!("could not find lint `{old_name}`"); - }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); let new_name_prefixed = cx.str_buf.with(|buf| { buf.extend(["clippy::", new_name]); cx.arena.alloc_str(buf) }); + update_rename_targets( + &mut data, + cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }), + new_name_prefixed, + ); - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name_prefixed; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` already has a rename registered"); - return; - }, - Err(idx) => { - renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name: new_name_prefixed, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ); - }, - } + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { + eprintln!("error: failed to find lint `{old_name}`"); + return; + }; + let Lint::Active(mut prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name: new_name_prefixed, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; + }; let mut rename_mod = false; - if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { - let lint = &mut lints[lint_idx]; - if lint.module.ends_with(old_name) - && lint + if let Entry::Vacant(e) = data.lints.entry(new_name) { + if prev_lint.module.ends_with(old_name) + && prev_lint .path .file_stem() .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) { - let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + let mut new_path = prev_lint.path.with_file_name(new_name).into_os_string(); new_path.push(".rs"); - if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + if try_rename_file(prev_lint.path.as_ref(), new_path.as_ref()) { rename_mod = true; } - lint.module = cx.str_buf.with(|buf| { - buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + prev_lint.module = cx.str_buf.with(|buf| { + buf.push_str(&prev_lint.module[..prev_lint.module.len() - old_name.len()]); buf.push_str(new_name); cx.arena.alloc_str(buf) }); } + e.insert(Lint::Active(prev_lint)); - rename_test_files( - old_name, - new_name, - &lints[lint_idx + 1..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(old_name)) - .collect::>(), - ); - lints[lint_idx].name = new_name; - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + rename_test_files(old_name, new_name, &create_ignored_prefixes(old_name, &data)); } else { println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); @@ -201,7 +169,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + generate_lint_files(UpdateMode::Change, &data); println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("All code referencing the old name has been updated"); @@ -211,9 +179,14 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// Removes a lint's declaration and test files. Returns whether the module containing the /// lint was deleted. -fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: &mut FileUpdater) -> bool { - let lint = lints.remove(lint_idx); - let delete_mod = if lints.iter().all(|l| l.module != lint.module) { +fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_>, updater: &mut FileUpdater) -> bool { + let delete_mod = if data.lints.iter().all(|(_, l)| { + if let Lint::Active(l) = l { + l.module != lint.module + } else { + true + } + }) { delete_file_if_exists(lint.path.as_ref()) } else { updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { @@ -231,18 +204,34 @@ fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: }); false }; - delete_test_files( - lint.name, - &lints[lint_idx..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(lint.name)) - .collect::>(), - ); + delete_test_files(name, &create_ignored_prefixes(name, data)); delete_mod } +/// Updates all renames to the old name to be renames to the new name. +/// +/// This is needed because rustc doesn't allow a lint to be renamed to a lint that has +/// also been renamed. +fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: &'cx str) { + for lint in data.lints.values_mut() { + if let Lint::Renamed(lint) = lint + && lint.new_name == old_name + { + lint.new_name = new_name; + } + } +} + +/// Creates a list of prefixes to ignore when +fn create_ignored_prefixes<'cx>(name: &str, data: &LintData<'cx>) -> Vec<&'cx str> { + data.lints + .keys() + .copied() + .filter(|&x| x.len() > name.len() && x.starts_with(name)) + .collect() +} + fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { let e = e.expect("error reading `tests/ui`"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index fdab4235a97c..acd0c2f382e8 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -19,6 +19,7 @@ #![allow(clippy::missing_panics_doc)] extern crate rustc_arena; +extern crate rustc_data_structures; #[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index de5caf4e1ef6..566eea336f0d 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -5,6 +5,7 @@ use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_ta use core::fmt::{Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; +use rustc_data_structures::fx::FxHashMap; use std::fs; use std::path::{self, Path, PathBuf}; use std::str::pattern::Pattern; @@ -81,8 +82,7 @@ impl StrBuf { } } -pub struct Lint<'cx> { - pub name: &'cx str, +pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, pub path: PathBuf, @@ -90,22 +90,34 @@ pub struct Lint<'cx> { } pub struct DeprecatedLint<'cx> { - pub name: &'cx str, pub reason: &'cx str, pub version: &'cx str, } pub struct RenamedLint<'cx> { - pub old_name: &'cx str, pub new_name: &'cx str, pub version: &'cx str, } +pub enum Lint<'cx> { + Active(ActiveLint<'cx>), + Deprecated(DeprecatedLint<'cx>), + Renamed(RenamedLint<'cx>), +} + +pub struct LintData<'cx> { + pub lints: FxHashMap<&'cx str, Lint<'cx>>, +} + impl<'cx> ParseCxImpl<'cx> { - /// Finds all lint declarations (`declare_clippy_lint!`) + /// Finds and parses all lint declarations. #[must_use] - pub fn find_lint_decls(&mut self) -> Vec> { - let mut lints = Vec::with_capacity(1000); + pub fn parse_lint_decls(&mut self) -> LintData<'cx> { + let mut data = LintData { + #[expect(clippy::default_trait_access)] + lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()), + }; + let mut contents = String::new(); for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { let e = expect_action(e, ErrAction::Read, "."); @@ -143,17 +155,18 @@ impl<'cx> ParseCxImpl<'cx> { e.path(), File::open_read_to_cleared_string(e.path(), &mut contents), module, - &mut lints, + &mut data, ); } } } - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - lints + + self.read_deprecated_lints(&mut data); + data } /// Parse a source file looking for `declare_clippy_lint` macro invocations. - fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec>) { + fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -170,19 +183,24 @@ impl<'cx> ParseCxImpl<'cx> { let mut captures = [Capture::EMPTY; 2]; while let Some(start) = cursor.find_ident("declare_clippy_lint") { if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { - lints.push(Lint { - name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), - group: self.arena.alloc_str(cursor.get_text(captures[1])), - module, - path: path.into(), - declaration_range: start as usize..cursor.pos() as usize, - }); + assert!( + data.lints + .insert( + self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + Lint::Active(ActiveLint { + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: start as usize..cursor.pos() as usize, + }), + ) + .is_none() + ); } } } - #[must_use] - pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec>) { + fn read_deprecated_lints(&mut self, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -204,8 +222,6 @@ impl<'cx> ParseCxImpl<'cx> { ]; let path = "clippy_lints/src/deprecated_lints.rs"; - let mut deprecated = Vec::with_capacity(30); - let mut renamed = Vec::with_capacity(80); let mut contents = String::new(); File::open_read_to_cleared_string(path, &mut contents); @@ -220,11 +236,17 @@ impl<'cx> ParseCxImpl<'cx> { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { - deprecated.push(DeprecatedLint { - name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); + assert!( + data.lints + .insert( + self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), + Lint::Deprecated(DeprecatedLint { + reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }), + ) + .is_none() + ); } } else { panic!("error reading deprecated lints"); @@ -232,19 +254,21 @@ impl<'cx> ParseCxImpl<'cx> { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { - renamed.push(RenamedLint { - old_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); + assert!( + data.lints + .insert( + self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), + Lint::Renamed(RenamedLint { + new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }), + ) + .is_none() + ); } } else { panic!("error reading renamed lints"); } - - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(rhs.old_name)); - (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal @@ -282,4 +306,14 @@ impl<'cx> ParseCxImpl<'cx> { ); value } + + fn parse_clippy_lint_name(&mut self, path: &Path, s: &str) -> &'cx str { + match self.parse_str_single_line(path, s).strip_prefix("clippy::") { + Some(x) => x, + None => panic!( + "error parsing `{}`: `{s}` should be a string starting with `clippy::`", + path.display() + ), + } + } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 3d0da6846114..ccc7a1d95355 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::Cursor; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::parse::{Lint, LintData, ParseCx}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; @@ -22,55 +22,62 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { - let lints = cx.find_lint_decls(); - let (deprecated, renamed) = cx.read_deprecated_lints(); - generate_lint_files(update_mode, &lints, &deprecated, &renamed); + let data = cx.parse_lint_decls(); + generate_lint_files(update_mode, &data); } #[expect(clippy::too_many_lines)] -pub fn generate_lint_files( - update_mode: UpdateMode, - lints: &[Lint<'_>], - deprecated: &[DeprecatedLint<'_>], - renamed: &[RenamedLint<'_>], -) { +pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) { let mut updater = FileUpdater::default(); + + let mut lints: Vec<_> = data.lints.iter().map(|(&x, y)| (x, y)).collect(); + lints.sort_by_key(|&(x, _)| x); updater.update_file_checked( "cargo dev update_lints", update_mode, - "README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for &(lint, _) in &lints { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), ); + + let mut active = Vec::with_capacity(lints.len()); + let mut deprecated = Vec::with_capacity(lints.len() / 8); + let mut renamed = Vec::with_capacity(lints.len() / 8); + for &(name, lint) in &lints { + match lint { + Lint::Active(lint) => active.push((name, lint)), + Lint::Deprecated(lint) => deprecated.push((name, lint)), + Lint::Renamed(lint) => renamed.push((name, lint)), + } + } + active.sort_by_key(|&(_, lint)| lint.module); + + // Round to avoid updating the readme every time a lint is added/deprecated. + let lint_count = active.len() / 50 * 50; updater.update_file_checked( "cargo dev update_lints", update_mode, - "book/src/README.md", + "README.md", &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + write!(dst, "{lint_count}").unwrap(); }), ); updater.update_file_checked( "cargo dev update_lints", update_mode, - "CHANGELOG.md", - &mut update_text_region_fn( - "\n", - "", - |dst| { - for lint in lints - .iter() - .map(|l| l.name) - .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ), + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), ); + updater.update_file_checked( "cargo dev update_lints", update_mode, @@ -84,11 +91,11 @@ pub fn generate_lint_files( ); dst.push_str(&src[..cursor.pos() as usize]); dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); - for lint in deprecated { + for &(name, data) in &deprecated { write!( dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.name, lint.reason, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.reason, ) .unwrap(); } @@ -98,11 +105,11 @@ pub fn generate_lint_files( declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ ", ); - for lint in renamed { + for &(name, data) in &renamed { write!( dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.old_name, lint.new_name, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.new_name, ) .unwrap(); } @@ -116,8 +123,8 @@ pub fn generate_lint_files( "tests/ui/deprecated.rs", &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); - for lint in deprecated { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + for &(lint, _) in &deprecated { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) @@ -131,25 +138,28 @@ pub fn generate_lint_files( let mut seen_lints = HashSet::new(); dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in renamed { + for &(_, lint) in &renamed { if seen_lints.insert(lint.new_name) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } seen_lints.clear(); - for lint in renamed { - if seen_lints.insert(lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + for &(lint, _) in &renamed { + if seen_lints.insert(lint) { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) }, ); - for (crate_name, lints) in lints.iter().into_group_map_by(|&l| { - let Some(path::Component::Normal(name)) = l.path.components().next() else { + for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| { + let Some(path::Component::Normal(name)) = lint.path.components().next() else { // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` - panic!("internal error: can't read crate name from path `{}`", l.path.display()); + panic!( + "internal error: can't read crate name from path `{}`", + lint.path.display() + ); }; name }) { @@ -161,14 +171,12 @@ pub fn generate_lint_files( "// 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`", |dst| { - for lint_mod in lints - .iter() - .filter(|l| !l.module.is_empty()) - .map(|l| l.module.split_once("::").map_or(l.module, |x| x.0)) - .sorted() - .dedup() - { - writeln!(dst, "mod {lint_mod};").unwrap(); + let mut prev = ""; + for &(_, lint) in &lints { + if lint.module != prev { + writeln!(dst, "mod {};", lint.module).unwrap(); + prev = lint.module; + } } }, ), @@ -180,11 +188,15 @@ pub fn generate_lint_files( &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); - for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { - if module_path.is_empty() { - writeln!(dst, " crate::{lint_name}_INFO,").unwrap(); + let mut buf = String::new(); + for &(name, lint) in &lints { + buf.clear(); + buf.push_str(name); + buf.make_ascii_uppercase(); + if lint.module.is_empty() { + writeln!(dst, " crate::{buf}_INFO,").unwrap(); } else { - writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap(); + writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap(); } } dst.push_str("];\n"); @@ -193,7 +205,3 @@ pub fn generate_lint_files( ); } } - -fn round_to_fifty(count: usize) -> usize { - count / 50 * 50 -} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index bd2971fa150d..f603866cf7b9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -99,9 +99,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::dereference::NEEDLESS_BORROW_INFO, crate::dereference::REF_BINDING_TO_REFERENCE_INFO, crate::derivable_impls::DERIVABLE_IMPLS_INFO, - crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, crate::disallowed_macros::DISALLOWED_MACROS_INFO, @@ -201,8 +201,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, crate::ifs::BRANCHES_SHARING_CODE_INFO, - crate::ifs::IFS_SAME_COND_INFO, crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::IFS_SAME_COND_INFO, crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, @@ -333,8 +333,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::matches::MATCH_SAME_ARMS_INFO, crate::matches::MATCH_SINGLE_BINDING_INFO, crate::matches::MATCH_STR_CASE_MISMATCH_INFO, - crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::NEEDLESS_MATCH_INFO, crate::matches::REDUNDANT_GUARDS_INFO, crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, @@ -356,9 +356,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, - crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, @@ -386,7 +386,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::IO_OTHER_ERROR_INFO, crate::methods::IP_CONSTANT_INFO, crate::methods::IS_DIGIT_ASCII_RADIX_INFO, - crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::ITER_CLONED_COLLECT_INFO, crate::methods::ITER_COUNT_INFO, crate::methods::ITER_FILTER_IS_OK_INFO, @@ -402,9 +401,10 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::ITER_SKIP_NEXT_INFO, crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, - crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO, + crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, crate::methods::MANUAL_INSPECT_INFO, @@ -433,8 +433,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::NEEDLESS_OPTION_TAKE_INFO, crate::methods::NEEDLESS_SPLITN_INFO, crate::methods::NEW_RET_NO_SELF_INFO, - crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::OBFUSCATED_IF_ELSE_INFO, crate::methods::OK_EXPECT_INFO, crate::methods::OPTION_AS_REF_CLONED_INFO, @@ -447,8 +447,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::PATH_ENDS_WITH_EXT_INFO, crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, - crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, @@ -463,9 +463,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, - crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, @@ -633,8 +633,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ranges::RANGE_MINUS_ONE_INFO, crate::ranges::RANGE_PLUS_ONE_INFO, crate::ranges::REVERSED_EMPTY_RANGES_INFO, - crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO, + crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO, @@ -686,12 +686,12 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, + crate::strings::STR_TO_STRING_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, crate::strings::STRING_LIT_AS_BYTES_INFO, crate::strings::STRING_SLICE_INFO, - crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, @@ -715,7 +715,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, crate::transmute::EAGER_TRANSMUTE_INFO, crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, - crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, @@ -723,6 +722,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTING_NULL_INFO, crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, crate::transmute::USELESS_TRANSMUTE_INFO, @@ -780,20 +780,20 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, crate::visibility::NEEDLESS_PUB_SELF_INFO, - crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, - crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::PRINT_LITERAL_INFO, crate::write::PRINT_STDERR_INFO, crate::write::PRINT_STDOUT_INFO, crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::USE_DEBUG_INFO, - crate::write::WRITELN_EMPTY_STRING_INFO, crate::write::WRITE_LITERAL_INFO, crate::write::WRITE_WITH_NEWLINE_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, From 19ec3f72f194b4e3f02a4cab070a69f3c22800fc Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 20 Oct 2025 15:58:23 -0400 Subject: [PATCH 07/10] `clippy_dev`: Use types to differentiate rustc and clippy lints. --- clippy_dev/src/edit_lints.rs | 35 ++++++--------------- clippy_dev/src/parse.rs | 57 ++++++++++++++++++++++++++++++++-- clippy_dev/src/update_lints.rs | 5 +-- 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 35459a1bbd70..411bf530b22b 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, ParseCx, RenamedLint}; +use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, @@ -48,14 +48,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut data = cx.parse_lint_decls(); - update_rename_targets( - &mut data, - cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }), - new_name, - ); + update_rename_targets(&mut data, old_name, LintName::new_rustc(new_name)); let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); @@ -64,7 +57,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let Lint::Active(prev_lint) = mem::replace( lint.get_mut(), Lint::Renamed(RenamedLint { - new_name, + new_name: LintName::new_rustc(new_name), version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }), ) else { @@ -105,18 +98,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let mut updater = FileUpdater::default(); let mut data = cx.parse_lint_decls(); - let new_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", new_name]); - cx.arena.alloc_str(buf) - }); - update_rename_targets( - &mut data, - cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }), - new_name_prefixed, - ); + update_rename_targets(&mut data, old_name, LintName::new_clippy(new_name)); let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); @@ -125,7 +107,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let Lint::Active(mut prev_lint) = mem::replace( lint.get_mut(), Lint::Renamed(RenamedLint { - new_name: new_name_prefixed, + new_name: LintName::new_clippy(new_name), version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }), ) else { @@ -157,7 +139,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam rename_test_files(old_name, new_name, &create_ignored_prefixes(old_name, &data)); } else { - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); return; } @@ -171,7 +153,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam } generate_lint_files(UpdateMode::Change, &data); - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("All code referencing the old name has been updated"); println!("Make sure to inspect the results as some things may have been missed"); println!("note: `cargo uibless` still needs to be run to update the test results"); @@ -213,7 +195,8 @@ fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_ /// /// This is needed because rustc doesn't allow a lint to be renamed to a lint that has /// also been renamed. -fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: &'cx str) { +fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: LintName<'cx>) { + let old_name = LintName::new_clippy(old_name); for lint in data.lints.values_mut() { if let Lint::Renamed(lint) = lint && lint.new_name == old_name diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 566eea336f0d..ffb50784a9a7 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -2,7 +2,7 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; -use core::fmt::{Display, Write as _}; +use core::fmt::{self, Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; use rustc_data_structures::fx::FxHashMap; @@ -82,6 +82,48 @@ impl StrBuf { } } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum LintTool { + Rustc, + Clippy, +} +impl LintTool { + /// Gets the namespace prefix to use when naming a lint including the `::`. + pub fn prefix(self) -> &'static str { + match self { + Self::Rustc => "", + Self::Clippy => "clippy::", + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct LintName<'cx> { + pub name: &'cx str, + pub tool: LintTool, +} +impl<'cx> LintName<'cx> { + pub fn new_rustc(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Rustc, + } + } + + pub fn new_clippy(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Clippy, + } + } +} +impl Display for LintName<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.tool.prefix())?; + f.write_str(self.name) + } +} + pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, @@ -95,7 +137,7 @@ pub struct DeprecatedLint<'cx> { } pub struct RenamedLint<'cx> { - pub new_name: &'cx str, + pub new_name: LintName<'cx>, pub version: &'cx str, } @@ -259,7 +301,7 @@ impl<'cx> ParseCxImpl<'cx> { .insert( self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), Lint::Renamed(RenamedLint { - new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + new_name: self.parse_lint_name(path.as_ref(), cursor.get_text(captures[2])), version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }), ) @@ -316,4 +358,13 @@ impl<'cx> ParseCxImpl<'cx> { ), } } + + fn parse_lint_name(&mut self, path: &Path, s: &str) -> LintName<'cx> { + let s = self.parse_str_single_line(path, s); + let (name, tool) = match s.strip_prefix("clippy::") { + Some(s) => (s, LintTool::Clippy), + None => (s, LintTool::Rustc), + }; + LintName { name, tool } + } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index ccc7a1d95355..a4cf15058986 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -143,11 +143,8 @@ pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } - seen_lints.clear(); for &(lint, _) in &renamed { - if seen_lints.insert(lint) { - writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); - } + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) From a8523a97ff43a93895f0f919d3e7ef0e26baeda0 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 14 Oct 2025 10:48:53 -0400 Subject: [PATCH 08/10] `clippy_dev`: Rename `update_lints` module to `generate` --- clippy_dev/src/edit_lints.rs | 2 +- clippy_dev/src/{update_lints.rs => generate.rs} | 0 clippy_dev/src/lib.rs | 2 +- clippy_dev/src/main.rs | 10 ++++++---- 4 files changed, 8 insertions(+), 6 deletions(-) rename clippy_dev/src/{update_lints.rs => generate.rs} (100%) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 411bf530b22b..3dd667165d58 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,6 +1,6 @@ +use crate::generate::generate_lint_files; use crate::parse::cursor::{self, Capture, Cursor}; use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint}; -use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/generate.rs similarity index 100% rename from clippy_dev/src/update_lints.rs rename to clippy_dev/src/generate.rs diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index acd0c2f382e8..97149cbb7346 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -28,13 +28,13 @@ extern crate rustc_literal_escaper; pub mod dogfood; pub mod edit_lints; pub mod fmt; +pub mod generate; pub mod lint; pub mod new_lint; pub mod release; pub mod serve; pub mod setup; pub mod sync; -pub mod update_lints; mod parse; mod utils; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 8dc2290df8e4..ba470d90014a 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,8 +4,8 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync, - update_lints, + ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, generate, lint, new_lint, new_parse_cx, release, serve, setup, + sync, }; use std::env; @@ -27,7 +27,9 @@ fn main() { allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), - DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))), + DevCommand::UpdateLints { check } => { + new_parse_cx(|cx| generate::generate_lint_files(UpdateMode::from_check(check), &cx.parse_lint_decls())); + }, DevCommand::NewLint { pass, name, @@ -35,7 +37,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)), + Ok(()) => new_parse_cx(|cx| generate::generate_lint_files(UpdateMode::Change, &cx.parse_lint_decls())), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { From 2d182db62891ad003fe6172d8d339d023d6ddd15 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Fri, 24 Oct 2025 21:24:35 -0400 Subject: [PATCH 09/10] `clippy_dev`: Parse and format lint pass macros. --- clippy_dev/src/edit_lints.rs | 11 +- clippy_dev/src/fmt.rs | 27 +- clippy_dev/src/generate.rs | 363 ++++++++++-------- clippy_dev/src/lib.rs | 2 +- clippy_dev/src/main.rs | 7 +- clippy_dev/src/parse.rs | 151 +++++++- clippy_dev/src/parse/cursor.rs | 63 ++- clippy_dev/src/utils.rs | 24 ++ clippy_lints/src/attrs/mod.rs | 15 +- clippy_lints/src/await_holding_invalid.rs | 6 +- clippy_lints/src/cargo/mod.rs | 6 +- clippy_lints/src/casts/mod.rs | 36 +- clippy_lints/src/collapsible_if.rs | 2 +- .../src/default_constructed_unit_structs.rs | 4 +- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/derive/mod.rs | 4 +- clippy_lints/src/doc/mod.rs | 20 +- clippy_lints/src/drop_forget_ref.rs | 6 +- clippy_lints/src/empty_line_after.rs | 2 +- clippy_lints/src/empty_with_brackets.rs | 5 +- clippy_lints/src/endian_bytes.rs | 6 +- clippy_lints/src/eta_reduction.rs | 5 +- clippy_lints/src/excessive_bools.rs | 5 +- .../src/field_scoped_visibility_modifiers.rs | 4 +- clippy_lints/src/float_literal.rs | 4 +- clippy_lints/src/floating_point_arithmetic.rs | 5 +- clippy_lints/src/format_args.rs | 2 +- clippy_lints/src/format_impl.rs | 2 +- clippy_lints/src/formatting.rs | 6 +- clippy_lints/src/functions/mod.rs | 18 +- clippy_lints/src/ifs/mod.rs | 4 +- .../impl_hash_with_borrow_str_and_bytes.rs | 4 +- clippy_lints/src/implicit_saturating_sub.rs | 5 +- .../src/inconsistent_struct_constructor.rs | 4 +- clippy_lints/src/inherent_to_string.rs | 5 +- .../src/integer_division_remainder_used.rs | 4 +- clippy_lints/src/item_name_repetitions.rs | 4 +- clippy_lints/src/iter_without_into_iter.rs | 5 +- clippy_lints/src/len_zero.rs | 2 +- clippy_lints/src/let_underscore.rs | 7 +- clippy_lints/src/lifetimes.rs | 2 +- clippy_lints/src/literal_representation.rs | 2 +- .../literal_string_with_formatting_args.rs | 4 +- clippy_lints/src/loops/mod.rs | 36 +- clippy_lints/src/manual_float_methods.rs | 2 +- clippy_lints/src/matches/mod.rs | 42 +- clippy_lints/src/mem_replace.rs | 8 +- clippy_lints/src/methods/mod.rs | 240 ++++++------ clippy_lints/src/misc.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 12 +- .../src/mixed_read_write_in_expression.rs | 5 +- clippy_lints/src/mut_key.rs | 2 +- .../src/needless_borrows_for_generic_args.rs | 4 +- .../src/needless_parens_on_range_literals.rs | 4 +- clippy_lints/src/no_effect.rs | 6 +- clippy_lints/src/non_canonical_impls.rs | 5 +- clippy_lints/src/non_copy_const.rs | 5 +- clippy_lints/src/non_expressive_names.rs | 6 +- clippy_lints/src/only_used_in_recursion.rs | 5 +- clippy_lints/src/operators/mod.rs | 28 +- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/panic_unimplemented.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 5 +- .../src/permissions_set_readonly_false.rs | 4 +- clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/question_mark.rs | 2 +- clippy_lints/src/ranges.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 2 +- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/returns/mod.rs | 6 +- clippy_lints/src/shadow.rs | 2 +- clippy_lints/src/std_instead_of_core.rs | 6 +- clippy_lints/src/string_patterns.rs | 5 +- clippy_lints/src/strings.rs | 5 +- .../src/suspicious_operation_groupings.rs | 4 +- clippy_lints/src/suspicious_trait_impl.rs | 5 +- clippy_lints/src/swap.rs | 2 +- clippy_lints/src/time_subtraction.rs | 5 +- clippy_lints/src/trait_bounds.rs | 5 +- clippy_lints/src/transmute/mod.rs | 18 +- clippy_lints/src/types/mod.rs | 10 +- .../src/undocumented_unsafe_blocks.rs | 5 +- clippy_lints/src/unicode.rs | 6 +- clippy_lints/src/unit_types/mod.rs | 2 +- .../src/unnecessary_map_on_constructor.rs | 4 +- .../src/unnecessary_owned_empty_strings.rs | 4 +- clippy_lints/src/unwrap_in_result.rs | 2 +- clippy_lints/src/visibility.rs | 6 +- clippy_lints/src/write.rs | 8 +- 89 files changed, 876 insertions(+), 536 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 3dd667165d58..70c096783af8 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,4 +1,3 @@ -use crate::generate::generate_lint_files; use crate::parse::cursor::{self, Capture, Cursor}; use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint}; use crate::utils::{ @@ -40,7 +39,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name }; remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default()); - generate_lint_files(UpdateMode::Change, &data); + data.gen_decls(UpdateMode::Change); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); } @@ -74,7 +73,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &data); + data.gen_decls(UpdateMode::Change); println!("info: `{old_name}` has successfully been uplifted as `{new_name}`"); println!("note: you must run `cargo uitest` to update the test results"); } @@ -151,7 +150,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &data); + data.gen_decls(UpdateMode::Change); println!("Renamed `{old_name}` to `{new_name}`"); println!("All code referencing the old name has been updated"); @@ -172,11 +171,11 @@ fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_ delete_file_if_exists(lint.path.as_ref()) } else { updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { - let mut start = &src[..lint.declaration_range.start]; + let mut start = &src[..lint.declaration_range.start as usize]; if start.ends_with("\n\n") { start = &start[..start.len() - 1]; } - let mut end = &src[lint.declaration_range.end..]; + let mut end = &src[lint.declaration_range.end as usize..]; if end.starts_with("\n\n") { end = &end[1..]; } diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index 781e37e6144e..f4ee600dde04 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,6 +1,7 @@ +use crate::new_parse_cx; use crate::utils::{ - ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads, - walk_dir_no_dot_or_target, + ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, slice_groups, + split_args_for_threads, walk_dir_no_dot_or_target, }; use itertools::Itertools; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -326,10 +327,30 @@ fn run_rustfmt(update_mode: UpdateMode) { // the "main" function of cargo dev fmt pub fn run(update_mode: UpdateMode) { - run_rustfmt(update_mode); fmt_syms(update_mode); if let Err(e) = fmt_conf(update_mode.is_check()) { e.display(); process::exit(1); } + + new_parse_cx(|cx| { + let data = cx.parse_lint_decls(); + let mut updater = FileUpdater::default(); + + for passes in slice_groups(&data.lint_passes, |head, tail| { + tail.iter().take_while(|&x| x.path == head.path).count() + }) { + updater.update_file_checked("cargo dev fmt", update_mode, &passes[0].path, &mut |_, src, dst| { + let pos = passes.iter().fold(0u32, |start, pass| { + dst.push_str(&src[start as usize..pass.decl_range.start as usize]); + pass.gen_mac(dst); + pass.decl_range.end + }); + dst.push_str(&src[pos as usize..]); + UpdateStatus::from_changed(src != dst) + }); + } + }); + + run_rustfmt(update_mode); } diff --git a/clippy_dev/src/generate.rs b/clippy_dev/src/generate.rs index a4cf15058986..afb328a2eb94 100644 --- a/clippy_dev/src/generate.rs +++ b/clippy_dev/src/generate.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::Cursor; -use crate::parse::{Lint, LintData, ParseCx}; +use crate::parse::{Lint, LintData, LintPass}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; @@ -12,193 +12,218 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; -/// Runs the `update_lints` command. -/// -/// This updates various generated values from the lint source code. -/// -/// `update_mode` indicates if the files should be updated or if updates should be checked for. -/// -/// # Panics -/// -/// Panics if a file path could not read from or then written to -pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { - let data = cx.parse_lint_decls(); - generate_lint_files(update_mode, &data); -} - -#[expect(clippy::too_many_lines)] -pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) { - let mut updater = FileUpdater::default(); - - let mut lints: Vec<_> = data.lints.iter().map(|(&x, y)| (x, y)).collect(); - lints.sort_by_key(|&(x, _)| x); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "CHANGELOG.md", - &mut update_text_region_fn( - "\n", - "", - |dst| { - for &(lint, _) in &lints { - writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ), - ); - - let mut active = Vec::with_capacity(lints.len()); - let mut deprecated = Vec::with_capacity(lints.len() / 8); - let mut renamed = Vec::with_capacity(lints.len() / 8); - for &(name, lint) in &lints { - match lint { - Lint::Active(lint) => active.push((name, lint)), - Lint::Deprecated(lint) => deprecated.push((name, lint)), - Lint::Renamed(lint) => renamed.push((name, lint)), - } - } - active.sort_by_key(|&(_, lint)| lint.module); - - // Round to avoid updating the readme every time a lint is added/deprecated. - let lint_count = active.len() / 50 * 50; - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{lint_count}").unwrap(); - }), - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "book/src/README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{lint_count}").unwrap(); - }), - ); +impl LintData<'_> { + #[expect(clippy::too_many_lines)] + pub fn gen_decls(&self, update_mode: UpdateMode) { + let mut updater = FileUpdater::default(); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "clippy_lints/src/deprecated_lints.rs", - &mut |_, src, dst| { - let mut cursor = Cursor::new(src); - assert!( - cursor.find_ident("declare_with_version").is_some() - && cursor.find_ident("declare_with_version").is_some(), - "error reading deprecated lints" - ); - dst.push_str(&src[..cursor.pos() as usize]); - dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); - for &(name, data) in &deprecated { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", - data.version, data.reason, - ) - .unwrap(); - } - dst.push_str( - "]}\n\n\ - #[rustfmt::skip]\n\ - declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ - ", - ); - for &(name, data) in &renamed { - write!( - dst, - " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", - data.version, data.new_name, - ) - .unwrap(); - } - dst.push_str("]}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "tests/ui/deprecated.rs", - &mut |_, src, dst| { - dst.push_str(GENERATED_FILE_COMMENT); - for &(lint, _) in &deprecated { - writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - updater.update_file_checked( - "cargo dev update_lints", - update_mode, - "tests/ui/rename.rs", - &mut move |_, src, dst| { - let mut seen_lints = HashSet::new(); - dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for &(_, lint) in &renamed { - if seen_lints.insert(lint.new_name) { - writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); - } - } - for &(lint, _) in &renamed { - writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); - } - dst.push_str("\nfn main() {}\n"); - UpdateStatus::from_changed(src != dst) - }, - ); - for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| { - let Some(path::Component::Normal(name)) = lint.path.components().next() else { - // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` - panic!( - "internal error: can't read crate name from path `{}`", - lint.path.display() - ); - }; - name - }) { + let mut lints: Vec<_> = self.lints.iter().map(|(&x, y)| (x, y)).collect(); + lints.sort_by_key(|&(x, _)| x); updater.update_file_checked( "cargo dev update_lints", update_mode, - Path::new(crate_name).join("src/lib.rs"), + "CHANGELOG.md", &mut update_text_region_fn( - "// 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`", + "\n", + "", |dst| { - let mut prev = ""; - for &(_, lint) in &lints { - if lint.module != prev { - writeln!(dst, "mod {};", lint.module).unwrap(); - prev = lint.module; - } + for &(lint, _) in &lints { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); } }, ), ); + + let mut active = Vec::with_capacity(lints.len()); + let mut deprecated = Vec::with_capacity(lints.len() / 8); + let mut renamed = Vec::with_capacity(lints.len() / 8); + for &(name, lint) in &lints { + match lint { + Lint::Active(lint) => active.push((name, lint)), + Lint::Deprecated(lint) => deprecated.push((name, lint)), + Lint::Renamed(lint) => renamed.push((name, lint)), + } + } + active.sort_by_key(|&(_, lint)| lint.module); + + // Round to avoid updating the readme every time a lint is added/deprecated. + let lint_count = active.len() / 50 * 50; + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), + ); + + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "clippy_lints/src/deprecated_lints.rs", + &mut |_, src, dst| { + let mut cursor = Cursor::new(src); + assert!( + cursor.find_ident("declare_with_version").is_some() + && cursor.find_ident("declare_with_version").is_some(), + "error reading deprecated lints" + ); + dst.push_str(&src[..cursor.pos() as usize]); + dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); + for &(name, data) in &deprecated { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.reason, + ) + .unwrap(); + } + dst.push_str( + "]}\n\n\ + #[rustfmt::skip]\n\ + declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ + ", + ); + for &(name, data) in &renamed { + write!( + dst, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.new_name, + ) + .unwrap(); + } + dst.push_str("]}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); updater.update_file_checked( "cargo dev update_lints", update_mode, - Path::new(crate_name).join("src/declared_lints.rs"), + "tests/ui/deprecated.rs", &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); - dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); - let mut buf = String::new(); - for &(name, lint) in &lints { - buf.clear(); - buf.push_str(name); - buf.make_ascii_uppercase(); - if lint.module.is_empty() { - writeln!(dst, " crate::{buf}_INFO,").unwrap(); - } else { - writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap(); + for &(lint, _) in &deprecated { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); + } + dst.push_str("\nfn main() {}\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + "tests/ui/rename.rs", + &mut move |_, src, dst| { + let mut seen_lints = HashSet::new(); + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); + for &(_, lint) in &renamed { + if seen_lints.insert(lint.new_name) { + writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } - dst.push_str("];\n"); + for &(lint, _) in &renamed { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); + } + dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) }, ); + for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| { + let Some(path::Component::Normal(name)) = lint.path.components().next() else { + // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` + panic!( + "internal error: can't read crate name from path `{}`", + lint.path.display() + ); + }; + name + }) { + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/lib.rs"), + &mut update_text_region_fn( + "// 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`", + |dst| { + let mut prev = ""; + for &(_, lint) in &lints { + if lint.module != prev { + writeln!(dst, "mod {};", lint.module).unwrap(); + prev = lint.module; + } + } + }, + ), + ); + updater.update_file_checked( + "cargo dev update_lints", + update_mode, + Path::new(crate_name).join("src/declared_lints.rs"), + &mut |_, src, dst| { + dst.push_str(GENERATED_FILE_COMMENT); + dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); + let mut buf = String::new(); + for &(name, lint) in &lints { + buf.clear(); + buf.push_str(name); + buf.make_ascii_uppercase(); + if lint.module.is_empty() { + writeln!(dst, " crate::{buf}_INFO,").unwrap(); + } else { + writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap(); + } + } + dst.push_str("];\n"); + UpdateStatus::from_changed(src != dst) + }, + ); + } + } +} + +impl LintPass<'_> { + /// Writes the formatted text that comes between the inner brackets of the `declare_lint_pass` + /// and `impl_lint_pass` macros. + pub fn gen_mac(&self, dst: &mut String) { + let start = dst.len(); + let _ = write!(dst, "{}!({}", self.mac.name(), self.name); + if let Some(lt) = self.lt { + let _ = write!(dst, "<{lt}>"); + } + dst.push_str(" => ["); + write_list( + self.lints.iter().copied(), + 80usize.saturating_sub(dst.len() - start), + " ", + dst, + ); + dst.push_str("]);"); + } +} + +fn write_list<'a>( + items: impl Iterator + Clone, + single_line_limit: usize, + indent: &str, + dst: &mut String, +) { + let len = items.clone().map(str::len).sum::(); + if len > single_line_limit { + for item in items { + dst.extend(["\n", indent, item, ","]); + } + dst.push('\n'); + } else { + let _ = write!(dst, "{}", items.format(", ")); } } diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 97149cbb7346..c8a9f86c81f7 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -28,7 +28,6 @@ extern crate rustc_literal_escaper; pub mod dogfood; pub mod edit_lints; pub mod fmt; -pub mod generate; pub mod lint; pub mod new_lint; pub mod release; @@ -36,6 +35,7 @@ pub mod serve; pub mod setup; pub mod sync; +mod generate; mod parse; mod utils; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ba470d90014a..5fe2295a1e21 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -4,8 +4,7 @@ use clap::{Args, Parser, Subcommand}; use clippy_dev::{ - ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, generate, lint, new_lint, new_parse_cx, release, serve, setup, - sync, + ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync, }; use std::env; @@ -28,7 +27,7 @@ fn main() { } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)), DevCommand::UpdateLints { check } => { - new_parse_cx(|cx| generate::generate_lint_files(UpdateMode::from_check(check), &cx.parse_lint_decls())); + new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::from_check(check))); }, DevCommand::NewLint { pass, @@ -37,7 +36,7 @@ fn main() { r#type, msrv, } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) { - Ok(()) => new_parse_cx(|cx| generate::generate_lint_files(UpdateMode::Change, &cx.parse_lint_decls())), + Ok(()) => new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::Change)), Err(e) => eprintln!("Unable to create lint: {e}"), }, DevCommand::Setup(SetupCommand { subcommand }) => match subcommand { diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index ffb50784a9a7..d705d39aaa24 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -13,6 +13,7 @@ use std::str::pattern::Pattern; pub struct ParseCxImpl<'cx> { pub arena: &'cx DroplessArena, pub str_buf: StrBuf, + pub str_list_buf: VecBuf<&'cx str>, } pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>; @@ -22,6 +23,7 @@ pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, f(&mut Scoped::new(ParseCxImpl { arena: &arena, str_buf: StrBuf::with_capacity(128), + str_list_buf: VecBuf::with_capacity(128), })) } @@ -82,6 +84,20 @@ impl StrBuf { } } +pub struct VecBuf(Vec); +impl VecBuf { + /// Creates a new buffer with the specified initial capacity. + pub fn with_capacity(cap: usize) -> Self { + Self(Vec::with_capacity(cap)) + } + + /// Performs an operation with the freshly cleared buffer. + pub fn with(&mut self, f: impl FnOnce(&mut Vec) -> R) -> R { + self.0.clear(); + f(&mut self.0) + } +} + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum LintTool { Rustc, @@ -128,7 +144,7 @@ pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, pub path: PathBuf, - pub declaration_range: Range, + pub declaration_range: Range, } pub struct DeprecatedLint<'cx> { @@ -147,8 +163,32 @@ pub enum Lint<'cx> { Renamed(RenamedLint<'cx>), } +#[derive(Clone, Copy)] +pub enum LintPassMac { + Declare, + Impl, +} +impl LintPassMac { + pub fn name(self) -> &'static str { + match self { + Self::Declare => "declare_lint_pass", + Self::Impl => "impl_lint_pass", + } + } +} + +pub struct LintPass<'cx> { + pub name: &'cx str, + pub lt: Option<&'cx str>, + pub mac: LintPassMac, + pub decl_range: Range, + pub lints: &'cx [&'cx str], + pub path: PathBuf, +} + pub struct LintData<'cx> { pub lints: FxHashMap<&'cx str, Lint<'cx>>, + pub lint_passes: Vec>, } impl<'cx> ParseCxImpl<'cx> { @@ -158,6 +198,7 @@ impl<'cx> ParseCxImpl<'cx> { let mut data = LintData { #[expect(clippy::default_trait_access)] lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()), + lint_passes: Vec::with_capacity(400), }; let mut contents = String::new(); @@ -193,7 +234,7 @@ impl<'cx> ParseCxImpl<'cx> { self.str_buf .alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::") }; - self.parse_clippy_lint_decls( + self.parse_lint_src_file( e.path(), File::open_read_to_cleared_string(e.path(), &mut contents), module, @@ -208,11 +249,11 @@ impl<'cx> ParseCxImpl<'cx> { } /// Parse a source file looking for `declare_clippy_lint` macro invocations. - fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) { + fn parse_lint_src_file(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] - static DECL_TOKENS: &[cursor::Pat<'_>] = &[ + static LINT_DECL_TOKENS: &[cursor::Pat<'_>] = &[ // !{ /// docs Bang, OpenBrace, AnyComment, // #[clippy::version = "version"] @@ -220,24 +261,96 @@ impl<'cx> ParseCxImpl<'cx> { // pub NAME, GROUP, Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma, ]; + #[rustfmt::skip] + static PASS_DECL_TOKENS: &[cursor::Pat<'_>] = &[ + // !( NAME <'lt> => [ + Bang, OpenParen, CaptureIdent, CaptureOptLifetimeArg, FatArrow, OpenBracket, + ]; let mut cursor = Cursor::new(contents); let mut captures = [Capture::EMPTY; 2]; - while let Some(start) = cursor.find_ident("declare_clippy_lint") { - if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { - assert!( - data.lints - .insert( - self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), - Lint::Active(ActiveLint { - group: self.arena.alloc_str(cursor.get_text(captures[1])), - module, - path: path.into(), - declaration_range: start as usize..cursor.pos() as usize, - }), - ) - .is_none() - ); + while let Some(mac_name) = cursor.find_any_ident() { + match cursor.get_text(mac_name) { + "declare_clippy_lint" + if cursor.match_all(LINT_DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) => + { + assert!( + data.lints + .insert( + self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + Lint::Active(ActiveLint { + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: mac_name.pos..cursor.pos(), + }), + ) + .is_none() + ); + }, + mac @ ("declare_lint_pass" | "impl_lint_pass") if cursor.match_all(PASS_DECL_TOKENS, &mut captures) => { + let mac = if matches!(mac, "declare_lint_pass") { + LintPassMac::Declare + } else { + LintPassMac::Impl + }; + let name = self.arena.alloc_str(cursor.get_text(captures[0])); + let lt = cursor.get_text(captures[1]); + let lt = if lt.is_empty() { + None + } else { + Some(self.arena.alloc_str(lt)) + }; + + let lints = self.str_list_buf.with(|buf| { + // Parses a comma separated list of paths and converts each path + // to a string with whitespace removed. + while !cursor.match_pat(CloseBracket) { + buf.push(self.str_buf.with(|buf| { + if cursor.match_pat(DoubleColon) { + buf.push_str("::"); + } + let capture = cursor.capture_ident()?; + buf.push_str(cursor.get_text(capture)); + while cursor.match_pat(DoubleColon) { + buf.push_str("::"); + let capture = cursor.capture_ident()?; + buf.push_str(cursor.get_text(capture)); + } + Some(self.arena.alloc_str(buf)) + })?); + + if !cursor.match_pat(Comma) { + if !cursor.match_pat(CloseBracket) { + return None; + } + break; + } + } + + // The arena panics when allocating a size of zero. + Some(if buf.is_empty() { + &[] + } else { + buf.sort_unstable(); + &*self.arena.alloc_slice(buf) + }) + }); + + if let Some(lints) = lints + && cursor.match_all(&[CloseParen, Semi], &mut []) + { + data.lint_passes.push(LintPass { + name, + lt, + mac, + decl_range: mac_name.pos..cursor.pos(), + lints, + path: path.into(), + }); + } + }, + _ => {}, } } } diff --git a/clippy_dev/src/parse/cursor.rs b/clippy_dev/src/parse/cursor.rs index 2c142af4883a..ad0f7df90b78 100644 --- a/clippy_dev/src/parse/cursor.rs +++ b/clippy_dev/src/parse/cursor.rs @@ -22,12 +22,14 @@ pub enum Pat<'a> { Comma, DoubleColon, Eq, + FatArrow, Lifetime, Lt, Gt, OpenBrace, OpenBracket, OpenParen, + CaptureOptLifetimeArg, Pound, Semi, } @@ -121,7 +123,6 @@ impl<'txt> Cursor<'txt> { Pat::AnyComment, TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. }, ) => self.step(), - (Pat::AnyComment, _) => return true, (Pat::Bang, TokenKind::Bang) | (Pat::CloseBrace, TokenKind::CloseBrace) | (Pat::CloseBracket, TokenKind::CloseBracket) @@ -152,12 +153,48 @@ impl<'txt> Cursor<'txt> { }, (Pat::DoubleColon, TokenKind::Colon) => { self.step(); - if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) { + if matches!(self.next_token.kind, TokenKind::Colon) { + self.step(); + return true; + } + return false; + }, + (Pat::FatArrow, TokenKind::Eq) => { + self.step(); + if matches!(self.next_token.kind, TokenKind::Gt) { self.step(); return true; } return false; }, + (Pat::CaptureOptLifetimeArg, TokenKind::Lt) => { + self.step(); + loop { + match self.next_token.kind { + TokenKind::Lifetime { .. } => break, + TokenKind::Whitespace => self.step(), + _ => return false, + } + } + *captures.next().unwrap() = Capture { + pos: self.pos, + len: self.next_token.len, + }; + self.step(); + loop { + match self.next_token.kind { + TokenKind::Gt => break, + TokenKind::Whitespace => self.step(), + _ => return false, + } + } + self.step(); + return true; + }, + (Pat::CaptureOptLifetimeArg, _) => { + *captures.next().unwrap() = Capture { pos: 0, len: 0 }; + return true; + }, #[rustfmt::skip] ( Pat::CaptureLitStr, @@ -173,6 +210,7 @@ impl<'txt> Cursor<'txt> { self.step(); return true; }, + (Pat::AnyComment, _) => return true, _ => return false, } } @@ -219,8 +257,8 @@ impl<'txt> Cursor<'txt> { } } - /// Consume the returns the position of the next non-whitespace token if it's an - /// identifier. Returns `None` otherwise. + /// Consume the returns the position of the next non-whitespace token if it's the + /// specified identifier. Returns `None` otherwise. pub fn match_ident(&mut self, s: &str) -> Option { loop { match self.next_token.kind { @@ -235,6 +273,23 @@ impl<'txt> Cursor<'txt> { } } + /// Consumes and captures the next non-whitespace token if it's an identifier. Returns + /// `None` otherwise. + pub fn capture_ident(&mut self) -> Option { + loop { + match self.next_token.kind { + TokenKind::Ident => { + let pos = self.pos; + let len = self.next_token.len; + self.step(); + return Some(Capture { pos, len }); + }, + TokenKind::Whitespace => self.step(), + _ => return None, + } + } + } + /// Continually attempt to match the pattern on subsequent tokens until a match is /// found. Returns whether the pattern was successfully matched. /// diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 52452dd86b49..98a3458d6c2a 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -600,3 +600,27 @@ pub fn walk_dir_no_dot_or_target(p: impl AsRef) -> impl Iterator( + slice: &'a [T], + split_idx: impl FnMut(&'a T, &'a [T]) -> usize, +) -> impl Iterator { + struct I<'a, T, F> { + slice: &'a [T], + split_idx: F, + } + impl<'a, T, F: FnMut(&'a T, &'a [T]) -> usize> Iterator for I<'a, T, F> { + type Item = &'a [T]; + fn next(&mut self) -> Option { + let (head, tail) = self.slice.split_first()?; + if let Some((head, tail)) = self.slice.split_at_checked((self.split_idx)(head, tail) + 1) { + self.slice = tail; + Some(head) + } else { + self.slice = &[]; + None + } + } + } + I { slice, split_idx } +} diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 91c2dc7f3dc6..d57b7e59cd15 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -478,10 +478,7 @@ pub struct Attributes { msrv: Msrv, } -impl_lint_pass!(Attributes => [ - INLINE_ALWAYS, - REPR_PACKED_WITHOUT_ABI, -]); +impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]); impl Attributes { pub fn new(conf: &'static Conf) -> Self { @@ -527,8 +524,8 @@ impl EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - NON_MINIMAL_CFG, DEPRECATED_CLIPPY_CFG_ATTR, + NON_MINIMAL_CFG, UNNECESSARY_CLIPPY_CFG, ]); @@ -557,13 +554,13 @@ impl PostExpansionEarlyAttributes { impl_lint_pass!(PostExpansionEarlyAttributes => [ ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, + BLANKET_CLIPPY_RESTRICTION_LINTS, DEPRECATED_SEMVER, + DUPLICATED_ATTRIBUTES, IGNORE_WITHOUT_REASON, - USELESS_ATTRIBUTE, - BLANKET_CLIPPY_RESTRICTION_LINTS, - SHOULD_PANIC_WITHOUT_EXPECT, MIXED_ATTRIBUTES_STYLE, - DUPLICATED_ATTRIBUTES, + SHOULD_PANIC_WITHOUT_EXPECT, + USELESS_ATTRIBUTE, ]); impl EarlyLintPass for PostExpansionEarlyAttributes { diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 31cc004f6855..2d61c5e0b78a 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -171,7 +171,11 @@ declare_clippy_lint! { "holding a type across an await point which is not allowed to be held as per the configuration" } -impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); +impl_lint_pass!(AwaitHolding => [ + AWAIT_HOLDING_INVALID_TYPE, + AWAIT_HOLDING_LOCK, + AWAIT_HOLDING_REFCELL_REF, +]); pub struct AwaitHolding { def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>, diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 60371dcd7715..65be4d7559ff 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -220,11 +220,11 @@ pub struct Cargo { impl_lint_pass!(Cargo => [ CARGO_COMMON_METADATA, - REDUNDANT_FEATURE_NAMES, - NEGATIVE_FEATURE_NAMES, + LINT_GROUPS_PRIORITY, MULTIPLE_CRATE_VERSIONS, + NEGATIVE_FEATURE_NAMES, + REDUNDANT_FEATURE_NAMES, WILDCARD_DEPENDENCIES, - LINT_GROUPS_PRIORITY, ]); impl Cargo { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 47cc1da0a6e9..2f6f9bf87cba 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -824,33 +824,33 @@ impl Casts { } impl_lint_pass!(Casts => [ - CAST_PRECISION_LOSS, - CAST_SIGN_LOSS, + AS_POINTER_UNDERSCORE, + AS_PTR_CAST_MUT, + AS_UNDERSCORE, + BORROW_AS_PTR, + CAST_ABS_TO_UNSIGNED, + CAST_ENUM_CONSTRUCTOR, + CAST_ENUM_TRUNCATION, + CAST_LOSSLESS, + CAST_NAN_TO_INT, CAST_POSSIBLE_TRUNCATION, CAST_POSSIBLE_WRAP, - CAST_LOSSLESS, + CAST_PRECISION_LOSS, CAST_PTR_ALIGNMENT, + CAST_SIGN_LOSS, CAST_SLICE_DIFFERENT_SIZES, - UNNECESSARY_CAST, - FN_TO_NUMERIC_CAST_ANY, + CAST_SLICE_FROM_RAW_PARTS, + CHAR_LIT_AS_U8, + CONFUSING_METHOD_TO_NUMERIC_CAST, FN_TO_NUMERIC_CAST, + FN_TO_NUMERIC_CAST_ANY, FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - CHAR_LIT_AS_U8, + MANUAL_DANGLING_PTR, PTR_AS_PTR, PTR_CAST_CONSTNESS, - CAST_ENUM_TRUNCATION, - CAST_ENUM_CONSTRUCTOR, - CAST_ABS_TO_UNSIGNED, - AS_UNDERSCORE, - BORROW_AS_PTR, - CAST_SLICE_FROM_RAW_PARTS, - AS_PTR_CAST_MUT, - CAST_NAN_TO_INT, - ZERO_PTR, REF_AS_PTR, - AS_POINTER_UNDERSCORE, - MANUAL_DANGLING_PTR, - CONFUSING_METHOD_TO_NUMERIC_CAST, + UNNECESSARY_CAST, + ZERO_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index b13e307a3f9c..3e0c4fe02a66 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -259,7 +259,7 @@ impl CollapsibleIf { } } -impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]); impl LateLintPass<'_> for CollapsibleIf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index 641f8ae03b72..95f0afcc3d88 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -45,7 +45,9 @@ declare_clippy_lint! { complexity, "unit structs can be constructed without calling `default`" } -declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]); +declare_lint_pass!(DefaultConstructedUnitStructs => [ + DEFAULT_CONSTRUCTED_UNIT_STRUCTS, +]); fn is_alias(ty: hir::Ty<'_>) -> bool { if let hir::TyKind::Path(ref qpath) = ty.kind { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index de1362081323..7b6c303473a9 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -142,10 +142,10 @@ declare_clippy_lint! { } impl_lint_pass!(Dereferencing<'_> => [ + EXPLICIT_AUTO_DEREF, EXPLICIT_DEREF_METHODS, NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE, - EXPLICIT_AUTO_DEREF, ]); #[derive(Default)] diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index eafe7c4bb9f2..cf9ed10be02e 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -185,11 +185,11 @@ declare_clippy_lint! { } declare_lint_pass!(Derive => [ - EXPL_IMPL_CLONE_ON_COPY, DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + EXPL_IMPL_CLONE_ON_COPY, UNSAFE_DERIVE_DESERIALIZE, - DERIVE_PARTIAL_EQ_WITHOUT_EQ ]); impl<'tcx> LateLintPass<'tcx> for Derive { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 2a3fb8294611..5a3aa6545e22 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -685,25 +685,25 @@ impl Documentation { } impl_lint_pass!(Documentation => [ + DOC_BROKEN_LINK, + DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, + DOC_INCLUDE_WITHOUT_CFG, + DOC_LAZY_CONTINUATION, DOC_LINK_CODE, DOC_LINK_WITH_QUOTES, - DOC_BROKEN_LINK, DOC_MARKDOWN, DOC_NESTED_REFDEFS, - MISSING_SAFETY_DOC, + DOC_OVERINDENTED_LIST_ITEMS, + DOC_SUSPICIOUS_FOOTNOTES, + EMPTY_DOCS, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, + MISSING_SAFETY_DOC, NEEDLESS_DOCTEST_MAIN, - TEST_ATTR_IN_DOCTEST, - UNNECESSARY_SAFETY_DOC, SUSPICIOUS_DOC_COMMENTS, - EMPTY_DOCS, - DOC_LAZY_CONTINUATION, - DOC_OVERINDENTED_LIST_ITEMS, + TEST_ATTR_IN_DOCTEST, TOO_LONG_FIRST_DOC_PARAGRAPH, - DOC_INCLUDE_WITHOUT_CFG, - DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS, - DOC_SUSPICIOUS_FOOTNOTES, + UNNECESSARY_SAFETY_DOC, ]); impl EarlyLintPass for Documentation { diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 3bb8c484ceec..db7414559769 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -75,11 +75,7 @@ const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ Forgetting such a type is the same as dropping it"; -declare_lint_pass!(DropForgetRef => [ - DROP_NON_DROP, - FORGET_NON_DROP, - MEM_FORGET, -]); +declare_lint_pass!(DropForgetRef => [DROP_NON_DROP, FORGET_NON_DROP, MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for DropForgetRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/empty_line_after.rs b/clippy_lints/src/empty_line_after.rs index 76e67b1154be..e3453c7be1d7 100644 --- a/clippy_lints/src/empty_line_after.rs +++ b/clippy_lints/src/empty_line_after.rs @@ -101,8 +101,8 @@ pub struct EmptyLineAfter { } impl_lint_pass!(EmptyLineAfter => [ - EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, + EMPTY_LINE_AFTER_OUTER_ATTR, ]); impl EmptyLineAfter { diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index e7230ebf8cba..67eb8880dd64 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -88,7 +88,10 @@ pub struct EmptyWithBrackets { empty_tuple_enum_variants: FxIndexMap, } -impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]); +impl_lint_pass!(EmptyWithBrackets => [ + EMPTY_ENUM_VARIANTS_WITH_BRACKETS, + EMPTY_STRUCTS_WITH_BRACKETS, +]); impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index 75db923b1c37..37a645756217 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -63,7 +63,11 @@ declare_clippy_lint! { "disallows usage of the `to_be_bytes` method" } -declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]); +declare_lint_pass!(EndianBytes => [ + BIG_ENDIAN_BYTES, + HOST_ENDIAN_BYTES, + LITTLE_ENDIAN_BYTES, +]); const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes]; const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes]; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 21385ee4fdc7..3a89d602f7cb 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -66,7 +66,10 @@ declare_clippy_lint! { "redundant closures for method calls" } -declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]); +declare_lint_pass!(EtaReduction => [ + REDUNDANT_CLOSURE, + REDUNDANT_CLOSURE_FOR_METHOD_CALLS, +]); impl<'tcx> LateLintPass<'tcx> for EtaReduction { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 686dc5c3c4fc..fdcb013bb71e 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -102,7 +102,10 @@ impl ExcessiveBools { } } -impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); +impl_lint_pass!(ExcessiveBools => [ + FN_PARAMS_EXCESSIVE_BOOLS, + STRUCT_EXCESSIVE_BOOLS, +]); fn has_n_bools<'tcx>(iter: impl Iterator>, mut count: u64) -> bool { iter.filter(|ty| is_bool(ty)).any(|_| { diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs index dfb0b4f103c5..4b736c68cb86 100644 --- a/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -47,7 +47,9 @@ declare_clippy_lint! { "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields" } -declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MODIFIERS]); +declare_lint_pass!(FieldScopedVisibilityModifiers => [ + FIELD_SCOPED_VISIBILITY_MODIFIERS, +]); impl EarlyLintPass for FieldScopedVisibilityModifiers { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 6178addfff12..630291e4d836 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -65,9 +65,7 @@ pub struct FloatLiteral { const_literal_digits_threshold: usize, } -impl_lint_pass!(FloatLiteral => [ - EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL -]); +impl_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl FloatLiteral { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 5f022ba307ff..45e17a29242e 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -105,10 +105,7 @@ declare_clippy_lint! { "usage of sub-optimal floating point operations" } -declare_lint_pass!(FloatingPointArithmetic => [ - IMPRECISE_FLOPS, - SUBOPTIMAL_FLOPS -]); +declare_lint_pass!(FloatingPointArithmetic => [IMPRECISE_FLOPS, SUBOPTIMAL_FLOPS]); // Returns the specialized log method for a given base if base is constant // and is one of 2, 10 and e diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 011cbf8c5d41..925175e98bb0 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -231,11 +231,11 @@ declare_clippy_lint! { impl_lint_pass!(FormatArgs<'_> => [ FORMAT_IN_FORMAT_ARGS, + POINTER_FORMAT, TO_STRING_IN_FORMAT_ARGS, UNINLINED_FORMAT_ARGS, UNNECESSARY_DEBUG_FORMATTING, UNUSED_FORMAT_SPECS, - POINTER_FORMAT, ]); #[expect(clippy::struct_field_names)] diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 903d43e56c4b..744d087f4e0f 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -113,7 +113,7 @@ impl FormatImpl { } } -impl_lint_pass!(FormatImpl => [RECURSIVE_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL]); +impl_lint_pass!(FormatImpl => [PRINT_IN_FORMAT_IMPL, RECURSIVE_FORMAT_IMPL]); impl<'tcx> LateLintPass<'tcx> for FormatImpl { fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 1c751643becb..512f892e61e6 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -138,11 +138,11 @@ declare_clippy_lint! { } declare_lint_pass!(Formatting => [ + POSSIBLE_MISSING_COMMA, + POSSIBLE_MISSING_ELSE, SUSPICIOUS_ASSIGNMENT_FORMATTING, - SUSPICIOUS_UNARY_OP_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, - POSSIBLE_MISSING_ELSE, - POSSIBLE_MISSING_COMMA + SUSPICIOUS_UNARY_OP_FORMATTING, ]); impl EarlyLintPass for Formatting { diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 5a40af421942..a74614c583eb 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -510,18 +510,18 @@ impl Functions { } impl_lint_pass!(Functions => [ - TOO_MANY_ARGUMENTS, - TOO_MANY_LINES, - NOT_UNSAFE_PTR_ARG_DEREF, - MUST_USE_UNIT, DOUBLE_MUST_USE, - MUST_USE_CANDIDATE, - RESULT_UNIT_ERR, - RESULT_LARGE_ERR, - MISNAMED_GETTERS, IMPL_TRAIT_IN_PARAMS, - RENAMED_FUNCTION_PARAMS, + MISNAMED_GETTERS, + MUST_USE_CANDIDATE, + MUST_USE_UNIT, + NOT_UNSAFE_PTR_ARG_DEREF, REF_OPTION, + RENAMED_FUNCTION_PARAMS, + RESULT_LARGE_ERR, + RESULT_UNIT_ERR, + TOO_MANY_ARGUMENTS, + TOO_MANY_LINES, ]); impl<'tcx> LateLintPass<'tcx> for Functions { diff --git a/clippy_lints/src/ifs/mod.rs b/clippy_lints/src/ifs/mod.rs index 739f2fc91729..fa17685c1698 100644 --- a/clippy_lints/src/ifs/mod.rs +++ b/clippy_lints/src/ifs/mod.rs @@ -160,10 +160,10 @@ impl<'tcx> CopyAndPaste<'tcx> { } impl_lint_pass!(CopyAndPaste<'_> => [ + BRANCHES_SHARING_CODE, IFS_SAME_COND, - SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, - BRANCHES_SHARING_CODE + SAME_FUNCTIONS_IN_IF_CONDITION, ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { diff --git a/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs b/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs index f73182d3af0d..dec08d94e89d 100644 --- a/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs +++ b/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs @@ -69,7 +69,9 @@ declare_clippy_lint! { "ensures that the semantics of `Borrow` for `Hash` are satisfied when `Borrow` and `Borrow<[u8]>` are implemented" } -declare_lint_pass!(ImplHashWithBorrowStrBytes => [IMPL_HASH_BORROW_WITH_STR_AND_BYTES]); +declare_lint_pass!(ImplHashWithBorrowStrBytes => [ + IMPL_HASH_BORROW_WITH_STR_AND_BYTES, +]); impl LateLintPass<'_> for ImplHashWithBorrowStrBytes { /// We are emitting this lint at the Hash impl of a type that implements all diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 678a29924e52..0eca4e70f9b9 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -79,7 +79,10 @@ pub struct ImplicitSaturatingSub { msrv: Msrv, } -impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); +impl_lint_pass!(ImplicitSaturatingSub => [ + IMPLICIT_SATURATING_SUB, + INVERTED_SATURATING_SUB, +]); impl ImplicitSaturatingSub { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index e6129757e560..60cc1da3eee2 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -76,7 +76,9 @@ impl InconsistentStructConstructor { } } -impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +impl_lint_pass!(InconsistentStructConstructor => [ + INCONSISTENT_STRUCT_CONSTRUCTOR, +]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index e569a5c7b612..22082646eb31 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -88,7 +88,10 @@ declare_clippy_lint! { "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait" } -declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]); +declare_lint_pass!(InherentToString => [ + INHERENT_TO_STRING, + INHERENT_TO_STRING_SHADOW_DISPLAY, +]); impl<'tcx> LateLintPass<'tcx> for InherentToString { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { diff --git a/clippy_lints/src/integer_division_remainder_used.rs b/clippy_lints/src/integer_division_remainder_used.rs index a1215491b48c..4a01b8e1cb66 100644 --- a/clippy_lints/src/integer_division_remainder_used.rs +++ b/clippy_lints/src/integer_division_remainder_used.rs @@ -28,7 +28,9 @@ declare_clippy_lint! { "use of disallowed default division and remainder operations" } -declare_lint_pass!(IntegerDivisionRemainderUsed => [INTEGER_DIVISION_REMAINDER_USED]); +declare_lint_pass!(IntegerDivisionRemainderUsed => [ + INTEGER_DIVISION_REMAINDER_USED, +]); impl LateLintPass<'_> for IntegerDivisionRemainderUsed { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 76f5fdfaa8dc..100ebf88bfec 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -197,9 +197,9 @@ impl ItemNameRepetitions { impl_lint_pass!(ItemNameRepetitions => [ ENUM_VARIANT_NAMES, - STRUCT_FIELD_NAMES, + MODULE_INCEPTION, MODULE_NAME_REPETITIONS, - MODULE_INCEPTION + STRUCT_FIELD_NAMES, ]); #[must_use] diff --git a/clippy_lints/src/iter_without_into_iter.rs b/clippy_lints/src/iter_without_into_iter.rs index 645e0f981f27..89f8ede06bb3 100644 --- a/clippy_lints/src/iter_without_into_iter.rs +++ b/clippy_lints/src/iter_without_into_iter.rs @@ -105,7 +105,10 @@ declare_clippy_lint! { "implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method" } -declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]); +declare_lint_pass!(IterWithoutIntoIter => [ + INTO_ITER_WITHOUT_ITER, + ITER_WITHOUT_INTO_ITER, +]); /// Checks if a given type is nameable in a trait (impl). /// RPIT is stable, but impl Trait in traits is not (yet), so when we have diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 877bd34a732b..870d60f82b78 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -126,7 +126,7 @@ pub struct LenZero { msrv: Msrv, } -impl_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]); +impl_lint_pass!(LenZero => [COMPARISON_TO_EMPTY, LEN_WITHOUT_IS_EMPTY, LEN_ZERO]); impl LenZero { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index b72e14246db7..ff6ba3ea1623 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -127,7 +127,12 @@ declare_clippy_lint! { "non-binding `let` without a type annotation" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]); +declare_lint_pass!(LetUnderscore => [ + LET_UNDERSCORE_FUTURE, + LET_UNDERSCORE_LOCK, + LET_UNDERSCORE_MUST_USE, + LET_UNDERSCORE_UNTYPED, +]); impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 727e9b172a87..8d118b2b431d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -135,9 +135,9 @@ impl Lifetimes { } impl_lint_pass!(Lifetimes => [ - NEEDLESS_LIFETIMES, ELIDABLE_LIFETIME_NAMES, EXTRA_UNUSED_LIFETIMES, + NEEDLESS_LIFETIMES, ]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7cbfa2d097ae..1dd3a042e3bf 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -196,10 +196,10 @@ pub struct LiteralDigitGrouping { } impl_lint_pass!(LiteralDigitGrouping => [ - UNREADABLE_LITERAL, INCONSISTENT_DIGIT_GROUPING, LARGE_DIGIT_GROUPS, MISTYPED_LITERAL_SUFFIXES, + UNREADABLE_LITERAL, UNUSUAL_BYTE_GROUPINGS, ]); diff --git a/clippy_lints/src/literal_string_with_formatting_args.rs b/clippy_lints/src/literal_string_with_formatting_args.rs index 244e7c95122e..2751a7edc1ae 100644 --- a/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/clippy_lints/src/literal_string_with_formatting_args.rs @@ -36,7 +36,9 @@ declare_clippy_lint! { "Checks if string literals have formatting arguments" } -declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARGS]); +declare_lint_pass!(LiteralStringWithFormattingArg => [ + LITERAL_STRING_WITH_FORMATTING_ARGS, +]); fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option)]) { if !spans.is_empty() diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index a064a5910ef9..2a1cd5889406 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -798,30 +798,30 @@ impl Loops { } impl_lint_pass!(Loops => [ - MANUAL_MEMCPY, - MANUAL_FLATTEN, - NEEDLESS_RANGE_LOOP, - EXPLICIT_ITER_LOOP, - EXPLICIT_INTO_ITER_LOOP, - ITER_NEXT_LOOP, - WHILE_LET_LOOP, - EXPLICIT_COUNTER_LOOP, + CHAR_INDICES_AS_BYTE_INDICES, EMPTY_LOOP, - WHILE_LET_ON_ITERATOR, + EXPLICIT_COUNTER_LOOP, + EXPLICIT_INTO_ITER_LOOP, + EXPLICIT_ITER_LOOP, FOR_KV_MAP, - NEVER_LOOP, + INFINITE_LOOP, + ITER_NEXT_LOOP, + MANUAL_FIND, + MANUAL_FLATTEN, + MANUAL_MEMCPY, + MANUAL_SLICE_FILL, + MANUAL_WHILE_LET_SOME, + MISSING_SPIN_LOOP, MUT_RANGE_BOUND, - WHILE_IMMUTABLE_CONDITION, - WHILE_FLOAT, + NEEDLESS_RANGE_LOOP, + NEVER_LOOP, SAME_ITEM_PUSH, SINGLE_ELEMENT_LOOP, - MISSING_SPIN_LOOP, - MANUAL_FIND, - MANUAL_WHILE_LET_SOME, UNUSED_ENUMERATE_INDEX, - INFINITE_LOOP, - MANUAL_SLICE_FILL, - CHAR_INDICES_AS_BYTE_INDICES, + WHILE_FLOAT, + WHILE_IMMUTABLE_CONDITION, + WHILE_LET_LOOP, + WHILE_LET_ON_ITERATOR, ]); impl<'tcx> LateLintPass<'tcx> for Loops { diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index a81c4dc6a793..6914c88323ac 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -61,7 +61,7 @@ declare_clippy_lint! { style, "use dedicated method to check if a float is finite" } -impl_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]); +impl_lint_pass!(ManualFloatMethods => [MANUAL_IS_FINITE, MANUAL_IS_INFINITE]); #[derive(Clone, Copy)] enum Variant { diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 6f49c5524118..aeffbb26722f 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1021,33 +1021,33 @@ impl Matches { } impl_lint_pass!(Matches => [ - SINGLE_MATCH, - MATCH_REF_PATS, - MATCH_BOOL, - SINGLE_MATCH_ELSE, - MATCH_OVERLAPPING_ARM, - MATCH_WILD_ERR_ARM, - MATCH_AS_REF, - WILDCARD_ENUM_MATCH_ARM, - MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - WILDCARD_IN_OR_PATTERNS, - MATCH_SINGLE_BINDING, - INFALLIBLE_DESTRUCTURING_MATCH, - REST_PAT_IN_FULLY_BOUND_STRUCTS, - REDUNDANT_PATTERN_MATCHING, - MATCH_LIKE_MATCHES_MACRO, - MATCH_SAME_ARMS, - NEEDLESS_MATCH, COLLAPSIBLE_MATCH, + INFALLIBLE_DESTRUCTURING_MATCH, + MANUAL_FILTER, + MANUAL_MAP, + MANUAL_OK_ERR, MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT, + MATCH_AS_REF, + MATCH_BOOL, + MATCH_LIKE_MATCHES_MACRO, + MATCH_OVERLAPPING_ARM, + MATCH_REF_PATS, + MATCH_SAME_ARMS, + MATCH_SINGLE_BINDING, MATCH_STR_CASE_MISMATCH, + MATCH_WILDCARD_FOR_SINGLE_VARIANTS, + MATCH_WILD_ERR_ARM, + NEEDLESS_MATCH, + REDUNDANT_GUARDS, + REDUNDANT_PATTERN_MATCHING, + REST_PAT_IN_FULLY_BOUND_STRUCTS, SIGNIFICANT_DROP_IN_SCRUTINEE, + SINGLE_MATCH, + SINGLE_MATCH_ELSE, TRY_ERR, - MANUAL_MAP, - MANUAL_FILTER, - REDUNDANT_GUARDS, - MANUAL_OK_ERR, + WILDCARD_ENUM_MATCH_ARM, + WILDCARD_IN_OR_PATTERNS, ]); impl<'tcx> LateLintPass<'tcx> for Matches { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index ac3cbaec55f3..6147c8badfd1 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -124,8 +124,12 @@ declare_clippy_lint! { "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`" } -impl_lint_pass!(MemReplace => - [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); +impl_lint_pass!(MemReplace => [ + MEM_REPLACE_OPTION_WITH_NONE, + MEM_REPLACE_OPTION_WITH_SOME, + MEM_REPLACE_WITH_DEFAULT, + MEM_REPLACE_WITH_UNINIT, +]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index fefd46f35c61..d48489738950 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4696,157 +4696,157 @@ impl Methods { } impl_lint_pass!(Methods => [ - UNWRAP_USED, - EXPECT_USED, - SHOULD_IMPLEMENT_TRAIT, - WRONG_SELF_CONVENTION, - OK_EXPECT, - UNWRAP_OR_DEFAULT, - MAP_UNWRAP_OR, - RESULT_MAP_OR_INTO_OPTION, - OPTION_MAP_OR_NONE, BIND_INSTEAD_OF_MAP, - OR_FUN_CALL, - OR_THEN_UNWRAP, - EXPECT_FUN_CALL, - CHARS_NEXT_CMP, + BYTES_COUNT_TO_LEN, + BYTES_NTH, + CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, + CHARS_NEXT_CMP, + CLEAR_WITH_DRAIN, + CLONED_INSTEAD_OF_COPIED, CLONE_ON_COPY, CLONE_ON_REF_PTR, COLLAPSIBLE_STR_REPLACE, CONST_IS_EMPTY, - ITER_OVEREAGER_CLONED, - CLONED_INSTEAD_OF_COPIED, - FLAT_MAP_OPTION, - INEFFICIENT_TO_STRING, - NEW_RET_NO_SELF, - SINGLE_CHAR_ADD_STR, - SEARCH_IS_SOME, - FILTER_NEXT, - SKIP_WHILE_NEXT, + DOUBLE_ENDED_ITERATOR_LAST, + DRAIN_COLLECT, + ERR_EXPECT, + EXPECT_FUN_CALL, + EXPECT_USED, + EXTEND_WITH_DRAIN, + FILETYPE_IS_FILE, + FILTER_MAP_BOOL_THEN, FILTER_MAP_IDENTITY, - MAP_IDENTITY, - MANUAL_FILTER_MAP, - MANUAL_FIND_MAP, - OPTION_FILTER_MAP, FILTER_MAP_NEXT, + FILTER_NEXT, FLAT_MAP_IDENTITY, - MAP_FLATTEN, + FLAT_MAP_OPTION, + FORMAT_COLLECT, + FROM_ITER_INSTEAD_OF_COLLECT, + GET_FIRST, + GET_LAST_WITH_LEN, + GET_UNWRAP, + IMPLICIT_CLONE, + INEFFICIENT_TO_STRING, + INSPECT_FOR_EACH, + INTO_ITER_ON_REF, + IO_OTHER_ERROR, + IP_CONSTANT, + IS_DIGIT_ASCII_RADIX, ITERATOR_STEP_BY_ZERO, - ITER_NEXT_SLICE, + ITER_CLONED_COLLECT, ITER_COUNT, + ITER_FILTER_IS_OK, + ITER_FILTER_IS_SOME, + ITER_KV_MAP, + ITER_NEXT_SLICE, ITER_NTH, ITER_NTH_ZERO, - BYTES_NTH, + ITER_ON_EMPTY_COLLECTIONS, + ITER_ON_SINGLE_ITEMS, + ITER_OUT_OF_BOUNDS, + ITER_OVEREAGER_CLONED, ITER_SKIP_NEXT, - GET_UNWRAP, - GET_LAST_WITH_LEN, - STRING_EXTEND_CHARS, - ITER_CLONED_COLLECT, + ITER_SKIP_ZERO, ITER_WITH_DRAIN, - TYPE_ID_ON_BOX, - USELESS_ASREF, - UNNECESSARY_FOLD, - UNNECESSARY_FILTER_MAP, - UNNECESSARY_FIND_MAP, - INTO_ITER_ON_REF, - SUSPICIOUS_MAP, - UNINIT_ASSUMED_INIT, + JOIN_ABSOLUTE_PATHS, + MANUAL_CONTAINS, + MANUAL_C_STR_LITERALS, + MANUAL_FILTER_MAP, + MANUAL_FIND_MAP, + MANUAL_INSPECT, + MANUAL_IS_VARIANT_AND, + MANUAL_NEXT_BACK, + MANUAL_OK_OR, + MANUAL_REPEAT_N, MANUAL_SATURATING_ARITHMETIC, - ZST_OFFSET, - PTR_OFFSET_WITH_CAST, - FILETYPE_IS_FILE, - OPTION_AS_REF_DEREF, - UNNECESSARY_LAZY_EVALUATIONS, - MAP_COLLECT_RESULT_UNIT, - FROM_ITER_INSTEAD_OF_COLLECT, - INSPECT_FOR_EACH, - IMPLICIT_CLONE, - SUSPICIOUS_TO_OWNED, - SUSPICIOUS_SPLITN, - MANUAL_STR_REPEAT, - EXTEND_WITH_DRAIN, MANUAL_SPLIT_ONCE, - NEEDLESS_SPLITN, - UNNECESSARY_TO_OWNED, - UNNECESSARY_JOIN, - ERR_EXPECT, - NEEDLESS_OPTION_AS_DEREF, - IS_DIGIT_ASCII_RADIX, - NEEDLESS_OPTION_TAKE, - NO_EFFECT_REPLACE, - OBFUSCATED_IF_ELSE, - ITER_ON_SINGLE_ITEMS, - ITER_ON_EMPTY_COLLECTIONS, - NAIVE_BYTECOUNT, - BYTES_COUNT_TO_LEN, - CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - GET_FIRST, - MANUAL_OK_OR, + MANUAL_STR_REPEAT, + MANUAL_TRY_FOLD, + MAP_ALL_ANY_IDENTITY, MAP_CLONE, + MAP_COLLECT_RESULT_UNIT, MAP_ERR_IGNORE, + MAP_FLATTEN, + MAP_IDENTITY, + MAP_UNWRAP_OR, + MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, MUT_MUTEX_LOCK, + NAIVE_BYTECOUNT, + NEEDLESS_AS_BYTES, + NEEDLESS_CHARACTER_ITERATION, + NEEDLESS_COLLECT, + NEEDLESS_OPTION_AS_DEREF, + NEEDLESS_OPTION_TAKE, + NEEDLESS_SPLITN, + NEW_RET_NO_SELF, NONSENSICAL_OPEN_OPTIONS, - SUSPICIOUS_OPEN_OPTIONS, + NO_EFFECT_REPLACE, + OBFUSCATED_IF_ELSE, + OK_EXPECT, + OPTION_AS_REF_CLONED, + OPTION_AS_REF_DEREF, + OPTION_FILTER_MAP, + OPTION_MAP_OR_NONE, + OR_FUN_CALL, + OR_THEN_UNWRAP, PATH_BUF_PUSH_OVERWRITE, + PATH_ENDS_WITH_EXT, + PTR_OFFSET_WITH_CAST, RANGE_ZIP_WITH_LEN, - REPEAT_ONCE, - STABLE_SORT_PRIMITIVE, - UNIT_HASH, + READONLY_WRITE_LOCK, READ_LINE_WITHOUT_TRIM, - UNNECESSARY_SORT_BY, - VEC_RESIZE_TO_ZERO, - VERBOSE_FILE_READS, - ITER_KV_MAP, + REDUNDANT_AS_STR, + REDUNDANT_ITER_CLONED, + REPEAT_ONCE, + RESULT_FILTER_MAP, + RESULT_MAP_OR_INTO_OPTION, + RETURN_AND_THEN, + SEARCH_IS_SOME, SEEK_FROM_CURRENT, SEEK_TO_START_INSTEAD_OF_REWIND, - NEEDLESS_COLLECT, - SUSPICIOUS_COMMAND_ARG_SPACE, - CLEAR_WITH_DRAIN, - MANUAL_NEXT_BACK, - UNNECESSARY_LITERAL_UNWRAP, - DRAIN_COLLECT, - MANUAL_TRY_FOLD, - FORMAT_COLLECT, + SHOULD_IMPLEMENT_TRAIT, + SINGLE_CHAR_ADD_STR, + SKIP_WHILE_NEXT, + SLICED_STRING_AS_BYTES, + STABLE_SORT_PRIMITIVE, + STRING_EXTEND_CHARS, STRING_LIT_CHARS_ANY, - ITER_SKIP_ZERO, - FILTER_MAP_BOOL_THEN, - READONLY_WRITE_LOCK, - ITER_OUT_OF_BOUNDS, - PATH_ENDS_WITH_EXT, - REDUNDANT_AS_STR, - WAKER_CLONE_WAKE, - UNNECESSARY_FALLIBLE_CONVERSIONS, - JOIN_ABSOLUTE_PATHS, - RESULT_FILTER_MAP, - ITER_FILTER_IS_SOME, - ITER_FILTER_IS_OK, - MANUAL_IS_VARIANT_AND, STR_SPLIT_AT_NEWLINE, - OPTION_AS_REF_CLONED, - UNNECESSARY_RESULT_MAP_OR_ELSE, - MANUAL_C_STR_LITERALS, - UNNECESSARY_GET_THEN_CHECK, + SUSPICIOUS_COMMAND_ARG_SPACE, + SUSPICIOUS_MAP, + SUSPICIOUS_OPEN_OPTIONS, + SUSPICIOUS_SPLITN, + SUSPICIOUS_TO_OWNED, + SWAP_WITH_TEMPORARY, + TYPE_ID_ON_BOX, + UNBUFFERED_BYTES, + UNINIT_ASSUMED_INIT, + UNIT_HASH, + UNNECESSARY_FALLIBLE_CONVERSIONS, + UNNECESSARY_FILTER_MAP, + UNNECESSARY_FIND_MAP, UNNECESSARY_FIRST_THEN_CHECK, - NEEDLESS_CHARACTER_ITERATION, - MANUAL_INSPECT, - UNNECESSARY_MIN_OR_MAX, - NEEDLESS_AS_BYTES, - MAP_ALL_ANY_IDENTITY, - MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, + UNNECESSARY_FOLD, + UNNECESSARY_GET_THEN_CHECK, + UNNECESSARY_JOIN, + UNNECESSARY_LAZY_EVALUATIONS, + UNNECESSARY_LITERAL_UNWRAP, UNNECESSARY_MAP_OR, - DOUBLE_ENDED_ITERATOR_LAST, - USELESS_NONZERO_NEW_UNCHECKED, - MANUAL_REPEAT_N, - SLICED_STRING_AS_BYTES, - RETURN_AND_THEN, - UNBUFFERED_BYTES, - MANUAL_CONTAINS, - IO_OTHER_ERROR, - SWAP_WITH_TEMPORARY, - IP_CONSTANT, - REDUNDANT_ITER_CLONED, + UNNECESSARY_MIN_OR_MAX, UNNECESSARY_OPTION_MAP_OR_ELSE, + UNNECESSARY_RESULT_MAP_OR_ELSE, + UNNECESSARY_SORT_BY, + UNNECESSARY_TO_OWNED, + UNWRAP_OR_DEFAULT, + UNWRAP_USED, + USELESS_ASREF, + USELESS_NONZERO_NEW_UNCHECKED, + VEC_RESIZE_TO_ZERO, + VERBOSE_FILE_READS, + WAKER_CLONE_WAKE, + WRONG_SELF_CONVENTION, + ZST_OFFSET, ]); /// Extracts a method call name, args, and `Span` of the method name. diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 19e9910dfe9d..0f04d90b70cf 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -94,9 +94,9 @@ declare_clippy_lint! { } declare_lint_pass!(LintPass => [ + SHORT_CIRCUIT_STATEMENT, USED_UNDERSCORE_BINDING, USED_UNDERSCORE_ITEMS, - SHORT_CIRCUIT_STATEMENT, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index f988323a8c13..e4a2befef77c 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -303,15 +303,15 @@ declare_clippy_lint! { } declare_lint_pass!(MiscEarlyLints => [ - UNNEEDED_FIELD_PATTERN, - MIXED_CASE_HEX_LITERALS, - UNSEPARATED_LITERAL_SUFFIX, - SEPARATED_LITERAL_SUFFIX, - ZERO_PREFIXED_LITERAL, BUILTIN_TYPE_SHADOW, + MIXED_CASE_HEX_LITERALS, + REDUNDANT_AT_REST_PATTERN, REDUNDANT_PATTERN, + SEPARATED_LITERAL_SUFFIX, + UNNEEDED_FIELD_PATTERN, UNNEEDED_WILDCARD_PATTERN, - REDUNDANT_AT_REST_PATTERN, + UNSEPARATED_LITERAL_SUFFIX, + ZERO_PREFIXED_LITERAL, ]); impl EarlyLintPass for MiscEarlyLints { diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index ddd4271960e1..d2d63920105a 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -79,7 +79,10 @@ declare_clippy_lint! { "whether an expression contains a diverging sub expression" } -declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVERGING_SUB_EXPRESSION]); +declare_lint_pass!(EvalOrderDependence => [ + DIVERGING_SUB_EXPRESSION, + MIXED_READ_WRITE_IN_EXPRESSION, +]); impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 98a9a98d281a..e4e898362b51 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -72,7 +72,7 @@ pub struct MutableKeyType<'tcx> { interior_mut: InteriorMut<'tcx>, } -impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]); +impl_lint_pass!(MutableKeyType<'_> => [MUTABLE_KEY_TYPE]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index c7c4976aeb7b..cca3f6ab24da 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -66,7 +66,9 @@ pub struct NeedlessBorrowsForGenericArgs<'tcx> { // `IntoIterator` for arrays requires Rust 1.53. msrv: Msrv, } -impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); +impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [ + NEEDLESS_BORROWS_FOR_GENERIC_ARGS, +]); impl NeedlessBorrowsForGenericArgs<'_> { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs index 021a11593f3a..6801ba0da3b0 100644 --- a/clippy_lints/src/needless_parens_on_range_literals.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -38,7 +38,9 @@ declare_clippy_lint! { "needless parenthesis on range literals can be removed" } -declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]); +declare_lint_pass!(NeedlessParensOnRangeLiterals => [ + NEEDLESS_PARENS_ON_RANGE_LITERALS, +]); fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool { snippet.starts_with('(') && snippet.ends_with(')') diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 701923cf6efc..c36fceea9172 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -79,7 +79,11 @@ pub struct NoEffect { local_bindings: Vec>, } -impl_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]); +impl_lint_pass!(NoEffect => [ + NO_EFFECT, + NO_EFFECT_UNDERSCORE_BINDING, + UNNECESSARY_OPERATION, +]); impl<'tcx> LateLintPass<'tcx> for NoEffect { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 3285023b34aa..b5780d22b5eb 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -108,7 +108,10 @@ declare_clippy_lint! { suspicious, "non-canonical implementation of `PartialOrd` on an `Ord` type" } -impl_lint_pass!(NonCanonicalImpls => [NON_CANONICAL_CLONE_IMPL, NON_CANONICAL_PARTIAL_ORD_IMPL]); +impl_lint_pass!(NonCanonicalImpls => [ + NON_CANONICAL_CLONE_IMPL, + NON_CANONICAL_PARTIAL_ORD_IMPL, +]); #[expect( clippy::struct_field_names, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 2fffc4244a73..a2db320f2ed5 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -256,7 +256,10 @@ pub struct NonCopyConst<'tcx> { freeze_tys: FxHashMap, IsFreeze>, } -impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); +impl_lint_pass!(NonCopyConst<'_> => [ + BORROW_INTERIOR_MUTABLE_CONST, + DECLARE_INTERIOR_MUTABLE_CONST, +]); impl<'tcx> NonCopyConst<'tcx> { pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 1961ac1516da..4d43a9b6f5ee 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -77,7 +77,11 @@ pub struct NonExpressiveNames { pub single_char_binding_names_threshold: u64, } -impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]); +impl_lint_pass!(NonExpressiveNames => [ + JUST_UNDERSCORES_AND_DIGITS, + MANY_SINGLE_CHAR_NAMES, + SIMILAR_NAMES, +]); impl NonExpressiveNames { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 784ea34bac58..ef81ae1fbd3b 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -162,7 +162,10 @@ declare_clippy_lint! { pedantic, "self receiver only used to recursively call method can be removed" } -impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION, SELF_ONLY_USED_IN_RECURSION]); +impl_lint_pass!(OnlyUsedInRecursion => [ + ONLY_USED_IN_RECURSION, + SELF_ONLY_USED_IN_RECURSION, +]); #[derive(Clone, Copy)] enum FnKind { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 75a6d57efb3c..68da15741152 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -909,32 +909,32 @@ impl Operators { impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, ARITHMETIC_SIDE_EFFECTS, - FLOAT_ARITHMETIC, ASSIGN_OP_PATTERN, - MISREFACTORED_ASSIGN_OP, BAD_BIT_MASK, - INEFFECTIVE_BIT_MASK, - VERBOSE_BIT_MASK, + CMP_OWNED, DOUBLE_COMPARISONS, - IMPOSSIBLE_COMPARISONS, - REDUNDANT_COMPARISONS, DURATION_SUBSEC, EQ_OP, - OP_REF, ERASING_OP, + FLOAT_ARITHMETIC, + FLOAT_CMP, + FLOAT_CMP_CONST, FLOAT_EQUALITY_WITHOUT_ABS, IDENTITY_OP, + IMPOSSIBLE_COMPARISONS, + INEFFECTIVE_BIT_MASK, INTEGER_DIVISION, - CMP_OWNED, - FLOAT_CMP, - FLOAT_CMP_CONST, - MODULO_ONE, + MANUAL_DIV_CEIL, + MANUAL_IS_MULTIPLE_OF, + MANUAL_MIDPOINT, + MISREFACTORED_ASSIGN_OP, MODULO_ARITHMETIC, + MODULO_ONE, NEEDLESS_BITWISE_BOOL, + OP_REF, + REDUNDANT_COMPARISONS, SELF_ASSIGNMENT, - MANUAL_MIDPOINT, - MANUAL_IS_MULTIPLE_OF, - MANUAL_DIV_CEIL, + VERBOSE_BIT_MASK, ]); impl<'tcx> LateLintPass<'tcx> for Operators { diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 57127e9d2298..cc147a4a84bd 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { "functions of type `Result<..>` that contain `panic!()` or assertion" } -declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); +declare_lint_pass!(PanicInResultFn => [PANIC_IN_RESULT_FN]); impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn check_fn( diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 43db0085f2e3..4c318f4a7a0e 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -93,7 +93,7 @@ declare_clippy_lint! { "usage of the `unreachable!` macro" } -impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]); +impl_lint_pass!(PanicUnimplemented => [PANIC, TODO, UNIMPLEMENTED, UNREACHABLE]); impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 6e9142b22e0e..5d31c8687efc 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -237,7 +237,10 @@ impl PassByRefOrValue { } } -impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]); +impl_lint_pass!(PassByRefOrValue => [ + LARGE_TYPES_PASSED_BY_VALUE, + TRIVIALLY_COPY_PASS_BY_REF, +]); impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index 68a34d459e0d..fab167667b93 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -26,7 +26,9 @@ declare_clippy_lint! { suspicious, "Checks for calls to `std::fs::Permissions.set_readonly` with argument `false`" } -declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALSE]); +declare_lint_pass!(PermissionsSetReadonlyFalse => [ + PERMISSIONS_SET_READONLY_FALSE, +]); impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 8446b6fbbea5..cedab93dfe8d 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -155,7 +155,7 @@ declare_clippy_lint! { "use `std::ptr::eq` when comparing raw pointers" } -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); +declare_lint_pass!(Ptr => [CMP_NULL, MUT_FROM_REF, PTR_ARG, PTR_EQ]); impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index e67ea1f5e370..9d9ef2e91a5d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -65,7 +65,7 @@ pub struct QuestionMark { inferred_ret_closure_stack: u16, } -impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); +impl_lint_pass!(QuestionMark => [MANUAL_LET_ELSE, QUESTION_MARK]); impl QuestionMark { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index e4c91b7efd2b..a2e12d836373 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -175,10 +175,10 @@ impl Ranges { } impl_lint_pass!(Ranges => [ - RANGE_PLUS_ONE, + MANUAL_RANGE_CONTAINS, RANGE_MINUS_ONE, + RANGE_PLUS_ONE, REVERSED_EMPTY_RANGES, - MANUAL_RANGE_CONTAINS, ]); impl<'tcx> LateLintPass<'tcx> for Ranges { diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index f2cf809d6012..03ed3a4076b9 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -66,7 +66,7 @@ declare_clippy_lint! { "slicing instead of dereferencing" } -declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]); +declare_lint_pass!(RedundantSlicing => [DEREF_BY_SLICING, REDUNDANT_SLICING]); static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range"); static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work"); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index d1fc228f4b35..c7d200089701 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -119,7 +119,7 @@ pub struct Regex { loop_stack: Vec<(OwnerId, Span)>, } -impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]); +impl_lint_pass!(Regex => [INVALID_REGEX, REGEX_CREATION_IN_LOOPS, TRIVIAL_REGEX]); impl<'tcx> LateLintPass<'tcx> for Regex { fn check_crate(&mut self, cx: &LateContext<'tcx>) { diff --git a/clippy_lints/src/returns/mod.rs b/clippy_lints/src/returns/mod.rs index 47c6332b9b81..eb84582593a4 100644 --- a/clippy_lints/src/returns/mod.rs +++ b/clippy_lints/src/returns/mod.rs @@ -115,7 +115,11 @@ declare_clippy_lint! { "using a return statement like `return Err(expr)?;` where removing it would suffice" } -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); +declare_lint_pass!(Return => [ + LET_AND_RETURN, + NEEDLESS_RETURN, + NEEDLESS_RETURN_WITH_QUESTION_MARK, +]); impl<'tcx> LateLintPass<'tcx> for Return { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 7fdea6bec510..42662c0769ea 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -111,7 +111,7 @@ pub(crate) struct Shadow { bindings: Vec<(FxHashMap>, LocalDefId)>, } -impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]); +impl_lint_pass!(Shadow => [SHADOW_REUSE, SHADOW_SAME, SHADOW_UNRELATED]); impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 8c4a50041e67..0cbb085c31bf 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -109,7 +109,11 @@ impl StdReexports { } } -impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]); +impl_lint_pass!(StdReexports => [ + ALLOC_INSTEAD_OF_CORE, + STD_INSTEAD_OF_ALLOC, + STD_INSTEAD_OF_CORE, +]); #[derive(Debug)] enum LintPoint { diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index e5347bf3e8f0..4c640c2ed541 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -82,7 +82,10 @@ impl StringPatterns { } } -impl_lint_pass!(StringPatterns => [MANUAL_PATTERN_CHAR_COMPARISON, SINGLE_CHAR_PATTERN]); +impl_lint_pass!(StringPatterns => [ + MANUAL_PATTERN_CHAR_COMPARISON, + SINGLE_CHAR_PATTERN, +]); const PATTERN_METHODS: [(Symbol, usize); 22] = [ (sym::contains, 0), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 47306949a699..1157df461ff5 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -246,7 +246,10 @@ declare_clippy_lint! { // Max length a b"foo" string can take const MAX_LENGTH_BYTE_STRING_LIT: usize = 32; -declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]); +declare_lint_pass!(StringLitAsBytes => [ + STRING_FROM_UTF8_AS_BYTES, + STRING_LIT_AS_BYTES, +]); impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index 0d809c17989d..cf0d2282f90f 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -64,7 +64,9 @@ declare_clippy_lint! { "groupings of binary operations that look suspiciously like typos" } -declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]); +declare_lint_pass!(SuspiciousOperationGroupings => [ + SUSPICIOUS_OPERATION_GROUPINGS, +]); impl EarlyLintPass for SuspiciousOperationGroupings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { diff --git a/clippy_lints/src/suspicious_trait_impl.rs b/clippy_lints/src/suspicious_trait_impl.rs index edb7600b7c06..694f1896a4d2 100644 --- a/clippy_lints/src/suspicious_trait_impl.rs +++ b/clippy_lints/src/suspicious_trait_impl.rs @@ -53,7 +53,10 @@ declare_clippy_lint! { "suspicious use of operators in impl of OpAssign trait" } -declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ASSIGN_IMPL]); +declare_lint_pass!(SuspiciousImpl => [ + SUSPICIOUS_ARITHMETIC_IMPL, + SUSPICIOUS_OP_ASSIGN_IMPL, +]); impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index c3cb2c09752f..66fdda46d060 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -75,7 +75,7 @@ declare_clippy_lint! { "`foo = bar; bar = foo` sequence" } -declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]); +declare_lint_pass!(Swap => [ALMOST_SWAPPED, MANUAL_SWAP]); impl<'tcx> LateLintPass<'tcx> for Swap { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index dbd4ec77fd5f..72a31cac162a 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -80,7 +80,10 @@ impl UncheckedTimeSubtraction { } } -impl_lint_pass!(UncheckedTimeSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_TIME_SUBTRACTION]); +impl_lint_pass!(UncheckedTimeSubtraction => [ + MANUAL_INSTANT_ELAPSED, + UNCHECKED_TIME_SUBTRACTION, +]); impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 352b8526b021..0a6b999775f4 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -100,7 +100,10 @@ impl TraitBounds { } } -impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]); +impl_lint_pass!(TraitBounds => [ + TRAIT_DUPLICATION_IN_BOUNDS, + TYPE_REPETITION_IN_BOUNDS, +]); impl<'tcx> LateLintPass<'tcx> for TraitBounds { fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) { diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 5fda388259a6..49538a19c66a 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -470,20 +470,20 @@ pub struct Transmute { } impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, - TRANSMUTE_PTR_TO_REF, - TRANSMUTE_PTR_TO_PTR, - USELESS_TRANSMUTE, - WRONG_TRANSMUTE, + EAGER_TRANSMUTE, + MISSING_TRANSMUTE_ANNOTATIONS, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_NON_ZERO, - UNSOUND_COLLECTION_TRANSMUTE, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + TRANSMUTE_NULL_TO_FN, + TRANSMUTE_PTR_TO_PTR, + TRANSMUTE_PTR_TO_REF, TRANSMUTE_UNDEFINED_REPR, TRANSMUTING_NULL, - TRANSMUTE_NULL_TO_FN, - EAGER_TRANSMUTE, - MISSING_TRANSMUTE_ANNOTATIONS, + UNSOUND_COLLECTION_TRANSMUTE, + USELESS_TRANSMUTE, + WRONG_TRANSMUTE, ]); impl Transmute { pub fn new(conf: &'static Conf) -> Self { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index ccb027f77bf5..6b239cfbd8ce 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -398,16 +398,16 @@ pub struct Types { } impl_lint_pass!(Types => [ + BORROWED_BOX, BOX_COLLECTION, - VEC_BOX, - OPTION_OPTION, LINKEDLIST, - BORROWED_BOX, - REDUNDANT_ALLOCATION, + OPTION_OPTION, + OWNED_COW, RC_BUFFER, RC_MUTEX, + REDUNDANT_ALLOCATION, TYPE_COMPLEXITY, - OWNED_COW + VEC_BOX, ]); impl<'tcx> LateLintPass<'tcx> for Types { diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 9afa9d65c261..138c42c4b484 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -105,7 +105,10 @@ impl UndocumentedUnsafeBlocks { } } -impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); +impl_lint_pass!(UndocumentedUnsafeBlocks => [ + UNDOCUMENTED_UNSAFE_BLOCKS, + UNNECESSARY_SAFETY_COMMENT, +]); impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index 79571b0409d2..bef8b8fb4ee0 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -72,7 +72,11 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [ + INVISIBLE_CHARACTERS, + NON_ASCII_LITERAL, + UNICODE_NOT_NFC, +]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index 4ffcc247acf6..13dd9d7b72c0 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -101,7 +101,7 @@ pub struct UnitTypes { format_args: FormatArgsStorage, } -impl_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); +impl_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_ARG, UNIT_CMP]); impl UnitTypes { pub fn new(format_args: FormatArgsStorage) -> Self { diff --git a/clippy_lints/src/unnecessary_map_on_constructor.rs b/clippy_lints/src/unnecessary_map_on_constructor.rs index 94b1a34455ff..46d572e09882 100644 --- a/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -31,7 +31,9 @@ declare_clippy_lint! { complexity, "using `map`/`map_err` on `Option` or `Result` constructors" } -declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTOR]); +declare_lint_pass!(UnnecessaryMapOnConstructor => [ + UNNECESSARY_MAP_ON_CONSTRUCTOR, +]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 0388450c9f7e..bcc10cbe7d7a 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -30,7 +30,9 @@ declare_clippy_lint! { style, "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" } -declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]); +declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [ + UNNECESSARY_OWNED_EMPTY_STRINGS, +]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index f26647fa3485..aafd1153f933 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -56,7 +56,7 @@ declare_clippy_lint! { "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" } -impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); +impl_lint_pass!(UnwrapInResult => [UNWRAP_IN_RESULT]); #[derive(Clone, Copy, Eq, PartialEq)] enum OptionOrResult { diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs index d17b3df99216..429228912462 100644 --- a/clippy_lints/src/visibility.rs +++ b/clippy_lints/src/visibility.rs @@ -74,7 +74,11 @@ declare_clippy_lint! { restriction, "disallows usage of `pub(in )` with `in`" } -declare_lint_pass!(Visibility => [NEEDLESS_PUB_SELF, PUB_WITH_SHORTHAND, PUB_WITHOUT_SHORTHAND]); +declare_lint_pass!(Visibility => [ + NEEDLESS_PUB_SELF, + PUB_WITHOUT_SHORTHAND, + PUB_WITH_SHORTHAND, +]); impl EarlyLintPass for Visibility { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index c55c5ec2f51a..645c2829b531 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -255,15 +255,15 @@ impl Write { } impl_lint_pass!(Write => [ - PRINT_WITH_NEWLINE, PRINTLN_EMPTY_STRING, - PRINT_STDOUT, + PRINT_LITERAL, PRINT_STDERR, + PRINT_STDOUT, + PRINT_WITH_NEWLINE, USE_DEBUG, - PRINT_LITERAL, - WRITE_WITH_NEWLINE, WRITELN_EMPTY_STRING, WRITE_LITERAL, + WRITE_WITH_NEWLINE, ]); impl<'tcx> LateLintPass<'tcx> for Write { From 47ae28116ed2be33eb33d61d8c587461beb82fc9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 28 Oct 2025 08:47:38 -0400 Subject: [PATCH 10/10] `clippy_dev`: Sort all lint and lint pass declarations. --- clippy_dev/src/fmt.rs | 33 +- clippy_dev/src/generate.rs | 60 +- clippy_dev/src/parse.rs | 26 +- clippy_dev/src/utils.rs | 21 +- clippy_lints/src/absolute_paths.rs | 1 + clippy_lints/src/almost_complete_range.rs | 1 + clippy_lints/src/approx_const.rs | 4 +- clippy_lints/src/arc_with_non_send_sync.rs | 1 + clippy_lints/src/asm_syntax.rs | 56 +- clippy_lints/src/assertions_on_constants.rs | 1 + clippy_lints/src/assigning_clones.rs | 4 +- clippy_lints/src/attrs/mod.rs | 526 +- clippy_lints/src/await_holding_invalid.rs | 74 +- clippy_lints/src/bool_to_int_with_if.rs | 1 + clippy_lints/src/booleans.rs | 4 +- clippy_lints/src/byte_char_slices.rs | 1 + clippy_lints/src/cargo/mod.rs | 154 +- clippy_lints/src/casts/mod.rs | 884 +-- clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/cloned_ref_to_slice_refs.rs | 4 +- clippy_lints/src/coerce_container_to_any.rs | 1 + clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/collapsible_if.rs | 64 +- clippy_lints/src/collection_is_never_read.rs | 1 + clippy_lints/src/crate_in_macro_def.rs | 1 + clippy_lints/src/dbg_macro.rs | 4 +- clippy_lints/src/default.rs | 4 +- .../src/default_constructed_unit_structs.rs | 1 + .../src/default_instead_of_iter_empty.rs | 1 + .../src/default_union_representation.rs | 1 + clippy_lints/src/dereference.rs | 46 +- clippy_lints/src/derivable_impls.rs | 4 +- clippy_lints/src/derive/mod.rs | 124 +- clippy_lints/src/disallowed_macros.rs | 4 +- clippy_lints/src/disallowed_methods.rs | 4 +- clippy_lints/src/disallowed_names.rs | 4 +- clippy_lints/src/disallowed_script_idents.rs | 4 +- clippy_lints/src/disallowed_types.rs | 4 +- clippy_lints/src/doc/mod.rs | 786 +-- clippy_lints/src/drop_forget_ref.rs | 4 +- clippy_lints/src/duplicate_mod.rs | 4 +- clippy_lints/src/empty_drop.rs | 1 + clippy_lints/src/empty_line_after.rs | 80 +- clippy_lints/src/empty_with_brackets.rs | 64 +- clippy_lints/src/endian_bytes.rs | 36 +- clippy_lints/src/error_impl_error.rs | 1 + clippy_lints/src/escape.rs | 4 +- clippy_lints/src/excessive_bools.rs | 84 +- clippy_lints/src/excessive_nesting.rs | 1 + clippy_lints/src/explicit_write.rs | 4 +- .../src/extra_unused_type_parameters.rs | 4 +- clippy_lints/src/float_literal.rs | 4 +- clippy_lints/src/format.rs | 4 +- clippy_lints/src/format_args.rs | 96 +- clippy_lints/src/format_impl.rs | 82 +- clippy_lints/src/format_push_string.rs | 1 + clippy_lints/src/formatting.rs | 120 +- clippy_lints/src/four_forward_slashes.rs | 1 + clippy_lints/src/from_over_into.rs | 4 +- clippy_lints/src/from_raw_with_void_ptr.rs | 1 + clippy_lints/src/functions/mod.rs | 506 +- clippy_lints/src/if_then_some_else_none.rs | 4 +- clippy_lints/src/ifs/mod.rs | 124 +- clippy_lints/src/ignored_unit_patterns.rs | 1 + clippy_lints/src/implicit_saturating_add.rs | 1 + clippy_lints/src/implicit_saturating_sub.rs | 8 +- clippy_lints/src/implied_bounds_in_impls.rs | 1 + clippy_lints/src/incompatible_msrv.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 8 +- clippy_lints/src/index_refutable_slice.rs | 4 +- clippy_lints/src/indexing_slicing.rs | 60 +- clippy_lints/src/infallible_try_from.rs | 1 + clippy_lints/src/item_name_repetitions.rs | 75 +- clippy_lints/src/iter_without_into_iter.rs | 56 +- clippy_lints/src/large_const_arrays.rs | 4 +- clippy_lints/src/large_enum_variant.rs | 4 +- clippy_lints/src/large_futures.rs | 4 +- clippy_lints/src/large_include_file.rs | 4 +- clippy_lints/src/large_stack_arrays.rs | 4 +- clippy_lints/src/large_stack_frames.rs | 4 +- clippy_lints/src/legacy_numeric_constants.rs | 5 +- clippy_lints/src/len_zero.rs | 54 +- clippy_lints/src/let_underscore.rs | 64 +- clippy_lints/src/let_with_type_underscore.rs | 1 + clippy_lints/src/lifetimes.rs | 82 +- clippy_lints/src/literal_representation.rs | 122 +- clippy_lints/src/loops/mod.rs | 838 +-- clippy_lints/src/macro_metavars_in_unsafe.rs | 1 + clippy_lints/src/macro_use.rs | 4 +- clippy_lints/src/main_recursion.rs | 4 +- clippy_lints/src/manual_bits.rs | 4 +- clippy_lints/src/manual_clamp.rs | 1 + clippy_lints/src/manual_float_methods.rs | 34 +- clippy_lints/src/manual_hash_one.rs | 4 +- clippy_lints/src/manual_is_ascii_check.rs | 1 + clippy_lints/src/manual_is_power_of_two.rs | 4 +- clippy_lints/src/manual_main_separator_str.rs | 4 +- clippy_lints/src/manual_non_exhaustive.rs | 4 +- clippy_lints/src/manual_option_as_slice.rs | 4 +- clippy_lints/src/manual_range_patterns.rs | 1 + clippy_lints/src/manual_rem_euclid.rs | 4 +- clippy_lints/src/manual_retain.rs | 4 +- .../src/manual_slice_size_calculation.rs | 1 + clippy_lints/src/manual_string_new.rs | 1 + clippy_lints/src/manual_strip.rs | 4 +- clippy_lints/src/matches/mod.rs | 1102 ++-- clippy_lints/src/mem_replace.rs | 50 +- clippy_lints/src/methods/mod.rs | 5274 ++++++++--------- clippy_lints/src/min_ident_chars.rs | 1 + clippy_lints/src/misc.rs | 42 +- clippy_lints/src/misc_early/mod.rs | 278 +- .../src/mismatching_type_param_order.rs | 1 + .../src/missing_asserts_for_indexing.rs | 1 + .../src/missing_const_for_thread_local.rs | 4 +- clippy_lints/src/missing_doc.rs | 4 +- .../src/missing_enforced_import_rename.rs | 4 +- clippy_lints/src/missing_fields_in_debug.rs | 1 + clippy_lints/src/missing_inline.rs | 4 +- clippy_lints/src/missing_trait_methods.rs | 1 + .../src/mixed_read_write_in_expression.rs | 56 +- .../src/multiple_unsafe_ops_per_block.rs | 1 + clippy_lints/src/mut_key.rs | 4 +- clippy_lints/src/needless_bool.rs | 1 + .../src/needless_borrows_for_generic_args.rs | 8 +- clippy_lints/src/needless_else.rs | 1 + clippy_lints/src/needless_if.rs | 1 + clippy_lints/src/needless_late_init.rs | 1 + clippy_lints/src/needless_maybe_sized.rs | 1 + clippy_lints/src/needless_pass_by_ref_mut.rs | 4 +- clippy_lints/src/new_without_default.rs | 4 +- clippy_lints/src/no_effect.rs | 12 +- clippy_lints/src/no_mangle_with_rust_abi.rs | 1 + clippy_lints/src/non_canonical_impls.rs | 2 + clippy_lints/src/non_copy_const.rs | 86 +- clippy_lints/src/non_expressive_names.rs | 56 +- .../src/non_send_fields_in_send_ty.rs | 4 +- clippy_lints/src/non_std_lazy_statics.rs | 4 +- clippy_lints/src/nonstandard_macro_braces.rs | 4 +- clippy_lints/src/only_used_in_recursion.rs | 1 + clippy_lints/src/operators/mod.rs | 736 +-- clippy_lints/src/panic_unimplemented.rs | 30 +- clippy_lints/src/partial_pub_fields.rs | 1 + clippy_lints/src/partialeq_to_none.rs | 1 + clippy_lints/src/pass_by_ref_or_value.rs | 74 +- .../src/permissions_set_readonly_false.rs | 1 + clippy_lints/src/ptr.rs | 72 +- clippy_lints/src/pub_underscore_fields.rs | 4 +- clippy_lints/src/pub_use.rs | 1 + clippy_lints/src/question_mark.rs | 4 +- clippy_lints/src/ranges.rs | 122 +- clippy_lints/src/raw_strings.rs | 42 +- clippy_lints/src/rc_clone_in_vec_init.rs | 1 + clippy_lints/src/read_zero_byte_vec.rs | 1 + clippy_lints/src/redundant_async_block.rs | 1 + clippy_lints/src/redundant_field_names.rs | 4 +- clippy_lints/src/redundant_locals.rs | 1 + clippy_lints/src/redundant_pub_crate.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 48 +- .../src/redundant_static_lifetimes.rs | 4 +- .../src/redundant_type_annotations.rs | 1 + clippy_lints/src/ref_patterns.rs | 1 + clippy_lints/src/regex.rs | 64 +- clippy_lints/src/replace_box.rs | 1 + .../src/reserve_after_initialization.rs | 1 + clippy_lints/src/semicolon_block.rs | 2 + clippy_lints/src/shadow.rs | 50 +- clippy_lints/src/single_call_fn.rs | 1 + .../src/single_component_path_imports.rs | 4 +- clippy_lints/src/single_range_in_vec_init.rs | 1 + clippy_lints/src/size_of_ref.rs | 1 + clippy_lints/src/std_instead_of_core.rs | 54 +- clippy_lints/src/string_patterns.rs | 10 +- clippy_lints/src/strings.rs | 177 +- .../src/suspicious_xor_used_as_pow.rs | 1 + clippy_lints/src/swap.rs | 52 +- clippy_lints/src/swap_ptr_to_ref.rs | 1 + clippy_lints/src/temporary_assignment.rs | 4 +- clippy_lints/src/time_subtraction.rs | 10 +- clippy_lints/src/trailing_empty_array.rs | 1 + clippy_lints/src/trait_bounds.rs | 56 +- clippy_lints/src/transmute/mod.rs | 402 +- clippy_lints/src/tuple_array_conversions.rs | 1 + clippy_lints/src/types/mod.rs | 308 +- clippy_lints/src/unconditional_recursion.rs | 4 +- .../src/undocumented_unsafe_blocks.rs | 11 +- clippy_lints/src/unit_types/mod.rs | 44 +- clippy_lints/src/unnecessary_box_returns.rs | 4 +- .../src/unnecessary_map_on_constructor.rs | 1 + .../src/unnecessary_owned_empty_strings.rs | 1 + clippy_lints/src/unnecessary_semicolon.rs | 4 +- .../src/unnecessary_struct_initialization.rs | 1 + clippy_lints/src/unnecessary_wraps.rs | 4 +- clippy_lints/src/unnested_or_patterns.rs | 4 +- clippy_lints/src/unused_async.rs | 4 +- clippy_lints/src/unused_result_ok.rs | 1 + clippy_lints/src/unused_rounding.rs | 1 + clippy_lints/src/unused_self.rs | 4 +- clippy_lints/src/unused_trait_names.rs | 4 +- clippy_lints/src/unwrap.rs | 56 +- clippy_lints/src/upper_case_acronyms.rs | 4 +- clippy_lints/src/use_self.rs | 4 +- clippy_lints/src/useless_conversion.rs | 4 +- clippy_lints/src/visibility.rs | 3 + clippy_lints/src/volatile_composites.rs | 1 + clippy_lints/src/wildcard_imports.rs | 4 +- clippy_lints/src/write.rs | 170 +- clippy_lints/src/zombie_processes.rs | 1 + 207 files changed, 7770 insertions(+), 7593 deletions(-) diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index f4ee600dde04..d13f966b19c3 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -1,7 +1,9 @@ +use crate::generate::gen_sorted_lints_file; use crate::new_parse_cx; +use crate::parse::VecBuf; use crate::utils::{ - ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, slice_groups, - split_args_for_threads, walk_dir_no_dot_or_target, + ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads, + walk_dir_no_dot_or_target, }; use itertools::Itertools; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -334,19 +336,24 @@ pub fn run(update_mode: UpdateMode) { } new_parse_cx(|cx| { - let data = cx.parse_lint_decls(); + let mut data = cx.parse_lint_decls(); + let (mut lints, passes) = data.split_by_lint_file(); let mut updater = FileUpdater::default(); + let mut ranges = VecBuf::with_capacity(256); - for passes in slice_groups(&data.lint_passes, |head, tail| { - tail.iter().take_while(|&x| x.path == head.path).count() - }) { - updater.update_file_checked("cargo dev fmt", update_mode, &passes[0].path, &mut |_, src, dst| { - let pos = passes.iter().fold(0u32, |start, pass| { - dst.push_str(&src[start as usize..pass.decl_range.start as usize]); - pass.gen_mac(dst); - pass.decl_range.end - }); - dst.push_str(&src[pos as usize..]); + for passes in passes { + let path = passes[0].path.clone(); + let mut lints = lints.remove(&*path); + let lints = lints.as_deref_mut().unwrap_or_default(); + updater.update_file_checked("cargo dev fmt", update_mode, &path, &mut |_, src, dst| { + gen_sorted_lints_file(src, dst, lints, passes, &mut ranges); + UpdateStatus::from_changed(src != dst) + }); + } + + for (&path, lints) in &mut lints { + updater.update_file_checked("cargo dev fmt", update_mode, path, &mut |_, src, dst| { + gen_sorted_lints_file(src, dst, lints, &mut [], &mut ranges); UpdateStatus::from_changed(src != dst) }); } diff --git a/clippy_dev/src/generate.rs b/clippy_dev/src/generate.rs index afb328a2eb94..324afd44af52 100644 --- a/clippy_dev/src/generate.rs +++ b/clippy_dev/src/generate.rs @@ -1,6 +1,7 @@ use crate::parse::cursor::Cursor; -use crate::parse::{Lint, LintData, LintPass}; +use crate::parse::{Lint, LintData, LintPass, VecBuf}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; +use core::range::Range; use itertools::Itertools; use std::collections::HashSet; use std::fmt::Write; @@ -227,3 +228,60 @@ fn write_list<'a>( let _ = write!(dst, "{}", items.format(", ")); } } + +/// Generates the contents of a lint's source file with all the lint and lint pass +/// declarations sorted. +pub fn gen_sorted_lints_file( + src: &str, + dst: &mut String, + lints: &mut [(&str, Range)], + passes: &mut [LintPass<'_>], + ranges: &mut VecBuf>, +) { + ranges.with(|ranges| { + ranges.extend(lints.iter().map(|&(_, x)| x)); + ranges.extend(passes.iter().map(|x| x.decl_range)); + ranges.sort_unstable_by_key(|x| x.start); + + lints.sort_unstable_by_key(|&(x, _)| x); + passes.sort_by_key(|x| x.name); + + let mut ranges = ranges.iter(); + let pos = if let Some(range) = ranges.next() { + dst.push_str(&src[..range.start as usize]); + for &(_, range) in &*lints { + dst.push_str(&src[range.start as usize..range.end as usize]); + dst.push_str("\n\n"); + } + for pass in passes { + pass.gen_mac(dst); + dst.push_str("\n\n"); + } + range.end + } else { + dst.push_str(src); + return; + }; + + let pos = ranges.fold(pos, |start, range| { + let s = &src[start as usize..range.start as usize]; + dst.push_str(if s.trim_start().is_empty() { + // Only whitespace between this and the previous item. No need to keep that. + "" + } else if src[..pos as usize].ends_with("\n\n") + && let Some(s) = s.strip_prefix("\n\n") + { + // Empty line before and after. Remove one of them. + s + } else { + // Remove only full lines unless something is in the way. + s.strip_prefix('\n').unwrap_or(s) + }); + range.end + }); + + // Since we always generate an empty line at the end, make sure to always skip it. + let s = &src[pos as usize..]; + dst.push_str(s.strip_prefix('\n').map_or(s, |s| s.strip_prefix('\n').unwrap_or(s))); + }); +} diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index d705d39aaa24..7f7fda765e32 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -1,7 +1,7 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; -use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; +use crate::utils::{ErrAction, File, Scoped, expect_action, slice_groups_mut, walk_dir_no_dot_or_target}; use core::fmt::{self, Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; @@ -190,6 +190,30 @@ pub struct LintData<'cx> { pub lints: FxHashMap<&'cx str, Lint<'cx>>, pub lint_passes: Vec>, } +impl<'cx> LintData<'cx> { + #[expect(clippy::type_complexity)] + pub fn split_by_lint_file<'s>( + &'s mut self, + ) -> ( + FxHashMap<&'s Path, Vec<(&'s str, Range)>>, + impl Iterator]>, + ) { + #[expect(clippy::default_trait_access)] + let mut lints = FxHashMap::with_capacity_and_hasher(500, Default::default()); + for (&name, lint) in &self.lints { + if let Lint::Active(lint) = lint { + lints + .entry(&*lint.path) + .or_insert_with(|| Vec::with_capacity(8)) + .push((name, lint.declaration_range)); + } + } + let passes = slice_groups_mut(&mut self.lint_passes, |head, tail| { + tail.iter().take_while(|&x| x.path == head.path).count() + }); + (lints, passes) + } +} impl<'cx> ParseCxImpl<'cx> { /// Finds and parses all lint declarations. diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 98a3458d6c2a..1f931140467e 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -1,5 +1,6 @@ use core::fmt::{self, Display}; use core::marker::PhantomData; +use core::mem; use core::num::NonZero; use core::ops::{Deref, DerefMut}; use core::range::Range; @@ -601,23 +602,25 @@ pub fn walk_dir_no_dot_or_target(p: impl AsRef) -> impl Iterator( - slice: &'a [T], - split_idx: impl FnMut(&'a T, &'a [T]) -> usize, -) -> impl Iterator { +pub fn slice_groups_mut( + slice: &mut [T], + split_idx: impl FnMut(&T, &[T]) -> usize, +) -> impl Iterator { struct I<'a, T, F> { - slice: &'a [T], + slice: &'a mut [T], split_idx: F, } - impl<'a, T, F: FnMut(&'a T, &'a [T]) -> usize> Iterator for I<'a, T, F> { - type Item = &'a [T]; + impl<'a, T, F: FnMut(&T, &[T]) -> usize> Iterator for I<'a, T, F> { + type Item = &'a mut [T]; fn next(&mut self) -> Option { let (head, tail) = self.slice.split_first()?; - if let Some((head, tail)) = self.slice.split_at_checked((self.split_idx)(head, tail) + 1) { + let idx = (self.split_idx)(head, tail) + 1; + // `mem::take` makes it so `self.slice` isn't reborrowed. + if let Some((head, tail)) = mem::take(&mut self.slice).split_at_mut_checked(idx) { self.slice = tail; Some(head) } else { - self.slice = &[]; + self.slice = &mut []; None } } diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index 1af6d448a93c..fd515939dfb8 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -52,6 +52,7 @@ declare_clippy_lint! { restriction, "checks for usage of an item without a `use` statement" } + impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 4f55968d5625..258970393023 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -28,6 +28,7 @@ declare_clippy_lint! { suspicious, "almost complete range" } + impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]); pub struct AlmostCompleteRange { diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index a3710ca51655..2ea921e5d461 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -39,6 +39,8 @@ declare_clippy_lint! { "the approximate of a known float constant (in `std::fXX::consts`)" } +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); + // Tuples are of the form (constant, name, min_digits, msrv) const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ (f64::E, "E", 4, None), @@ -111,8 +113,6 @@ impl ApproxConstant { } } -impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); - fn count_digits_after_dot(input: &str) -> usize { input .char_indices() diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index acfdfa65baed..e449b06199d3 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -39,6 +39,7 @@ declare_clippy_lint! { suspicious, "using `Arc` with a type that does not implement `Send` and `Sync`" } + declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs index 69a8eb7d94e7..3c5cf74d5a17 100644 --- a/clippy_lints/src/asm_syntax.rs +++ b/clippy_lints/src/asm_syntax.rs @@ -59,10 +59,10 @@ fn check_asm_syntax( declare_clippy_lint! { /// ### What it does - /// Checks for usage of Intel x86 assembly syntax. + /// Checks for usage of AT&T x86 assembly syntax. /// /// ### Why restrict this? - /// To enforce consistent use of AT&T x86 assembly syntax. + /// To enforce consistent use of Intel x86 assembly syntax. /// /// ### Example /// @@ -71,7 +71,7 @@ declare_clippy_lint! { /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; - /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); /// # } /// ``` /// Use instead: @@ -80,37 +80,21 @@ declare_clippy_lint! { /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; - /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); /// # } /// ``` #[clippy::version = "1.49.0"] - pub INLINE_ASM_X86_INTEL_SYNTAX, + pub INLINE_ASM_X86_ATT_SYNTAX, restriction, - "prefer AT&T x86 assembly syntax" -} - -declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); - -impl EarlyLintPass for InlineAsmX86IntelSyntax { - fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let ExprKind::InlineAsm(inline_asm) = &expr.kind { - check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); - } - } - - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if let ItemKind::GlobalAsm(inline_asm) = &item.kind { - check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); - } - } + "prefer Intel x86 assembly syntax" } declare_clippy_lint! { /// ### What it does - /// Checks for usage of AT&T x86 assembly syntax. + /// Checks for usage of Intel x86 assembly syntax. /// /// ### Why restrict this? - /// To enforce consistent use of Intel x86 assembly syntax. + /// To enforce consistent use of AT&T x86 assembly syntax. /// /// ### Example /// @@ -119,7 +103,7 @@ declare_clippy_lint! { /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; - /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); /// # } /// ``` /// Use instead: @@ -128,17 +112,33 @@ declare_clippy_lint! { /// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] /// # unsafe { let ptr = "".as_ptr(); /// # use std::arch::asm; - /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); /// # } /// ``` #[clippy::version = "1.49.0"] - pub INLINE_ASM_X86_ATT_SYNTAX, + pub INLINE_ASM_X86_INTEL_SYNTAX, restriction, - "prefer Intel x86 assembly syntax" + "prefer AT&T x86 assembly syntax" } declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::InlineAsm(inline_asm) = &expr.kind { + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel); + } + } + + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::GlobalAsm(inline_asm) = &item.kind { + check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel); + } + } +} + impl EarlyLintPass for InlineAsmX86AttSyntax { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::InlineAsm(inline_asm) = &expr.kind { diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 2586c89bc868..1ca1814088fc 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -32,6 +32,7 @@ declare_clippy_lint! { } impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); + pub struct AssertionsOnConstants { msrv: Msrv, } diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index efce23d13a38..60bc9b2b5b85 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -53,6 +53,8 @@ declare_clippy_lint! { "assigning the result of cloning may be inefficient" } +impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); + pub struct AssigningClones { msrv: Msrv, } @@ -63,8 +65,6 @@ impl AssigningClones { } } -impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]); - impl<'tcx> LateLintPass<'tcx> for AssigningClones { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = e.kind diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index d57b7e59cd15..5377b5e427ba 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -25,106 +25,60 @@ use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait declare_clippy_lint! { /// ### What it does - /// Checks for items annotated with `#[inline(always)]`, - /// unless the annotated function is empty or simply panics. - /// - /// ### Why is this bad? - /// While there are valid uses of this annotation (and once - /// you know when to use it, by all means `allow` this lint), it's a common - /// newbie-mistake to pepper one's code with it. - /// - /// As a rule of thumb, before slapping `#[inline(always)]` on a function, - /// measure if that additional function call really affects your runtime profile - /// sufficiently to make up for the increase in compile time. - /// - /// ### Known problems - /// False positives, big time. This lint is meant to be - /// deactivated by everyone doing serious performance work. This means having - /// done the measurement. - /// - /// ### Example - /// ```ignore - /// #[inline(always)] - /// fn not_quite_hot_code(..) { ... } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub INLINE_ALWAYS, - pedantic, - "use of `#[inline(always)]`" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `extern crate` and `use` items annotated with - /// lint attributes. - /// - /// This lint permits lint attributes for lints emitted on the items themself. - /// For `use` items these lints are: - /// * ambiguous_glob_reexports - /// * dead_code - /// * deprecated - /// * hidden_glob_reexports - /// * unreachable_pub - /// * unused - /// * unused_braces - /// * unused_import_braces - /// * clippy::disallowed_types - /// * clippy::enum_glob_use - /// * clippy::macro_use_imports - /// * clippy::module_name_repetitions - /// * clippy::redundant_pub_crate - /// * clippy::single_component_path_imports - /// * clippy::unsafe_removed_from_name - /// * clippy::wildcard_imports + /// Checks for usage of the `#[allow]` attribute and suggests replacing it with + /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) /// - /// For `extern crate` items these lints are: - /// * `unused_imports` on items with `#[macro_use]` + /// This lint only warns outer attributes (`#[allow]`), as inner attributes + /// (`#![allow]`) are usually used to enable or disable lints on a global scale. /// /// ### Why is this bad? - /// Lint attributes have no effect on crate imports. Most - /// likely a `!` was forgotten. + /// `#[expect]` attributes suppress the lint emission, but emit a warning, if + /// the expectation is unfulfilled. This can be useful to be notified when the + /// lint is no longer triggered. /// /// ### Example - /// ```ignore - /// #[deny(dead_code)] - /// extern crate foo; - /// #[forbid(dead_code)] - /// use foo::bar; + /// ```rust,ignore + /// #[allow(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + /// } /// ``` - /// /// Use instead: /// ```rust,ignore - /// #[allow(unused_imports)] - /// use foo::baz; - /// #[allow(unused_imports)] - /// #[macro_use] - /// extern crate baz; + /// #[expect(unused_mut)] + /// fn foo() -> usize { + /// let mut a = Vec::new(); + /// a.len() + /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub USELESS_ATTRIBUTE, - correctness, - "use of lint attributes on `extern crate` items" + #[clippy::version = "1.70.0"] + pub ALLOW_ATTRIBUTES, + restriction, + "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." } declare_clippy_lint! { /// ### What it does - /// Checks for `#[deprecated]` annotations with a `since` - /// field that is not a valid semantic version. Also allows "TBD" to signal - /// future deprecation. + /// Checks for attributes that allow lints without a reason. /// - /// ### Why is this bad? - /// For checking the version of the deprecation, it must be - /// a valid semver. Failing that, the contained information is useless. + /// ### Why restrict this? + /// Justifying each `allow` helps readers understand the reasoning, + /// and may allow removing `allow` attributes if their purpose is obsolete. /// /// ### Example /// ```no_run - /// #[deprecated(since = "forever")] - /// fn something_else() { /* ... */ } + /// #![allow(clippy::some_lint)] /// ``` - #[clippy::version = "pre 1.29.0"] - pub DEPRECATED_SEMVER, - correctness, - "use of `#[deprecated(since = \"x\")]` where x is not semver" + /// + /// Use instead: + /// ```no_run + /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] + /// ``` + #[clippy::version = "1.61.0"] + pub ALLOW_ATTRIBUTES_WITHOUT_REASON, + restriction, + "ensures that all `allow` and `expect` attributes have a reason" } declare_clippy_lint! { @@ -183,95 +137,191 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for attributes that allow lints without a reason. + /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for + /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with + /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. /// - /// ### Why restrict this? - /// Justifying each `allow` helps readers understand the reasoning, - /// and may allow removing `allow` attributes if their purpose is obsolete. + /// ### Why is this bad? + /// This feature has been deprecated for years and shouldn't be used anymore. /// /// ### Example /// ```no_run - /// #![allow(clippy::some_lint)] + /// #[cfg(feature = "cargo-clippy")] + /// struct Bar; /// ``` /// /// Use instead: /// ```no_run - /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] + /// #[cfg(clippy)] + /// struct Bar; /// ``` - #[clippy::version = "1.61.0"] - pub ALLOW_ATTRIBUTES_WITHOUT_REASON, - restriction, - "ensures that all `allow` and `expect` attributes have a reason" + #[clippy::version = "1.78.0"] + pub DEPRECATED_CLIPPY_CFG_ATTR, + suspicious, + "usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`" } declare_clippy_lint! { /// ### What it does - /// Checks for usage of the `#[allow]` attribute and suggests replacing it with - /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) + /// Checks for `#[deprecated]` annotations with a `since` + /// field that is not a valid semantic version. Also allows "TBD" to signal + /// future deprecation. /// - /// This lint only warns outer attributes (`#[allow]`), as inner attributes - /// (`#![allow]`) are usually used to enable or disable lints on a global scale. + /// ### Why is this bad? + /// For checking the version of the deprecation, it must be + /// a valid semver. Failing that, the contained information is useless. + /// + /// ### Example + /// ```no_run + /// #[deprecated(since = "forever")] + /// fn something_else() { /* ... */ } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DEPRECATED_SEMVER, + correctness, + "use of `#[deprecated(since = \"x\")]` where x is not semver" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for attributes that appear two or more times. /// /// ### Why is this bad? - /// `#[expect]` attributes suppress the lint emission, but emit a warning, if - /// the expectation is unfulfilled. This can be useful to be notified when the - /// lint is no longer triggered. + /// Repeating an attribute on the same item (or globally on the same crate) + /// is unnecessary and doesn't have an effect. /// /// ### Example - /// ```rust,ignore - /// #[allow(unused_mut)] - /// fn foo() -> usize { - /// let mut a = Vec::new(); - /// a.len() + /// ```no_run + /// #[allow(dead_code)] + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// #[allow(dead_code)] + /// fn foo() {} + /// ``` + #[clippy::version = "1.79.0"] + pub DUPLICATED_ATTRIBUTES, + suspicious, + "duplicated attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for ignored tests without messages. + /// + /// ### Why is this bad? + /// The reason for ignoring the test may not be obvious. + /// + /// ### Example + /// ```no_run + /// #[test] + /// #[ignore] + /// fn test() {} + /// ``` + /// Use instead: + /// ```no_run + /// #[test] + /// #[ignore = "Some good reason"] + /// fn test() {} + /// ``` + #[clippy::version = "1.88.0"] + pub IGNORE_WITHOUT_REASON, + pedantic, + "ignored tests without messages" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for items annotated with `#[inline(always)]`, + /// unless the annotated function is empty or simply panics. + /// + /// ### Why is this bad? + /// While there are valid uses of this annotation (and once + /// you know when to use it, by all means `allow` this lint), it's a common + /// newbie-mistake to pepper one's code with it. + /// + /// As a rule of thumb, before slapping `#[inline(always)]` on a function, + /// measure if that additional function call really affects your runtime profile + /// sufficiently to make up for the increase in compile time. + /// + /// ### Known problems + /// False positives, big time. This lint is meant to be + /// deactivated by everyone doing serious performance work. This means having + /// done the measurement. + /// + /// ### Example + /// ```ignore + /// #[inline(always)] + /// fn not_quite_hot_code(..) { ... } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub INLINE_ALWAYS, + pedantic, + "use of `#[inline(always)]`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). + /// + /// ### Why is this bad? + /// Having both style of said attributes makes it more complicated to read code. + /// + /// ### Known problems + /// This lint currently has false-negatives when mixing same attributes + /// but they have different path symbols, for example: + /// ```ignore + /// #[custom_attribute] + /// pub fn foo() { + /// #![my_crate::custom_attribute] + /// } + /// ``` + /// + /// ### Example + /// ```no_run + /// #[cfg(linux)] + /// pub fn foo() { + /// #![cfg(windows)] /// } /// ``` /// Use instead: - /// ```rust,ignore - /// #[expect(unused_mut)] - /// fn foo() -> usize { - /// let mut a = Vec::new(); - /// a.len() + /// ```no_run + /// #[cfg(linux)] + /// #[cfg(windows)] + /// pub fn foo() { /// } /// ``` - #[clippy::version = "1.70.0"] - pub ALLOW_ATTRIBUTES, - restriction, - "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." + #[clippy::version = "1.78.0"] + pub MIXED_ATTRIBUTES_STYLE, + style, + "item has both inner and outer attributes" } declare_clippy_lint! { /// ### What it does - /// Checks for `#[should_panic]` attributes without specifying the expected panic message. + /// Checks for `any` and `all` combinators in `cfg` with only one condition. /// /// ### Why is this bad? - /// The expected panic message should be specified to ensure that the test is actually - /// panicking with the expected message, and not another unrelated panic. + /// If there is only one condition, no need to wrap it into `any` or `all` combinators. /// /// ### Example /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } + /// #[cfg(any(unix))] + /// pub struct Bar; /// ``` /// /// Use instead: /// ```no_run - /// fn random() -> i32 { 0 } - /// - /// #[should_panic = "attempt to divide by zero"] - /// #[test] - /// fn my_test() { - /// let _ = 1 / random(); - /// } + /// #[cfg(unix)] + /// pub struct Bar; /// ``` - #[clippy::version = "1.74.0"] - pub SHOULD_PANIC_WITHOUT_EXPECT, - pedantic, - "ensures that all `should_panic` attributes specify its expected panic message" + #[clippy::version = "1.71.0"] + pub NON_MINIMAL_CFG, + style, + "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" } declare_clippy_lint! { @@ -314,52 +364,37 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for `any` and `all` combinators in `cfg` with only one condition. + /// Checks for `#[should_panic]` attributes without specifying the expected panic message. /// /// ### Why is this bad? - /// If there is only one condition, no need to wrap it into `any` or `all` combinators. + /// The expected panic message should be specified to ensure that the test is actually + /// panicking with the expected message, and not another unrelated panic. /// /// ### Example /// ```no_run - /// #[cfg(any(unix))] - /// pub struct Bar; - /// ``` - /// - /// Use instead: - /// ```no_run - /// #[cfg(unix)] - /// pub struct Bar; - /// ``` - #[clippy::version = "1.71.0"] - pub NON_MINIMAL_CFG, - style, - "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for - /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with - /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. - /// - /// ### Why is this bad? - /// This feature has been deprecated for years and shouldn't be used anymore. + /// fn random() -> i32 { 0 } /// - /// ### Example - /// ```no_run - /// #[cfg(feature = "cargo-clippy")] - /// struct Bar; + /// #[should_panic] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } /// ``` /// /// Use instead: /// ```no_run - /// #[cfg(clippy)] - /// struct Bar; + /// fn random() -> i32 { 0 } + /// + /// #[should_panic = "attempt to divide by zero"] + /// #[test] + /// fn my_test() { + /// let _ = 1 / random(); + /// } /// ``` - #[clippy::version = "1.78.0"] - pub DEPRECATED_CLIPPY_CFG_ATTR, - suspicious, - "usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`" + #[clippy::version = "1.74.0"] + pub SHOULD_PANIC_WITHOUT_EXPECT, + pedantic, + "ensures that all `should_panic` attributes specify its expected panic message" } declare_clippy_lint! { @@ -388,98 +423,82 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). - /// - /// ### Why is this bad? - /// Having both style of said attributes makes it more complicated to read code. + /// Checks for `extern crate` and `use` items annotated with + /// lint attributes. /// - /// ### Known problems - /// This lint currently has false-negatives when mixing same attributes - /// but they have different path symbols, for example: - /// ```ignore - /// #[custom_attribute] - /// pub fn foo() { - /// #![my_crate::custom_attribute] - /// } - /// ``` + /// This lint permits lint attributes for lints emitted on the items themself. + /// For `use` items these lints are: + /// * ambiguous_glob_reexports + /// * dead_code + /// * deprecated + /// * hidden_glob_reexports + /// * unreachable_pub + /// * unused + /// * unused_braces + /// * unused_import_braces + /// * clippy::disallowed_types + /// * clippy::enum_glob_use + /// * clippy::macro_use_imports + /// * clippy::module_name_repetitions + /// * clippy::redundant_pub_crate + /// * clippy::single_component_path_imports + /// * clippy::unsafe_removed_from_name + /// * clippy::wildcard_imports /// - /// ### Example - /// ```no_run - /// #[cfg(linux)] - /// pub fn foo() { - /// #![cfg(windows)] - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[cfg(linux)] - /// #[cfg(windows)] - /// pub fn foo() { - /// } - /// ``` - #[clippy::version = "1.78.0"] - pub MIXED_ATTRIBUTES_STYLE, - style, - "item has both inner and outer attributes" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for attributes that appear two or more times. + /// For `extern crate` items these lints are: + /// * `unused_imports` on items with `#[macro_use]` /// /// ### Why is this bad? - /// Repeating an attribute on the same item (or globally on the same crate) - /// is unnecessary and doesn't have an effect. + /// Lint attributes have no effect on crate imports. Most + /// likely a `!` was forgotten. /// /// ### Example - /// ```no_run - /// #[allow(dead_code)] - /// #[allow(dead_code)] - /// fn foo() {} + /// ```ignore + /// #[deny(dead_code)] + /// extern crate foo; + /// #[forbid(dead_code)] + /// use foo::bar; /// ``` /// /// Use instead: - /// ```no_run - /// #[allow(dead_code)] - /// fn foo() {} + /// ```rust,ignore + /// #[allow(unused_imports)] + /// use foo::baz; + /// #[allow(unused_imports)] + /// #[macro_use] + /// extern crate baz; /// ``` - #[clippy::version = "1.79.0"] - pub DUPLICATED_ATTRIBUTES, - suspicious, - "duplicated attribute" + #[clippy::version = "pre 1.29.0"] + pub USELESS_ATTRIBUTE, + correctness, + "use of lint attributes on `extern crate` items" } -declare_clippy_lint! { - /// ### What it does - /// Checks for ignored tests without messages. - /// - /// ### Why is this bad? - /// The reason for ignoring the test may not be obvious. - /// - /// ### Example - /// ```no_run - /// #[test] - /// #[ignore] - /// fn test() {} - /// ``` - /// Use instead: - /// ```no_run - /// #[test] - /// #[ignore = "Some good reason"] - /// fn test() {} - /// ``` - #[clippy::version = "1.88.0"] - pub IGNORE_WITHOUT_REASON, - pedantic, - "ignored tests without messages" -} +impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]); + +impl_lint_pass!(EarlyAttributes => [ + DEPRECATED_CFG_ATTR, + DEPRECATED_CLIPPY_CFG_ATTR, + NON_MINIMAL_CFG, + UNNECESSARY_CLIPPY_CFG, +]); + +impl_lint_pass!(PostExpansionEarlyAttributes => [ + ALLOW_ATTRIBUTES, + ALLOW_ATTRIBUTES_WITHOUT_REASON, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DEPRECATED_SEMVER, + DUPLICATED_ATTRIBUTES, + IGNORE_WITHOUT_REASON, + MIXED_ATTRIBUTES_STYLE, + SHOULD_PANIC_WITHOUT_EXPECT, + USELESS_ATTRIBUTE, +]); pub struct Attributes { msrv: Msrv, } -impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]); - impl Attributes { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv } @@ -522,13 +541,6 @@ impl EarlyAttributes { } } -impl_lint_pass!(EarlyAttributes => [ - DEPRECATED_CFG_ATTR, - DEPRECATED_CLIPPY_CFG_ATTR, - NON_MINIMAL_CFG, - UNNECESSARY_CLIPPY_CFG, -]); - impl EarlyLintPass for EarlyAttributes { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); @@ -551,18 +563,6 @@ impl PostExpansionEarlyAttributes { } } -impl_lint_pass!(PostExpansionEarlyAttributes => [ - ALLOW_ATTRIBUTES, - ALLOW_ATTRIBUTES_WITHOUT_REASON, - BLANKET_CLIPPY_RESTRICTION_LINTS, - DEPRECATED_SEMVER, - DUPLICATED_ATTRIBUTES, - IGNORE_WITHOUT_REASON, - MIXED_ATTRIBUTES_STYLE, - SHOULD_PANIC_WITHOUT_EXPECT, - USELESS_ATTRIBUTE, -]); - impl EarlyLintPass for PostExpansionEarlyAttributes { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { blanket_clippy_restriction_lints::check_command_line(cx); diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 2d61c5e0b78a..dbd7dbdd1c6f 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -10,6 +10,43 @@ use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across await + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly safe to use concurrently from + /// a memory access perspective, but that will cause bugs at runtime if + /// they are held in such a way. + /// + /// ### Example + /// + /// ```toml + /// await-holding-invalid-types = [ + /// # You can specify a type name + /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } + /// ] + /// ``` + /// + /// ```no_run + /// # async fn baz() {} + /// struct CustomLockType; + /// struct OtherCustomLockType; + /// async fn foo() { + /// let _x = CustomLockType; + /// let _y = OtherCustomLockType; + /// baz().await; // Lint violation + /// } + /// ``` + #[clippy::version = "1.62.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "holding a type across an await point which is not allowed to be held as per the configuration" +} + declare_clippy_lint! { /// ### What it does /// Checks for calls to `await` while holding a non-async-aware @@ -134,43 +171,6 @@ declare_clippy_lint! { "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_clippy_lint! { - /// ### What it does - /// Allows users to configure types which should not be held across await - /// suspension points. - /// - /// ### Why is this bad? - /// There are some types which are perfectly safe to use concurrently from - /// a memory access perspective, but that will cause bugs at runtime if - /// they are held in such a way. - /// - /// ### Example - /// - /// ```toml - /// await-holding-invalid-types = [ - /// # You can specify a type name - /// "CustomLockType", - /// # You can (optionally) specify a reason - /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } - /// ] - /// ``` - /// - /// ```no_run - /// # async fn baz() {} - /// struct CustomLockType; - /// struct OtherCustomLockType; - /// async fn foo() { - /// let _x = CustomLockType; - /// let _y = OtherCustomLockType; - /// baz().await; // Lint violation - /// } - /// ``` - #[clippy::version = "1.62.0"] - pub AWAIT_HOLDING_INVALID_TYPE, - suspicious, - "holding a type across an await point which is not allowed to be held as per the configuration" -} - impl_lint_pass!(AwaitHolding => [ AWAIT_HOLDING_INVALID_TYPE, AWAIT_HOLDING_LOCK, diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 129e77478406..b98a20a90ccb 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -43,6 +43,7 @@ declare_clippy_lint! { pedantic, "using if to convert bool to int" } + declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index f3985603c4d2..abf6083607bc 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -73,6 +73,8 @@ declare_clippy_lint! { "boolean expressions that contain terminals which can be eliminated" } +impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); + // For each pairs, both orders are considered. const METHODS_WITH_NEGATION: [(Option, Symbol, Symbol); 3] = [ (None, sym::is_some, sym::is_none), @@ -90,8 +92,6 @@ impl NonminimalBool { } } -impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]); - impl<'tcx> LateLintPass<'tcx> for NonminimalBool { fn check_fn( &mut self, diff --git a/clippy_lints/src/byte_char_slices.rs b/clippy_lints/src/byte_char_slices.rs index d88c0711b397..b4f31acb537c 100644 --- a/clippy_lints/src/byte_char_slices.rs +++ b/clippy_lints/src/byte_char_slices.rs @@ -27,6 +27,7 @@ declare_clippy_lint! { style, "hard to read byte char slice" } + declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]); impl EarlyLintPass for ByteCharSlice { diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 65be4d7559ff..3c3ccdcc30d0 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -58,34 +58,68 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` + /// Checks for lint groups with the same priority as lints in the `Cargo.toml` + /// [`[lints]` table](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section). + /// + /// This lint will be removed once [cargo#12918](https://github.com/rust-lang/cargo/issues/12918) + /// is resolved. /// /// ### Why is this bad? - /// These prefixes and suffixes have no significant meaning. + /// The order of lints in the `[lints]` is ignored, to have a lint override a group the + /// `priority` field needs to be used, otherwise the sort order is undefined. + /// + /// ### Known problems + /// Does not check lints inherited using `lints.workspace = true` /// /// ### Example /// ```toml - /// # The `Cargo.toml` with feature name redundancy - /// [features] - /// default = ["use-abc", "with-def", "ghi-support"] - /// use-abc = [] // redundant - /// with-def = [] // redundant - /// ghi-support = [] // redundant + /// # Passed as `--allow=clippy::similar_names --warn=clippy::pedantic` + /// # which results in `similar_names` being `warn` + /// [lints.clippy] + /// pedantic = "warn" + /// similar_names = "allow" /// ``` - /// /// Use instead: /// ```toml - /// [features] - /// default = ["abc", "def", "ghi"] - /// abc = [] - /// def = [] - /// ghi = [] + /// # Passed as `--warn=clippy::pedantic --allow=clippy::similar_names` + /// # which results in `similar_names` being `allow` + /// [lints.clippy] + /// pedantic = { level = "warn", priority = -1 } + /// similar_names = "allow" /// ``` + #[clippy::version = "1.78.0"] + pub LINT_GROUPS_PRIORITY, + correctness, + "a lint group in `Cargo.toml` at the same priority as a lint" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks to see if multiple versions of a crate are being + /// used. /// - #[clippy::version = "1.57.0"] - pub REDUNDANT_FEATURE_NAMES, + /// ### Why is this bad? + /// This bloats the size of targets, and can lead to + /// confusing error messages when structs or traits are used interchangeably + /// between different versions of a crate. + /// + /// ### Known problems + /// Because this can be caused purely by the dependencies + /// themselves, it's not always possible to fix this issue. + /// In those cases, you can allow that specific crate using + /// the `allowed_duplicate_crates` configuration option. + /// + /// ### Example + /// ```toml + /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning. + /// [dependencies] + /// ctrlc = "=3.1.0" + /// ansi_term = "=0.11.0" + /// ``` + #[clippy::version = "pre 1.29.0"] + pub MULTIPLE_CRATE_VERSIONS, cargo, - "usage of a redundant feature name" + "multiple versions of the same crate being used" } declare_clippy_lint! { @@ -120,31 +154,34 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks to see if multiple versions of a crate are being - /// used. + /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` /// /// ### Why is this bad? - /// This bloats the size of targets, and can lead to - /// confusing error messages when structs or traits are used interchangeably - /// between different versions of a crate. - /// - /// ### Known problems - /// Because this can be caused purely by the dependencies - /// themselves, it's not always possible to fix this issue. - /// In those cases, you can allow that specific crate using - /// the `allowed_duplicate_crates` configuration option. + /// These prefixes and suffixes have no significant meaning. /// /// ### Example /// ```toml - /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning. - /// [dependencies] - /// ctrlc = "=3.1.0" - /// ansi_term = "=0.11.0" + /// # The `Cargo.toml` with feature name redundancy + /// [features] + /// default = ["use-abc", "with-def", "ghi-support"] + /// use-abc = [] // redundant + /// with-def = [] // redundant + /// ghi-support = [] // redundant /// ``` - #[clippy::version = "pre 1.29.0"] - pub MULTIPLE_CRATE_VERSIONS, + /// + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def", "ghi"] + /// abc = [] + /// def = [] + /// ghi = [] + /// ``` + /// + #[clippy::version = "1.57.0"] + pub REDUNDANT_FEATURE_NAMES, cargo, - "multiple versions of the same crate being used" + "usage of a redundant feature name" } declare_clippy_lint! { @@ -176,48 +213,6 @@ declare_clippy_lint! { "wildcard dependencies being used" } -declare_clippy_lint! { - /// ### What it does - /// Checks for lint groups with the same priority as lints in the `Cargo.toml` - /// [`[lints]` table](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section). - /// - /// This lint will be removed once [cargo#12918](https://github.com/rust-lang/cargo/issues/12918) - /// is resolved. - /// - /// ### Why is this bad? - /// The order of lints in the `[lints]` is ignored, to have a lint override a group the - /// `priority` field needs to be used, otherwise the sort order is undefined. - /// - /// ### Known problems - /// Does not check lints inherited using `lints.workspace = true` - /// - /// ### Example - /// ```toml - /// # Passed as `--allow=clippy::similar_names --warn=clippy::pedantic` - /// # which results in `similar_names` being `warn` - /// [lints.clippy] - /// pedantic = "warn" - /// similar_names = "allow" - /// ``` - /// Use instead: - /// ```toml - /// # Passed as `--warn=clippy::pedantic --allow=clippy::similar_names` - /// # which results in `similar_names` being `allow` - /// [lints.clippy] - /// pedantic = { level = "warn", priority = -1 } - /// similar_names = "allow" - /// ``` - #[clippy::version = "1.78.0"] - pub LINT_GROUPS_PRIORITY, - correctness, - "a lint group in `Cargo.toml` at the same priority as a lint" -} - -pub struct Cargo { - allowed_duplicate_crates: FxHashSet, - ignore_publish: bool, -} - impl_lint_pass!(Cargo => [ CARGO_COMMON_METADATA, LINT_GROUPS_PRIORITY, @@ -227,6 +222,11 @@ impl_lint_pass!(Cargo => [ WILDCARD_DEPENDENCIES, ]); +pub struct Cargo { + allowed_duplicate_crates: FxHashSet, + ignore_publish: bool, +} + impl Cargo { pub fn new(conf: &'static Conf) -> Self { Self { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 2f6f9bf87cba..7c3627a171a3 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -35,434 +35,376 @@ use rustc_session::impl_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for casts from any numeric type to a float type where - /// the receiving type cannot store all values from the original type without - /// rounding errors. This possible rounding is to be expected, so this lint is - /// `Allow` by default. - /// - /// Basically, this warns on casting any integer with 32 or more bits to `f32` - /// or any 64-bit integer to `f64`. + /// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type. /// - /// ### Why is this bad? - /// It's not bad at all. But in some applications it can be - /// helpful to know where precision loss can take place. This lint can help find - /// those places in the code. + /// ### Why restrict this? + /// The conversion might include a dangerous cast that might go undetected due to the type being inferred. /// /// ### Example /// ```no_run - /// let x = u64::MAX; - /// x as f64; + /// fn as_usize(t: &T) -> usize { + /// // BUG: `t` is already a reference, so we will here + /// // return a dangling pointer to a temporary value instead + /// &t as *const _ as usize + /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_PRECISION_LOSS, - pedantic, - "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" + /// Use instead: + /// ```no_run + /// fn as_usize(t: &T) -> usize { + /// t as *const T as usize + /// } + /// ``` + #[clippy::version = "1.85.0"] + pub AS_POINTER_UNDERSCORE, + restriction, + "detects `as *mut _` and `as *const _` conversion" } declare_clippy_lint! { /// ### What it does - /// Checks for casts from a signed to an unsigned numeric - /// type. In this case, negative values wrap around to large positive values, - /// which can be quite surprising in practice. However, since the cast works as - /// defined, this lint is `Allow` by default. + /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer. /// /// ### Why is this bad? - /// Possibly surprising results. You can activate this lint - /// as a one-time check to see where numeric wrapping can arise. + /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior + /// mutability is used, making it unlikely that having it as a mutable pointer is correct. /// /// ### Example /// ```no_run - /// let y: i8 = -1; - /// y as u64; // will return 18446744073709551615 + /// let mut vec = Vec::::with_capacity(1); + /// let ptr = vec.as_ptr() as *mut u8; + /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_SIGN_LOSS, - pedantic, - "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" + /// Use instead: + /// ```no_run + /// let mut vec = Vec::::with_capacity(1); + /// let ptr = vec.as_mut_ptr(); + /// unsafe { ptr.write(4) }; + /// ``` + #[clippy::version = "1.66.0"] + pub AS_PTR_CAST_MUT, + nursery, + "casting the result of the `&self`-taking `as_ptr` to a mutable pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts between numeric types that may - /// truncate large values. This is expected behavior, so the cast is `Allow` by - /// default. It suggests user either explicitly ignore the lint, - /// or use `try_from()` and handle the truncation, default, or panic explicitly. + /// Checks for the usage of `as _` conversion using inferred type. /// - /// ### Why is this bad? - /// In some problem domains, it is good practice to avoid - /// truncation. This lint can be activated to help assess where additional - /// checks could be beneficial. + /// ### Why restrict this? + /// The conversion might include lossy conversion or a dangerous cast that might go + /// undetected due to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. /// /// ### Example /// ```no_run - /// fn as_u8(x: u64) -> u8 { - /// x as u8 - /// } + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); /// ``` /// Use instead: /// ```no_run - /// fn as_u8(x: u64) -> u8 { - /// if let Ok(x) = u8::try_from(x) { - /// x - /// } else { - /// todo!(); - /// } - /// } - /// // Or - /// #[allow(clippy::cast_possible_truncation)] - /// fn as_u16(x: u64) -> u16 { - /// x as u16 - /// } + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_POSSIBLE_TRUNCATION, - pedantic, - "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`" + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" } declare_clippy_lint! { /// ### What it does - /// Checks for casts from an unsigned type to a signed type of - /// the same size, or possibly smaller due to target-dependent integers. - /// Performing such a cast is a no-op for the compiler (that is, nothing is - /// changed at the bit level), and the binary representation of the value is - /// reinterpreted. This can cause wrapping if the value is too big - /// for the target signed type. However, the cast works as defined, so this lint - /// is `Allow` by default. + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `&raw const` or + /// `&raw mut` instead. /// /// ### Why is this bad? - /// While such a cast is not bad in itself, the results can - /// be surprising when this is not the intended behavior: + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `&raw` explanation in the Reference for more information. /// /// ### Example /// ```no_run - /// let _ = u32::MAX as i32; // will yield a value of `-1` - /// ``` + /// let val = 1; + /// let p = &val as *const i32; /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` /// Use instead: /// ```no_run - /// let _ = i32::try_from(u32::MAX).ok(); - /// ``` + /// let val = 1; + /// let p = &raw const val; /// - #[clippy::version = "pre 1.29.0"] - pub CAST_POSSIBLE_WRAP, + /// let mut val_mut = 1; + /// let p_mut = &raw mut val_mut; + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, pedantic, - "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`" + "borrowing just to cast to a raw pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts between numeric types that can be replaced by safe - /// conversion functions. + /// Checks for usage of the `abs()` method that cast the result to unsigned. /// /// ### Why is this bad? - /// Rust's `as` keyword will perform many kinds of conversions, including - /// silently lossy conversions. Conversion functions such as `i32::from` - /// will only perform lossless conversions. Using the conversion functions - /// prevents conversions from becoming silently lossy if the input types - /// ever change, and makes it clear for people reading the code that the - /// conversion is lossless. + /// The `unsigned_abs()` method avoids panic when called on the MIN value. /// /// ### Example /// ```no_run - /// fn as_u64(x: u8) -> u64 { - /// x as u64 - /// } + /// let x: i32 = -42; + /// let y: u32 = x.abs() as u32; /// ``` - /// - /// Using `::from` would look like this: - /// + /// Use instead: /// ```no_run - /// fn as_u64(x: u8) -> u64 { - /// u64::from(x) - /// } + /// let x: i32 = -42; + /// let y: u32 = x.unsigned_abs(); /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_LOSSLESS, - pedantic, - "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" + #[clippy::version = "1.62.0"] + pub CAST_ABS_TO_UNSIGNED, + suspicious, + "casting the result of `abs()` to an unsigned integer can panic" } declare_clippy_lint! { /// ### What it does - /// Checks for casts to the same type, casts of int literals to integer - /// types, casts of float literals to float types, and casts between raw - /// pointers that don't change type or constness. + /// Checks for casts from an enum tuple constructor to an integer. /// /// ### Why is this bad? - /// It's just unnecessary. - /// - /// ### Known problems - /// When the expression on the left is a function call, the lint considers - /// the return type to be a type alias if it's aliased through a `use` - /// statement (like `use std::io::Result as IoResult`). It will not lint - /// such cases. - /// - /// This check will only work on primitive types without any intermediate - /// references: raw pointers and trait objects may or may not work. + /// The cast is easily confused with casting a c-like enum value to an integer. /// /// ### Example /// ```no_run - /// let _ = 2i32 as i32; - /// let _ = 0.5 as f32; - /// ``` - /// - /// Better: - /// - /// ```no_run - /// let _ = 2_i32; - /// let _ = 0.5_f32; + /// enum E { X(i32) }; + /// let _ = E::X as usize; /// ``` - #[clippy::version = "pre 1.29.0"] - pub UNNECESSARY_CAST, - complexity, - "cast to the same type, e.g., `x as i32` where `x: i32`" + #[clippy::version = "1.61.0"] + pub CAST_ENUM_CONSTRUCTOR, + suspicious, + "casts from an enum tuple constructor to an integer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts, using `as` or `pointer::cast`, from a - /// less strictly aligned pointer to a more strictly aligned pointer. + /// Checks for casts from an enum type to an integral type that will definitely truncate the + /// value. /// /// ### Why is this bad? - /// Dereferencing the resulting pointer may be undefined behavior. - /// - /// ### Known problems - /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) or - /// similar on the resulting pointer is fine. Is over-zealous: casts with - /// manual alignment checks or casts like `u64` -> `u8` -> `u16` can be - /// fine. Miri is able to do a more in-depth analysis. + /// The resulting integral value will not match the value of the variant it came from. /// /// ### Example /// ```no_run - /// let _ = (&1u8 as *const u8) as *const u16; - /// let _ = (&mut 1u8 as *mut u8) as *mut u16; - /// - /// (&1u8 as *const u8).cast::(); - /// (&mut 1u8 as *mut u8).cast::(); + /// enum E { X = 256 }; + /// let _ = E::X as u8; /// ``` - #[clippy::version = "pre 1.29.0"] - pub CAST_PTR_ALIGNMENT, - pedantic, - "cast from a pointer to a more strictly aligned pointer" + #[clippy::version = "1.61.0"] + pub CAST_ENUM_TRUNCATION, + suspicious, + "casts from an enum type to an integral type that will truncate the value" } declare_clippy_lint! { /// ### What it does - /// Checks for casts of function pointers to something other than `usize`. + /// Checks for casts between numeric types that can be replaced by safe + /// conversion functions. /// /// ### Why is this bad? - /// Casting a function pointer to anything other than `usize`/`isize` is - /// not portable across architectures. If the target type is too small the - /// address would be truncated, and target types larger than `usize` are - /// unnecessary. - /// - /// Casting to `isize` also doesn't make sense, since addresses are never - /// signed. + /// Rust's `as` keyword will perform many kinds of conversions, including + /// silently lossy conversions. Conversion functions such as `i32::from` + /// will only perform lossless conversions. Using the conversion functions + /// prevents conversions from becoming silently lossy if the input types + /// ever change, and makes it clear for people reading the code that the + /// conversion is lossless. /// /// ### Example /// ```no_run - /// fn fun() -> i32 { 1 } - /// let _ = fun as i64; + /// fn as_u64(x: u8) -> u64 { + /// x as u64 + /// } /// ``` /// - /// Use instead: + /// Using `::from` would look like this: + /// /// ```no_run - /// # fn fun() -> i32 { 1 } - /// let _ = fun as usize; + /// fn as_u64(x: u8) -> u64 { + /// u64::from(x) + /// } /// ``` #[clippy::version = "pre 1.29.0"] - pub FN_TO_NUMERIC_CAST, - style, - "casting a function pointer to a numeric type other than `usize`" + pub CAST_LOSSLESS, + pedantic, + "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`" } declare_clippy_lint! { /// ### What it does - /// Checks for casts of a function pointer to a numeric type not wide enough to - /// store an address. + /// Checks for a known NaN float being cast to an integer /// /// ### Why is this bad? - /// Such a cast discards some bits of the function's address. If this is intended, it would be more - /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with - /// a comment) to perform the truncation. + /// NaNs are cast into zero, so one could simply use this and make the + /// code more readable. The lint could also hint at a programmer error. /// /// ### Example - /// ```no_run - /// fn fn1() -> i16 { - /// 1 - /// }; - /// let _ = fn1 as i32; + /// ```rust,ignore + /// let _ = (0.0_f32 / 0.0) as u64; /// ``` - /// /// Use instead: - /// ```no_run - /// // Cast to usize first, then comment with the reason for the truncation - /// fn fn1() -> i16 { - /// 1 - /// }; - /// let fn_ptr = fn1 as usize; - /// let fn_ptr_truncated = fn_ptr as i32; + /// ```rust,ignore + /// let _ = 0_u64; /// ``` - #[clippy::version = "pre 1.29.0"] - pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - style, - "casting a function pointer to a numeric type not wide enough to store the address" + #[clippy::version = "1.66.0"] + pub CAST_NAN_TO_INT, + suspicious, + "casting a known floating-point NaN into an integer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts of a function pointer to any integer type. + /// Checks for casts between numeric types that may + /// truncate large values. This is expected behavior, so the cast is `Allow` by + /// default. It suggests user either explicitly ignore the lint, + /// or use `try_from()` and handle the truncation, default, or panic explicitly. /// - /// ### Why restrict this? - /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parentheses are omitted from a function call. If you aren't doing anything - /// low-level with function pointers then you can opt out of casting functions to integers in - /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function - /// pointer casts in your code. + /// ### Why is this bad? + /// In some problem domains, it is good practice to avoid + /// truncation. This lint can be activated to help assess where additional + /// checks could be beneficial. /// /// ### Example /// ```no_run - /// // fn1 is cast as `usize` - /// fn fn1() -> u16 { - /// 1 - /// }; - /// let _ = fn1 as usize; + /// fn as_u8(x: u64) -> u8 { + /// x as u8 + /// } /// ``` - /// /// Use instead: /// ```no_run - /// // maybe you intended to call the function? - /// fn fn2() -> u16 { - /// 1 - /// }; - /// let _ = fn2() as usize; - /// - /// // or - /// - /// // maybe you intended to cast it to a function type? - /// fn fn3() -> u16 { - /// 1 + /// fn as_u8(x: u64) -> u8 { + /// if let Ok(x) = u8::try_from(x) { + /// x + /// } else { + /// todo!(); + /// } + /// } + /// // Or + /// #[allow(clippy::cast_possible_truncation)] + /// fn as_u16(x: u64) -> u16 { + /// x as u16 /// } - /// let _ = fn3 as fn() -> u16; /// ``` - #[clippy::version = "1.58.0"] - pub FN_TO_NUMERIC_CAST_ANY, - restriction, - "casting a function pointer to any integer type" + #[clippy::version = "pre 1.29.0"] + pub CAST_POSSIBLE_TRUNCATION, + pedantic, + "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`" } declare_clippy_lint! { /// ### What it does - /// Checks for expressions where a character literal is cast - /// to `u8` and suggests using a byte literal instead. - /// - /// ### Why is this bad? - /// In general, casting values to smaller types is - /// error-prone and should be avoided where possible. In the particular case of - /// converting a character literal to `u8`, it is easy to avoid by just using a - /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter - /// than `'a' as u8`. + /// Checks for casts from an unsigned type to a signed type of + /// the same size, or possibly smaller due to target-dependent integers. + /// Performing such a cast is a no-op for the compiler (that is, nothing is + /// changed at the bit level), and the binary representation of the value is + /// reinterpreted. This can cause wrapping if the value is too big + /// for the target signed type. However, the cast works as defined, so this lint + /// is `Allow` by default. + /// + /// ### Why is this bad? + /// While such a cast is not bad in itself, the results can + /// be surprising when this is not the intended behavior: /// /// ### Example - /// ```rust,ignore - /// 'x' as u8 + /// ```no_run + /// let _ = u32::MAX as i32; // will yield a value of `-1` /// ``` /// - /// A better version, using the byte literal: - /// - /// ```rust,ignore - /// b'x' + /// Use instead: + /// ```no_run + /// let _ = i32::try_from(u32::MAX).ok(); /// ``` + /// #[clippy::version = "pre 1.29.0"] - pub CHAR_LIT_AS_U8, - complexity, - "casting a character literal to `u8` truncates" + pub CAST_POSSIBLE_WRAP, + pedantic, + "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`" } declare_clippy_lint! { /// ### What it does - /// Checks for `as` casts between raw pointers that don't change their - /// constness, namely `*const T` to `*const U` and `*mut T` to `*mut U`. + /// Checks for casts from any numeric type to a float type where + /// the receiving type cannot store all values from the original type without + /// rounding errors. This possible rounding is to be expected, so this lint is + /// `Allow` by default. + /// + /// Basically, this warns on casting any integer with 32 or more bits to `f32` + /// or any 64-bit integer to `f64`. /// /// ### Why is this bad? - /// Though `as` casts between raw pointers are not terrible, - /// `pointer::cast` is safer because it cannot accidentally change the - /// pointer's mutability, nor cast the pointer to other types like `usize`. + /// It's not bad at all. But in some applications it can be + /// helpful to know where precision loss can take place. This lint can help find + /// those places in the code. /// /// ### Example /// ```no_run - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr: *mut u32 = &mut 42_u32; - /// let _ = ptr as *const i32; - /// let _ = mut_ptr as *mut i32; - /// ``` - /// Use instead: - /// ```no_run - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr: *mut u32 = &mut 42_u32; - /// let _ = ptr.cast::(); - /// let _ = mut_ptr.cast::(); + /// let x = u64::MAX; + /// x as f64; /// ``` - #[clippy::version = "1.51.0"] - pub PTR_AS_PTR, + #[clippy::version = "pre 1.29.0"] + pub CAST_PRECISION_LOSS, pedantic, - "casting using `as` between raw pointers that doesn't change their constness, where `pointer::cast` could take the place of `as`" + "casts that cause loss of precision, e.g., `x as f32` where `x: u64`" } declare_clippy_lint! { /// ### What it does - /// Checks for `as` casts between raw pointers that change their constness, namely `*const T` to - /// `*mut T` and `*mut T` to `*const T`. + /// Checks for casts, using `as` or `pointer::cast`, from a + /// less strictly aligned pointer to a more strictly aligned pointer. /// /// ### Why is this bad? - /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and - /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another - /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. + /// Dereferencing the resulting pointer may be undefined behavior. + /// + /// ### Known problems + /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) or + /// similar on the resulting pointer is fine. Is over-zealous: casts with + /// manual alignment checks or casts like `u64` -> `u8` -> `u16` can be + /// fine. Miri is able to do a more in-depth analysis. /// /// ### Example /// ```no_run - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr = ptr as *mut u32; - /// let ptr = mut_ptr as *const u32; - /// let ptr1 = std::ptr::null::() as *mut u32; - /// let ptr2 = std::ptr::null_mut::() as *const u32; - /// let ptr3 = std::ptr::null::().cast_mut(); - /// let ptr4 = std::ptr::null_mut::().cast_const(); - /// ``` - /// Use instead: - /// ```no_run - /// let ptr: *const u32 = &42_u32; - /// let mut_ptr = ptr.cast_mut(); - /// let ptr = mut_ptr.cast_const(); - /// let ptr1 = std::ptr::null_mut::(); - /// let ptr2 = std::ptr::null::(); - /// let ptr3 = std::ptr::null_mut::(); - /// let ptr4 = std::ptr::null::(); + /// let _ = (&1u8 as *const u8) as *const u16; + /// let _ = (&mut 1u8 as *mut u8) as *mut u16; + /// + /// (&1u8 as *const u8).cast::(); + /// (&mut 1u8 as *mut u8).cast::(); /// ``` - #[clippy::version = "1.72.0"] - pub PTR_CAST_CONSTNESS, + #[clippy::version = "pre 1.29.0"] + pub CAST_PTR_ALIGNMENT, pedantic, - "casting using `as` on raw pointers to change constness when specialized methods apply" + "cast from a pointer to a more strictly aligned pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for casts from an enum type to an integral type that will definitely truncate the - /// value. + /// Checks for casts from a signed to an unsigned numeric + /// type. In this case, negative values wrap around to large positive values, + /// which can be quite surprising in practice. However, since the cast works as + /// defined, this lint is `Allow` by default. /// /// ### Why is this bad? - /// The resulting integral value will not match the value of the variant it came from. + /// Possibly surprising results. You can activate this lint + /// as a one-time check to see where numeric wrapping can arise. /// /// ### Example /// ```no_run - /// enum E { X = 256 }; - /// let _ = E::X as u8; + /// let y: i8 = -1; + /// y as u64; // will return 18446744073709551615 /// ``` - #[clippy::version = "1.61.0"] - pub CAST_ENUM_TRUNCATION, - suspicious, - "casts from an enum type to an integral type that will truncate the value" + #[clippy::version = "pre 1.29.0"] + pub CAST_SIGN_LOSS, + pedantic, + "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`" } declare_clippy_lint! { @@ -512,202 +454,280 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for casts from an enum tuple constructor to an integer. + /// Checks for a raw slice being cast to a slice pointer /// /// ### Why is this bad? - /// The cast is easily confused with casting a c-like enum value to an integer. + /// This can result in multiple `&mut` references to the same location when only a pointer is + /// required. + /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require + /// the same [safety requirements] to be upheld. /// /// ### Example - /// ```no_run - /// enum E { X(i32) }; - /// let _ = E::X as usize; + /// ```rust,ignore + /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; + /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; /// ``` - #[clippy::version = "1.61.0"] - pub CAST_ENUM_CONSTRUCTOR, + /// Use instead: + /// ```rust,ignore + /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); + /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); + /// ``` + /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety + #[clippy::version = "1.65.0"] + pub CAST_SLICE_FROM_RAW_PARTS, suspicious, - "casts from an enum tuple constructor to an integer" + "casting a slice created from a pointer and length to a slice pointer" } declare_clippy_lint! { /// ### What it does - /// Checks for usage of the `abs()` method that cast the result to unsigned. + /// Checks for expressions where a character literal is cast + /// to `u8` and suggests using a byte literal instead. /// /// ### Why is this bad? - /// The `unsigned_abs()` method avoids panic when called on the MIN value. + /// In general, casting values to smaller types is + /// error-prone and should be avoided where possible. In the particular case of + /// converting a character literal to `u8`, it is easy to avoid by just using a + /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter + /// than `'a' as u8`. + /// + /// ### Example + /// ```rust,ignore + /// 'x' as u8 + /// ``` + /// + /// A better version, using the byte literal: + /// + /// ```rust,ignore + /// b'x' + /// ``` + #[clippy::version = "pre 1.29.0"] + pub CHAR_LIT_AS_U8, + complexity, + "casting a character literal to `u8` truncates" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. /// /// ### Example /// ```no_run - /// let x: i32 = -42; - /// let y: u32 = x.abs() as u32; + /// let _ = u16::max as usize; /// ``` + /// /// Use instead: /// ```no_run - /// let x: i32 = -42; - /// let y: u32 = x.unsigned_abs(); + /// let _ = u16::MAX as usize; /// ``` - #[clippy::version = "1.62.0"] - pub CAST_ABS_TO_UNSIGNED, + #[clippy::version = "1.89.0"] + pub CONFUSING_METHOD_TO_NUMERIC_CAST, suspicious, - "casting the result of `abs()` to an unsigned integer can panic" + "casting a primitive method pointer to any integer type" } declare_clippy_lint! { /// ### What it does - /// Checks for the usage of `as _` conversion using inferred type. + /// Checks for casts of function pointers to something other than `usize`. /// - /// ### Why restrict this? - /// The conversion might include lossy conversion or a dangerous cast that might go - /// undetected due to the type being inferred. + /// ### Why is this bad? + /// Casting a function pointer to anything other than `usize`/`isize` is + /// not portable across architectures. If the target type is too small the + /// address would be truncated, and target types larger than `usize` are + /// unnecessary. /// - /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// Casting to `isize` also doesn't make sense, since addresses are never + /// signed. /// /// ### Example /// ```no_run - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as _); + /// fn fun() -> i32 { 1 } + /// let _ = fun as i64; /// ``` + /// /// Use instead: /// ```no_run - /// fn foo(n: usize) {} - /// let n: u16 = 256; - /// foo(n as usize); + /// # fn fun() -> i32 { 1 } + /// let _ = fun as usize; /// ``` - #[clippy::version = "1.63.0"] - pub AS_UNDERSCORE, - restriction, - "detects `as _` conversion" + #[clippy::version = "pre 1.29.0"] + pub FN_TO_NUMERIC_CAST, + style, + "casting a function pointer to a numeric type other than `usize`" } declare_clippy_lint! { /// ### What it does - /// Checks for the usage of `&expr as *const T` or - /// `&mut expr as *mut T`, and suggest using `&raw const` or - /// `&raw mut` instead. + /// Checks for casts of a function pointer to any integer type. /// - /// ### Why is this bad? - /// This would improve readability and avoid creating a reference - /// that points to an uninitialized value or unaligned place. - /// Read the `&raw` explanation in the Reference for more information. + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. /// /// ### Example /// ```no_run - /// let val = 1; - /// let p = &val as *const i32; + /// // fn1 is cast as `usize` + /// fn fn1() -> u16 { + /// 1 + /// }; + /// let _ = fn1 as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// // maybe you intended to call the function? + /// fn fn2() -> u16 { + /// 1 + /// }; + /// let _ = fn2() as usize; /// - /// let mut val_mut = 1; - /// let p_mut = &mut val_mut as *mut i32; - /// ``` - /// Use instead: - /// ```no_run - /// let val = 1; - /// let p = &raw const val; + /// // or /// - /// let mut val_mut = 1; - /// let p_mut = &raw mut val_mut; + /// // maybe you intended to cast it to a function type? + /// fn fn3() -> u16 { + /// 1 + /// } + /// let _ = fn3 as fn() -> u16; /// ``` - #[clippy::version = "1.60.0"] - pub BORROW_AS_PTR, - pedantic, - "borrowing just to cast to a raw pointer" + #[clippy::version = "1.58.0"] + pub FN_TO_NUMERIC_CAST_ANY, + restriction, + "casting a function pointer to any integer type" } declare_clippy_lint! { /// ### What it does - /// Checks for a raw slice being cast to a slice pointer + /// Checks for casts of a function pointer to a numeric type not wide enough to + /// store an address. /// /// ### Why is this bad? - /// This can result in multiple `&mut` references to the same location when only a pointer is - /// required. - /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require - /// the same [safety requirements] to be upheld. + /// Such a cast discards some bits of the function's address. If this is intended, it would be more + /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with + /// a comment) to perform the truncation. /// /// ### Example - /// ```rust,ignore - /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _; - /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _; + /// ```no_run + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let _ = fn1 as i32; /// ``` + /// /// Use instead: - /// ```rust,ignore - /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len); - /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); + /// ```no_run + /// // Cast to usize first, then comment with the reason for the truncation + /// fn fn1() -> i16 { + /// 1 + /// }; + /// let fn_ptr = fn1 as usize; + /// let fn_ptr_truncated = fn_ptr as i32; /// ``` - /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety - #[clippy::version = "1.65.0"] - pub CAST_SLICE_FROM_RAW_PARTS, - suspicious, - "casting a slice created from a pointer and length to a slice pointer" + #[clippy::version = "pre 1.29.0"] + pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + style, + "casting a function pointer to a numeric type not wide enough to store the address" } declare_clippy_lint! { /// ### What it does - /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer. + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. /// /// ### Why is this bad? - /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior - /// mutability is used, making it unlikely that having it as a mutable pointer is correct. + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. /// /// ### Example /// ```no_run - /// let mut vec = Vec::::with_capacity(1); - /// let ptr = vec.as_ptr() as *mut u8; - /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; /// ``` /// Use instead: /// ```no_run - /// let mut vec = Vec::::with_capacity(1); - /// let ptr = vec.as_mut_ptr(); - /// unsafe { ptr.write(4) }; + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); /// ``` - #[clippy::version = "1.66.0"] - pub AS_PTR_CAST_MUT, - nursery, - "casting the result of the `&self`-taking `as_ptr` to a mutable pointer" + #[clippy::version = "1.88.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" } declare_clippy_lint! { /// ### What it does - /// Checks for a known NaN float being cast to an integer + /// Checks for `as` casts between raw pointers that don't change their + /// constness, namely `*const T` to `*const U` and `*mut T` to `*mut U`. /// /// ### Why is this bad? - /// NaNs are cast into zero, so one could simply use this and make the - /// code more readable. The lint could also hint at a programmer error. + /// Though `as` casts between raw pointers are not terrible, + /// `pointer::cast` is safer because it cannot accidentally change the + /// pointer's mutability, nor cast the pointer to other types like `usize`. /// /// ### Example - /// ```rust,ignore - /// let _ = (0.0_f32 / 0.0) as u64; + /// ```no_run + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr as *const i32; + /// let _ = mut_ptr as *mut i32; /// ``` /// Use instead: - /// ```rust,ignore - /// let _ = 0_u64; + /// ```no_run + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr: *mut u32 = &mut 42_u32; + /// let _ = ptr.cast::(); + /// let _ = mut_ptr.cast::(); /// ``` - #[clippy::version = "1.66.0"] - pub CAST_NAN_TO_INT, - suspicious, - "casting a known floating-point NaN into an integer" + #[clippy::version = "1.51.0"] + pub PTR_AS_PTR, + pedantic, + "casting using `as` between raw pointers that doesn't change their constness, where `pointer::cast` could take the place of `as`" } declare_clippy_lint! { /// ### What it does - /// Catch casts from `0` to some pointer type + /// Checks for `as` casts between raw pointers that change their constness, namely `*const T` to + /// `*mut T` and `*mut T` to `*const T`. /// /// ### Why is this bad? - /// This generally means `null` and is better expressed as - /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. + /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and + /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another + /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. /// /// ### Example /// ```no_run - /// let a = 0 as *const u32; + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr as *mut u32; + /// let ptr = mut_ptr as *const u32; + /// let ptr1 = std::ptr::null::() as *mut u32; + /// let ptr2 = std::ptr::null_mut::() as *const u32; + /// let ptr3 = std::ptr::null::().cast_mut(); + /// let ptr4 = std::ptr::null_mut::().cast_const(); /// ``` - /// /// Use instead: /// ```no_run - /// let a = std::ptr::null::(); + /// let ptr: *const u32 = &42_u32; + /// let mut_ptr = ptr.cast_mut(); + /// let ptr = mut_ptr.cast_const(); + /// let ptr1 = std::ptr::null_mut::(); + /// let ptr2 = std::ptr::null::(); + /// let ptr3 = std::ptr::null_mut::(); + /// let ptr4 = std::ptr::null::(); /// ``` - #[clippy::version = "pre 1.29.0"] - pub ZERO_PTR, - style, - "using `0 as *{const, mut} T`" + #[clippy::version = "1.72.0"] + pub PTR_CAST_CONSTNESS, + pedantic, + "casting using `as` on raw pointers to change constness when specialized methods apply" } declare_clippy_lint! { @@ -736,91 +756,61 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type. + /// Checks for casts to the same type, casts of int literals to integer + /// types, casts of float literals to float types, and casts between raw + /// pointers that don't change type or constness. /// - /// ### Why restrict this? - /// The conversion might include a dangerous cast that might go undetected due to the type being inferred. + /// ### Why is this bad? + /// It's just unnecessary. /// - /// ### Example - /// ```no_run - /// fn as_usize(t: &T) -> usize { - /// // BUG: `t` is already a reference, so we will here - /// // return a dangling pointer to a temporary value instead - /// &t as *const _ as usize - /// } - /// ``` - /// Use instead: - /// ```no_run - /// fn as_usize(t: &T) -> usize { - /// t as *const T as usize - /// } - /// ``` - #[clippy::version = "1.85.0"] - pub AS_POINTER_UNDERSCORE, - restriction, - "detects `as *mut _` and `as *const _` conversion" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// ### Known problems + /// When the expression on the left is a function call, the lint considers + /// the return type to be a type alias if it's aliased through a `use` + /// statement (like `use std::io::Result as IoResult`). It will not lint + /// such cases. /// - /// ### Why is this bad? - /// This creates a dangling pointer and is better expressed as - /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// This check will only work on primitive types without any intermediate + /// references: raw pointers and trait objects may or may not work. /// /// ### Example /// ```no_run - /// let ptr = 4 as *const u32; - /// let aligned = std::mem::align_of::() as *const u32; - /// let mut_ptr: *mut i64 = 8 as *mut _; + /// let _ = 2i32 as i32; + /// let _ = 0.5 as f32; /// ``` - /// Use instead: + /// + /// Better: + /// /// ```no_run - /// let ptr = std::ptr::dangling::(); - /// let aligned = std::ptr::dangling::(); - /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// let _ = 2_i32; + /// let _ = 0.5_f32; /// ``` - #[clippy::version = "1.88.0"] - pub MANUAL_DANGLING_PTR, - style, - "casting small constant literals to pointers to create dangling pointers" + #[clippy::version = "pre 1.29.0"] + pub UNNECESSARY_CAST, + complexity, + "cast to the same type, e.g., `x as i32` where `x: i32`" } declare_clippy_lint! { /// ### What it does - /// Checks for casts of a primitive method pointer like `max`/`min` to any integer type. + /// Catch casts from `0` to some pointer type /// - /// ### Why restrict this? - /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parentheses are omitted from a function call. If you aren't doing anything - /// low-level with function pointers then you can opt out of casting functions to integers in - /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function - /// pointer casts in your code. + /// ### Why is this bad? + /// This generally means `null` and is better expressed as + /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}. /// /// ### Example /// ```no_run - /// let _ = u16::max as usize; + /// let a = 0 as *const u32; /// ``` /// /// Use instead: /// ```no_run - /// let _ = u16::MAX as usize; + /// let a = std::ptr::null::(); /// ``` - #[clippy::version = "1.89.0"] - pub CONFUSING_METHOD_TO_NUMERIC_CAST, - suspicious, - "casting a primitive method pointer to any integer type" -} - -pub struct Casts { - msrv: Msrv, -} - -impl Casts { - pub fn new(conf: &'static Conf) -> Self { - Self { msrv: conf.msrv } - } + #[clippy::version = "pre 1.29.0"] + pub ZERO_PTR, + style, + "using `0 as *{const, mut} T`" } impl_lint_pass!(Casts => [ @@ -853,6 +843,16 @@ impl_lint_pass!(Casts => [ ZERO_PTR, ]); +pub struct Casts { + msrv: Msrv, +} + +impl Casts { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.in_external_macro(cx.sess().source_map()) { diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 9b3822f9d8f0..cd00972c1098 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -34,6 +34,8 @@ declare_clippy_lint! { "`try_from` could replace manual bounds checking when casting" } +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); + pub struct CheckedConversions { msrv: Msrv, } @@ -44,8 +46,6 @@ impl CheckedConversions { } } -impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); - impl LateLintPass<'_> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { if let ExprKind::Binary(op, lhs, rhs) = item.kind diff --git a/clippy_lints/src/cloned_ref_to_slice_refs.rs b/clippy_lints/src/cloned_ref_to_slice_refs.rs index 35b799aefb04..c5eabe4c2b88 100644 --- a/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -44,6 +44,8 @@ declare_clippy_lint! { "cloning a reference for slice references" } +impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); + pub struct ClonedRefToSliceRefs<'a> { msrv: &'a Msrv, } @@ -53,8 +55,6 @@ impl<'a> ClonedRefToSliceRefs<'a> { } } -impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]); - impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if self.msrv.meets(cx, { diff --git a/clippy_lints/src/coerce_container_to_any.rs b/clippy_lints/src/coerce_container_to_any.rs index 2e3acb7748e2..1a7e20b98271 100644 --- a/clippy_lints/src/coerce_container_to_any.rs +++ b/clippy_lints/src/coerce_container_to_any.rs @@ -46,6 +46,7 @@ declare_clippy_lint! { nursery, "coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended" } + declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]); impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny { diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 595625c08bef..fccbd2421680 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -40,6 +40,8 @@ declare_clippy_lint! { @eval_always = true } +impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); + pub struct CognitiveComplexity { limit: LimitStack, } @@ -52,8 +54,6 @@ impl CognitiveComplexity { } } -impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); - impl CognitiveComplexity { fn check<'tcx>( &self, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3e0c4fe02a66..c9ae9a3a2128 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -14,72 +14,74 @@ use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { /// ### What it does - /// Checks for nested `if` statements which can be collapsed - /// by `&&`-combining their conditions. + /// Checks for collapsible `else { if ... }` expressions + /// that can be collapsed to `else if ...`. /// /// ### Why is this bad? /// Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. /// /// ### Example - /// ```no_run - /// # let (x, y) = (true, true); + /// ```rust,ignore + /// /// if x { + /// … + /// } else { /// if y { - /// // … + /// … /// } /// } /// ``` /// - /// Use instead: - /// ```no_run - /// # let (x, y) = (true, true); - /// if x && y { - /// // … + /// Should be written: + /// + /// ```rust,ignore + /// if x { + /// … + /// } else if y { + /// … /// } /// ``` - #[clippy::version = "pre 1.29.0"] - pub COLLAPSIBLE_IF, + #[clippy::version = "1.51.0"] + pub COLLAPSIBLE_ELSE_IF, style, - "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" + "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" } declare_clippy_lint! { /// ### What it does - /// Checks for collapsible `else { if ... }` expressions - /// that can be collapsed to `else if ...`. + /// Checks for nested `if` statements which can be collapsed + /// by `&&`-combining their conditions. /// /// ### Why is this bad? /// Each `if`-statement adds one level of nesting, which /// makes code look more complex than it really is. /// /// ### Example - /// ```rust,ignore - /// + /// ```no_run + /// # let (x, y) = (true, true); /// if x { - /// … - /// } else { /// if y { - /// … + /// // … /// } /// } /// ``` /// - /// Should be written: - /// - /// ```rust,ignore - /// if x { - /// … - /// } else if y { - /// … + /// Use instead: + /// ```no_run + /// # let (x, y) = (true, true); + /// if x && y { + /// // … /// } /// ``` - #[clippy::version = "1.51.0"] - pub COLLAPSIBLE_ELSE_IF, + #[clippy::version = "pre 1.29.0"] + pub COLLAPSIBLE_IF, style, - "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)" + "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`" } +impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]); + pub struct CollapsibleIf { msrv: Msrv, lint_commented_code: bool, @@ -259,8 +261,6 @@ impl CollapsibleIf { } } -impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]); - impl LateLintPass<'_> for CollapsibleIf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::If(cond, then, else_) = &expr.kind diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index fd84ce70bd71..f9afdb0cabf9 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -41,6 +41,7 @@ declare_clippy_lint! { nursery, "a collection is never queried" } + declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index 19f62e8bf79c..509b345048c1 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -49,6 +49,7 @@ declare_clippy_lint! { suspicious, "using `crate` in a macro definition" } + declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 152516baf734..dabbd4c28de6 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -33,6 +33,8 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } +impl_lint_pass!(DbgMacro => [DBG_MACRO]); + pub struct DbgMacro { allow_dbg_in_tests: bool, /// Tracks the `dbg!` macro callsites that are already checked. @@ -41,8 +43,6 @@ pub struct DbgMacro { prev_ctxt: SyntaxContext, } -impl_lint_pass!(DbgMacro => [DBG_MACRO]); - impl DbgMacro { pub fn new(conf: &'static Conf) -> Self { DbgMacro { diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index a48e4d2fbd57..2064d896861b 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -69,14 +69,14 @@ declare_clippy_lint! { "binding initialized with Default should have its fields set in the initializer" } +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); + #[derive(Default)] pub struct Default { // Spans linted by `field_reassign_with_default`. reassigned_linted: FxHashSet, } -impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); - impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() diff --git a/clippy_lints/src/default_constructed_unit_structs.rs b/clippy_lints/src/default_constructed_unit_structs.rs index 95f0afcc3d88..c831f96443c6 100644 --- a/clippy_lints/src/default_constructed_unit_structs.rs +++ b/clippy_lints/src/default_constructed_unit_structs.rs @@ -45,6 +45,7 @@ declare_clippy_lint! { complexity, "unit structs can be constructed without calling `default`" } + declare_lint_pass!(DefaultConstructedUnitStructs => [ DEFAULT_CONSTRUCTED_UNIT_STRUCTS, ]); diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs index 056e39c02af9..f041fc95fdd2 100644 --- a/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/clippy_lints/src/default_instead_of_iter_empty.rs @@ -28,6 +28,7 @@ declare_clippy_lint! { style, "check `std::iter::Empty::default()` and replace with `std::iter::empty()`" } + declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]); impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty { diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index df6525ce040e..c04e07ee7f10 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -48,6 +48,7 @@ declare_clippy_lint! { restriction, "unions without a `#[repr(C)]` attribute" } + declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]); impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 7b6c303473a9..2627f95d7a1b 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -23,6 +23,29 @@ use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use std::borrow::Cow; +declare_clippy_lint! { + /// ### What it does + /// Checks for dereferencing expressions which would be covered by auto-deref. + /// + /// ### Why is this bad? + /// This unnecessarily complicates the code. + /// + /// ### Example + /// ```no_run + /// let x = String::new(); + /// let y: &str = &*x; + /// ``` + /// Use instead: + /// ```no_run + /// let x = String::new(); + /// let y: &str = &x; + /// ``` + #[clippy::version = "1.64.0"] + pub EXPLICIT_AUTO_DEREF, + complexity, + "dereferencing when the compiler would automatically dereference" +} + declare_clippy_lint! { /// ### What it does /// Checks for explicit `deref()` or `deref_mut()` method calls. @@ -118,29 +141,6 @@ declare_clippy_lint! { "`ref` binding to a reference" } -declare_clippy_lint! { - /// ### What it does - /// Checks for dereferencing expressions which would be covered by auto-deref. - /// - /// ### Why is this bad? - /// This unnecessarily complicates the code. - /// - /// ### Example - /// ```no_run - /// let x = String::new(); - /// let y: &str = &*x; - /// ``` - /// Use instead: - /// ```no_run - /// let x = String::new(); - /// let y: &str = &x; - /// ``` - #[clippy::version = "1.64.0"] - pub EXPLICIT_AUTO_DEREF, - complexity, - "dereferencing when the compiler would automatically dereference" -} - impl_lint_pass!(Dereferencing<'_> => [ EXPLICIT_AUTO_DEREF, EXPLICIT_DEREF_METHODS, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 06c2393e0a39..e8baf2b66a1e 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -56,6 +56,8 @@ declare_clippy_lint! { "manual implementation of the `Default` trait which is equal to a derive" } +impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); + pub struct DerivableImpls { msrv: Msrv, } @@ -66,8 +68,6 @@ impl DerivableImpls { } } -impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); - fn is_path_self(e: &Expr<'_>) -> bool { if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _)) diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index cf9ed10be02e..a15ef1a3edc5 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -10,36 +10,6 @@ mod derived_hash_with_manual_eq; mod expl_impl_clone_on_copy; mod unsafe_derive_deserialize; -declare_clippy_lint! { - /// ### What it does - /// Lints against manual `PartialEq` implementations for types with a derived `Hash` - /// implementation. - /// - /// ### Why is this bad? - /// The implementation of these traits must agree (for - /// example for use with `HashMap`) so it’s probably a bad idea to use a - /// default-generated `Hash` implementation with an explicitly defined - /// `PartialEq`. In particular, the following must hold for any type: - /// - /// ```text - /// k1 == k2 ⇒ hash(k1) == hash(k2) - /// ``` - /// - /// ### Example - /// ```ignore - /// #[derive(Hash)] - /// struct Foo; - /// - /// impl PartialEq for Foo { - /// ... - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub DERIVED_HASH_WITH_MANUAL_EQ, - correctness, - "deriving `Hash` but implementing `PartialEq` explicitly" -} - declare_clippy_lint! { /// ### What it does /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord` @@ -91,6 +61,68 @@ declare_clippy_lint! { "deriving `Ord` but implementing `PartialOrd` explicitly" } +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```no_run + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```no_run + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + nursery, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints against manual `PartialEq` implementations for types with a derived `Hash` + /// implementation. + /// + /// ### Why is this bad? + /// The implementation of these traits must agree (for + /// example for use with `HashMap`) so it’s probably a bad idea to use a + /// default-generated `Hash` implementation with an explicitly defined + /// `PartialEq`. In particular, the following must hold for any type: + /// + /// ```text + /// k1 == k2 ⇒ hash(k1) == hash(k2) + /// ``` + /// + /// ### Example + /// ```ignore + /// #[derive(Hash)] + /// struct Foo; + /// + /// impl PartialEq for Foo { + /// ... + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub DERIVED_HASH_WITH_MANUAL_EQ, + correctness, + "deriving `Hash` but implementing `PartialEq` explicitly" +} + declare_clippy_lint! { /// ### What it does /// Checks for explicit `Clone` implementations for `Copy` @@ -152,38 +184,6 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for types that derive `PartialEq` and could implement `Eq`. - /// - /// ### Why is this bad? - /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, - /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used - /// in APIs that require `Eq` types. It also allows structs containing `T` to derive - /// `Eq` themselves. - /// - /// ### Example - /// ```no_run - /// #[derive(PartialEq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - /// Use instead: - /// ```no_run - /// #[derive(PartialEq, Eq)] - /// struct Foo { - /// i_am_eq: i32, - /// i_am_eq_too: Vec, - /// } - /// ``` - #[clippy::version = "1.63.0"] - pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, - nursery, - "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" -} - declare_lint_pass!(Derive => [ DERIVED_HASH_WITH_MANUAL_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 1c9c971730f6..7253b65e4337 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -63,6 +63,8 @@ declare_clippy_lint! { "use of a disallowed macro" } +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); + pub struct DisallowedMacros { disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, seen: FxHashSet, @@ -125,8 +127,6 @@ impl DisallowedMacros { } } -impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); - impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { // once we check a crate in the late pass we can emit the early pass lints diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 8c067432cb4e..b34671a04450 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -61,6 +61,8 @@ declare_clippy_lint! { "use of a disallowed method call" } +impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); + pub struct DisallowedMethods { disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } @@ -84,8 +86,6 @@ impl DisallowedMethods { } } -impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); - impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { diff --git a/clippy_lints/src/disallowed_names.rs b/clippy_lints/src/disallowed_names.rs index 566aa12b08f5..1f658b2aa068 100644 --- a/clippy_lints/src/disallowed_names.rs +++ b/clippy_lints/src/disallowed_names.rs @@ -26,6 +26,8 @@ declare_clippy_lint! { "usage of a disallowed/placeholder name" } +impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); + pub struct DisallowedNames { disallow: FxHashSet, } @@ -38,8 +40,6 @@ impl DisallowedNames { } } -impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); - impl<'tcx> LateLintPass<'tcx> for DisallowedNames { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { if let PatKind::Binding(.., ident, _) = pat.kind diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index cf964d4b5804..3721642fed2b 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -45,6 +45,8 @@ declare_clippy_lint! { "usage of non-allowed Unicode scripts" } +impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]); + pub struct DisallowedScriptIdents { whitelist: FxHashSet