diff options
| -rw-r--r-- | clippy_dev/src/deprecate_lint.rs | 38 | ||||
| -rw-r--r-- | clippy_dev/src/rename_lint.rs | 45 | ||||
| -rw-r--r-- | clippy_dev/src/update_lints.rs | 140 | ||||
| -rw-r--r-- | clippy_dev/src/utils.rs | 63 | ||||
| -rw-r--r-- | clippy_lints/src/deprecated_lints.rs | 2 |
5 files changed, 172 insertions, 116 deletions
diff --git a/clippy_dev/src/deprecate_lint.rs b/clippy_dev/src/deprecate_lint.rs index 2c4286b325d..bf0e7771046 100644 --- a/clippy_dev/src/deprecate_lint.rs +++ b/clippy_dev/src/deprecate_lint.rs @@ -1,5 +1,7 @@ -use crate::update_lints::{DeprecatedLint, Lint, gather_all, generate_lint_files}; -use crate::utils::{UpdateMode, Version, insert_at_marker, rewrite_file}; +use crate::update_lints::{ + DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints, +}; +use crate::utils::{UpdateMode, Version}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::{fs, io}; @@ -21,7 +23,16 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { }; let stripped_name = &prefixed_name[8..]; - let (mut lints, mut deprecated_lints, renamed_lints) = gather_all(); + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: renamed_lints, + deprecated: mut deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + deprecated_end, + .. + } = read_deprecated_lints(); + let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else { eprintln!("error: failed to find lint `{name}`"); return; @@ -38,16 +49,17 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) { }; if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) { - rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| { - insert_at_marker( - s, - "// end deprecated lints. used by `cargo dev deprecate_lint`", - &format!( - "#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ", - clippy_version.rust_display(), - ), - ) - }); + deprecated_contents.insert_str( + deprecated_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + prefixed_name, + reason, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); deprecated_lints.push(DeprecatedLint { name: prefixed_name, diff --git a/clippy_dev/src/rename_lint.rs b/clippy_dev/src/rename_lint.rs index c78ec98c1aa..9e7e5d97f02 100644 --- a/clippy_dev/src/rename_lint.rs +++ b/clippy_dev/src/rename_lint.rs @@ -1,7 +1,8 @@ use crate::update_lints::{ - RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test_fn, generate_lint_files, + DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files, + read_deprecated_lints, }; -use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, insert_at_marker, rewrite_file, try_rename_file}; +use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file}; use std::ffi::OsStr; use std::path::Path; use walkdir::WalkDir; @@ -31,7 +32,16 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b } let mut updater = FileUpdater::default(); - let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut lints = find_lint_decls(); + let DeprecatedLints { + renamed: mut renamed_lints, + deprecated: deprecated_lints, + file: mut deprecated_file, + contents: mut deprecated_contents, + renamed_end, + .. + } = read_deprecated_lints(); + let mut old_lint_index = None; let mut found_new_name = false; for (i, lint) in lints.iter().enumerate() { @@ -76,19 +86,17 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } - rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| { - insert_at_marker( - s, - "// end renamed lints. used by `cargo dev rename_lint`", - &format!( - "#[clippy::version = \"{}\"]\n \ - (\"{}\", \"{}\"),\n ", - clippy_version.rust_display(), - lint.old_name, - lint.new_name, - ), - ) - }); + deprecated_contents.insert_str( + renamed_end as usize, + &format!( + " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", + clippy_version.rust_display(), + lint.old_name, + lint.new_name, + ), + ); + deprecated_file.replace_contents(deprecated_contents.as_bytes()); + drop(deprecated_file); renamed_lints.push(lint); renamed_lints.sort_by(|lhs, rhs| { @@ -166,12 +174,13 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being // renamed. let replacer = StringReplacer::new(replacements); - for file in clippy_lints_src_files() { + for file in WalkDir::new("clippy_lints/src") { + let file = file.expect("error reading `clippy_lints/src`"); if file .path() .as_os_str() .to_str() - .is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") + .is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs") { updater.update_file(file.path(), &mut replacer.replace_ident_fn()); } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index ad995f5e4c2..28c988bc19f 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,11 +1,11 @@ -use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; +use crate::utils::{File, FileAction, FileUpdater, UpdateMode, UpdateStatus, panic_file, update_text_region_fn}; +use core::str; use itertools::Itertools; use rustc_lexer::{LiteralKind, TokenKind, tokenize}; use rustc_literal_escaper::{Mode, unescape_unicode}; use std::collections::{HashMap, HashSet}; -use std::ffi::OsStr; use std::fmt::Write; -use std::fs; +use std::fs::OpenOptions; use std::ops::Range; use std::path::Path; use walkdir::{DirEntry, WalkDir}; @@ -26,8 +26,11 @@ 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(update_mode: UpdateMode) { - let (lints, deprecated_lints, renamed_lints) = gather_all(); - generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); + let lints = find_lint_decls(); + let DeprecatedLints { + renamed, deprecated, .. + } = read_deprecated_lints(); + generate_lint_files(update_mode, &lints, &deprecated, &renamed); } pub fn generate_lint_files( @@ -36,8 +39,6 @@ pub fn generate_lint_files( deprecated: &[DeprecatedLint], renamed: &[RenamedLint], ) { - let mut lints = lints.to_owned(); - lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); FileUpdater::default().update_files_checked( "cargo dev update_lints", update_mode, @@ -107,7 +108,7 @@ pub fn generate_lint_files( } pub fn print_lints() { - let (lints, _, _) = gather_all(); + let lints = find_lint_decls(); let lint_count = lints.len(); let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter()); @@ -205,40 +206,54 @@ pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str, } } -/// Gathers all lints defined in `clippy_lints/src` +/// Finds all lint declarations (`declare_clippy_lint!`) #[must_use] -pub fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) { +pub fn find_lint_decls() -> Vec<Lint> { let mut lints = Vec::with_capacity(1000); - let mut deprecated_lints = Vec::with_capacity(50); - let mut renamed_lints = Vec::with_capacity(50); - - for file in clippy_lints_src_files() { - let path = file.path(); - let contents = - fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display())); - let module = path.as_os_str().to_str().unwrap()["clippy_lints/src/".len()..].replace(['/', '\\'], "::"); - - // If the lints are stored in mod.rs, we get the module name from - // the containing directory: - let module = if let Some(module) = module.strip_suffix("::mod.rs") { - module - } else { - module.strip_suffix(".rs").unwrap_or(&module) - }; - - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints); - } else { - parse_contents(&contents, module, &mut lints); - } + let mut contents = String::new(); + for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) { + parse_clippy_lint_decls( + File::open_read_to_cleared_string(file.path(), &mut contents), + &module, + &mut lints, + ); } - (lints, deprecated_lints, renamed_lints) + lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); + lints } -pub fn clippy_lints_src_files() -> impl Iterator<Item = DirEntry> { - let iter = WalkDir::new("clippy_lints/src").into_iter(); - iter.map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) +/// Reads the source files from the given root directory +fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> { + WalkDir::new(src_root).into_iter().filter_map(move |e| { + let e = match e { + Ok(e) => e, + Err(ref e) => panic_file(e, FileAction::Read, src_root), + }; + let path = e.path().as_os_str().as_encoded_bytes(); + if let Some(path) = path.strip_suffix(b".rs") + && let Some(path) = path.get("clippy_lints/src/".len()..) + { + if path == b"lib" { + Some((e, String::new())) + } else { + let path = if let Some(path) = path.strip_suffix(b"mod") + && let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\")) + { + path + } else { + path + }; + if let Ok(path) = str::from_utf8(path) { + let path = path.replace(['/', '\\'], "::"); + Some((e, path)) + } else { + None + } + } + } else { + None + } + }) } macro_rules! match_tokens { @@ -266,7 +281,7 @@ pub(crate) struct LintDeclSearchResult<'a> { } /// Parse a source file looking for `declare_clippy_lint` macro invocations. -fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { +fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec<Lint>) { let mut offset = 0usize; let mut iter = tokenize(contents).map(|t| { let range = offset..offset + t.len as usize; @@ -333,15 +348,40 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) { } } -/// Parse a source file looking for `declare_deprecated_lint` macro invocations. -fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) { - let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else { - return; - }; - let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else { - return; +pub struct DeprecatedLints { + pub file: File<'static>, + pub contents: String, + pub deprecated: Vec<DeprecatedLint>, + pub renamed: Vec<RenamedLint>, + pub deprecated_end: u32, + pub renamed_end: u32, +} + +#[must_use] +#[expect(clippy::cast_possible_truncation)] +pub fn read_deprecated_lints() -> DeprecatedLints { + let mut res = DeprecatedLints { + file: File::open( + "clippy_lints/src/deprecated_lints.rs", + OpenOptions::new().read(true).write(true), + ), + contents: String::new(), + deprecated: Vec::with_capacity(30), + renamed: Vec::with_capacity(80), + deprecated_end: 0, + renamed_end: 0, }; + res.file.read_append_to_string(&mut res.contents); + + let (_, contents) = res.contents.split_once("\ndeclare_with_version! { DEPRECATED").unwrap(); + let (deprecated_src, contents) = contents.split_once("\n]}").unwrap(); + res.deprecated_end = (res.contents.len() - contents.len() - 2) as u32; + + let (_, contents) = contents.split_once("\ndeclare_with_version! { RENAMED").unwrap(); + let (renamed_src, contents) = contents.split_once("\n]}").unwrap(); + res.renamed_end = (res.contents.len() - contents.len() - 2) as u32; + for line in deprecated_src.lines() { let mut offset = 0usize; let mut iter = tokenize(line).map(|t| { @@ -362,7 +402,7 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint // "new_name"), Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma ); - deprecated.push(DeprecatedLint::new(name, reason)); + res.deprecated.push(DeprecatedLint::new(name, reason)); } for line in renamed_src.lines() { let mut offset = 0usize; @@ -384,8 +424,10 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint // "new_name"), Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma ); - renamed.push(RenamedLint::new(old_name, new_name)); + res.renamed.push(RenamedLint::new(old_name, new_name)); } + + res } /// Removes the line splices and surrounding quotes from a string literal @@ -411,7 +453,7 @@ mod tests { use super::*; #[test] - fn test_parse_contents() { + fn test_parse_clippy_lint_decls() { static CONTENTS: &str = r#" declare_clippy_lint! { #[clippy::version = "Hello Clippy!"] @@ -429,7 +471,7 @@ mod tests { } "#; let mut result = Vec::new(); - parse_contents(CONTENTS, "module_name", &mut result); + parse_clippy_lint_decls(CONTENTS, "module_name", &mut result); for r in &mut result { r.declaration_range = Range::default(); } diff --git a/clippy_dev/src/utils.rs b/clippy_dev/src/utils.rs index 9f48832af00..87645aff674 100644 --- a/clippy_dev/src/utils.rs +++ b/clippy_dev/src/utils.rs @@ -12,10 +12,30 @@ static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; #[cfg(windows)] static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe"; +#[derive(Clone, Copy)] +pub enum FileAction { + Open, + Read, + Write, + Create, + Rename, +} +impl FileAction { + fn as_str(self) -> &'static str { + match self { + Self::Open => "opening", + Self::Read => "reading", + Self::Write => "writing", + Self::Create => "creating", + Self::Rename => "renaming", + } + } +} + #[cold] #[track_caller] -fn panic_io(e: &io::Error, action: &str, path: &Path) -> ! { - panic!("error {action} `{}`: {}", path.display(), *e) +pub fn panic_file(err: &impl Display, action: FileAction, path: &Path) -> ! { + panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) } /// Wrapper around `std::fs::File` which panics with a path on failure. @@ -30,7 +50,7 @@ impl<'a> File<'a> { let path = path.as_ref(); match options.open(path) { Ok(inner) => Self { inner, path }, - Err(e) => panic_io(&e, "opening", path), + Err(e) => panic_file(&e, FileAction::Open, path), } } @@ -41,7 +61,7 @@ impl<'a> File<'a> { match options.open(path) { Ok(inner) => Some(Self { inner, path }), Err(e) if e.kind() == io::ErrorKind::NotFound => None, - Err(e) => panic_io(&e, "opening", path), + Err(e) => panic_file(&e, FileAction::Open, path), } } @@ -59,7 +79,7 @@ impl<'a> File<'a> { pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { match self.inner.read_to_string(dst) { Ok(_) => {}, - Err(e) => panic_io(&e, "reading", self.path), + Err(e) => panic_file(&e, FileAction::Read, self.path), } dst } @@ -81,7 +101,7 @@ impl<'a> File<'a> { Err(e) => Err(e), }; if let Err(e) = res { - panic_io(&e, "writing", self.path); + panic_file(&e, FileAction::Write, self.path); } } } @@ -391,7 +411,7 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { match OpenOptions::new().create_new(true).write(true).open(new_name) { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, - Err(e) => panic_io(&e, "creating", new_name), + Err(e) => panic_file(&e, FileAction::Create, new_name), } match fs::rename(old_name, new_name) { Ok(()) => true, @@ -400,37 +420,12 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { if e.kind() == io::ErrorKind::NotFound { false } else { - panic_io(&e, "renaming", old_name); + panic_file(&e, FileAction::Rename, old_name); } }, } } -#[must_use] -pub fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> { - let i = text.find(marker)?; - let (pre, post) = text.split_at(i); - Some([pre, new_text, post].into_iter().collect()) -} - -pub fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) { - let mut file = OpenOptions::new() - .write(true) - .read(true) - .open(path) - .unwrap_or_else(|e| panic_io(&e, "opening", path)); - let mut buf = String::new(); - file.read_to_string(&mut buf) - .unwrap_or_else(|e| panic_io(&e, "reading", path)); - if let Some(new_contents) = f(&buf) { - file.rewind().unwrap_or_else(|e| panic_io(&e, "writing", path)); - file.write_all(new_contents.as_bytes()) - .unwrap_or_else(|e| panic_io(&e, "writing", path)); - file.set_len(new_contents.len() as u64) - .unwrap_or_else(|e| panic_io(&e, "writing", path)); - } -} - pub fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_io(&e, "writing", path)); + fs::write(path, contents).unwrap_or_else(|e| panic_file(&e, FileAction::Write, path)); } diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index a1909c5363f..d37b0efd35d 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -44,7 +44,6 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[ ("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"), #[clippy::version = "1.86.0"] ("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"), - // end deprecated lints. used by `cargo dev deprecate_lint` ]} #[rustfmt::skip] @@ -195,5 +194,4 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[ ("clippy::transmute_float_to_int", "unnecessary_transmutes"), #[clippy::version = "1.88.0"] ("clippy::transmute_num_to_bytes", "unnecessary_transmutes"), - // end renamed lints. used by `cargo dev rename_lint` ]} |
