Skip to content

Commit de5769a

Browse files
committed
clippy_dev: Split gathering lint decls from parsing deprecated lints.
1 parent 743f1ee commit de5769a

File tree

5 files changed

+172
-116
lines changed

5 files changed

+172
-116
lines changed

clippy_dev/src/deprecate_lint.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
use crate::update_lints::{DeprecatedLint, Lint, gather_all, generate_lint_files};
2-
use crate::utils::{UpdateMode, Version, insert_at_marker, rewrite_file};
1+
use crate::update_lints::{
2+
DeprecatedLint, DeprecatedLints, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints,
3+
};
4+
use crate::utils::{UpdateMode, Version};
35
use std::ffi::OsStr;
46
use std::path::{Path, PathBuf};
57
use std::{fs, io};
@@ -21,7 +23,16 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
2123
};
2224
let stripped_name = &prefixed_name[8..];
2325

24-
let (mut lints, mut deprecated_lints, renamed_lints) = gather_all();
26+
let mut lints = find_lint_decls();
27+
let DeprecatedLints {
28+
renamed: renamed_lints,
29+
deprecated: mut deprecated_lints,
30+
file: mut deprecated_file,
31+
contents: mut deprecated_contents,
32+
deprecated_end,
33+
..
34+
} = read_deprecated_lints();
35+
2536
let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else {
2637
eprintln!("error: failed to find lint `{name}`");
2738
return;
@@ -38,16 +49,17 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
3849
};
3950

4051
if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) {
41-
rewrite_file("clippy_lints/src/deprecated_lints.rs".as_ref(), |s| {
42-
insert_at_marker(
43-
s,
44-
"// end deprecated lints. used by `cargo dev deprecate_lint`",
45-
&format!(
46-
"#[clippy::version = \"{}\"]\n (\"{prefixed_name}\", \"{reason}\"),\n ",
47-
clippy_version.rust_display(),
48-
),
49-
)
50-
});
52+
deprecated_contents.insert_str(
53+
deprecated_end as usize,
54+
&format!(
55+
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
56+
clippy_version.rust_display(),
57+
prefixed_name,
58+
reason,
59+
),
60+
);
61+
deprecated_file.replace_contents(deprecated_contents.as_bytes());
62+
drop(deprecated_file);
5163

5264
deprecated_lints.push(DeprecatedLint {
5365
name: prefixed_name,

clippy_dev/src/rename_lint.rs

+27-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::update_lints::{
2-
RenamedLint, clippy_lints_src_files, gather_all, gen_renamed_lints_test_fn, generate_lint_files,
2+
DeprecatedLints, RenamedLint, find_lint_decls, gen_renamed_lints_test_fn, generate_lint_files,
3+
read_deprecated_lints,
34
};
4-
use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, insert_at_marker, rewrite_file, try_rename_file};
5+
use crate::utils::{FileUpdater, StringReplacer, UpdateMode, Version, try_rename_file};
56
use std::ffi::OsStr;
67
use std::path::Path;
78
use walkdir::WalkDir;
@@ -31,7 +32,16 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
3132
}
3233

3334
let mut updater = FileUpdater::default();
34-
let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
35+
let mut lints = find_lint_decls();
36+
let DeprecatedLints {
37+
renamed: mut renamed_lints,
38+
deprecated: deprecated_lints,
39+
file: mut deprecated_file,
40+
contents: mut deprecated_contents,
41+
renamed_end,
42+
..
43+
} = read_deprecated_lints();
44+
3545
let mut old_lint_index = None;
3646
let mut found_new_name = false;
3747
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
7686
updater.update_file(file.path(), &mut replacer.replace_ident_fn());
7787
}
7888

79-
rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| {
80-
insert_at_marker(
81-
s,
82-
"// end renamed lints. used by `cargo dev rename_lint`",
83-
&format!(
84-
"#[clippy::version = \"{}\"]\n \
85-
(\"{}\", \"{}\"),\n ",
86-
clippy_version.rust_display(),
87-
lint.old_name,
88-
lint.new_name,
89-
),
90-
)
91-
});
89+
deprecated_contents.insert_str(
90+
renamed_end as usize,
91+
&format!(
92+
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
93+
clippy_version.rust_display(),
94+
lint.old_name,
95+
lint.new_name,
96+
),
97+
);
98+
deprecated_file.replace_contents(deprecated_contents.as_bytes());
99+
drop(deprecated_file);
92100

93101
renamed_lints.push(lint);
94102
renamed_lints.sort_by(|lhs, rhs| {
@@ -166,12 +174,13 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
166174
// Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
167175
// renamed.
168176
let replacer = StringReplacer::new(replacements);
169-
for file in clippy_lints_src_files() {
177+
for file in WalkDir::new("clippy_lints/src") {
178+
let file = file.expect("error reading `clippy_lints/src`");
170179
if file
171180
.path()
172181
.as_os_str()
173182
.to_str()
174-
.is_none_or(|x| x["clippy_lints/src/".len()..] != *"deprecated_lints.rs")
183+
.is_some_and(|x| x.ends_with("*.rs") && x["clippy_lints/src/".len()..] != *"deprecated_lints.rs")
175184
{
176185
updater.update_file(file.path(), &mut replacer.replace_ident_fn());
177186
}

clippy_dev/src/update_lints.rs

+91-49
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
1+
use crate::utils::{File, FileAction, FileUpdater, UpdateMode, UpdateStatus, panic_file, update_text_region_fn};
2+
use core::str;
23
use itertools::Itertools;
34
use rustc_lexer::{LiteralKind, TokenKind, tokenize};
45
use rustc_literal_escaper::{Mode, unescape_unicode};
56
use std::collections::{HashMap, HashSet};
6-
use std::ffi::OsStr;
77
use std::fmt::Write;
8-
use std::fs;
8+
use std::fs::OpenOptions;
99
use std::ops::Range;
1010
use std::path::Path;
1111
use walkdir::{DirEntry, WalkDir};
@@ -26,8 +26,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht
2626
///
2727
/// Panics if a file path could not read from or then written to
2828
pub fn update(update_mode: UpdateMode) {
29-
let (lints, deprecated_lints, renamed_lints) = gather_all();
30-
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
29+
let lints = find_lint_decls();
30+
let DeprecatedLints {
31+
renamed, deprecated, ..
32+
} = read_deprecated_lints();
33+
generate_lint_files(update_mode, &lints, &deprecated, &renamed);
3134
}
3235

3336
pub fn generate_lint_files(
@@ -36,8 +39,6 @@ pub fn generate_lint_files(
3639
deprecated: &[DeprecatedLint],
3740
renamed: &[RenamedLint],
3841
) {
39-
let mut lints = lints.to_owned();
40-
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
4142
FileUpdater::default().update_files_checked(
4243
"cargo dev lint",
4344
update_mode,
@@ -107,7 +108,7 @@ pub fn generate_lint_files(
107108
}
108109

109110
pub fn print_lints() {
110-
let (lints, _, _) = gather_all();
111+
let lints = find_lint_decls();
111112
let lint_count = lints.len();
112113
let grouped_by_lint_group = Lint::by_lint_group(lints.into_iter());
113114

@@ -205,40 +206,54 @@ pub fn gen_renamed_lints_test_fn(lints: &[RenamedLint]) -> impl Fn(&Path, &str,
205206
}
206207
}
207208

208-
/// Gathers all lints defined in `clippy_lints/src`
209+
/// Finds all lint declarations (`declare_clippy_lint!`)
209210
#[must_use]
210-
pub fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
211+
pub fn find_lint_decls() -> Vec<Lint> {
211212
let mut lints = Vec::with_capacity(1000);
212-
let mut deprecated_lints = Vec::with_capacity(50);
213-
let mut renamed_lints = Vec::with_capacity(50);
214-
215-
for file in clippy_lints_src_files() {
216-
let path = file.path();
217-
let contents =
218-
fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
219-
let module = path.as_os_str().to_str().unwrap()["clippy_lints/src/".len()..].replace(['/', '\\'], "::");
220-
221-
// If the lints are stored in mod.rs, we get the module name from
222-
// the containing directory:
223-
let module = if let Some(module) = module.strip_suffix("::mod.rs") {
224-
module
225-
} else {
226-
module.strip_suffix(".rs").unwrap_or(&module)
227-
};
228-
229-
if module == "deprecated_lints" {
230-
parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints);
231-
} else {
232-
parse_contents(&contents, module, &mut lints);
233-
}
213+
let mut contents = String::new();
214+
for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) {
215+
parse_clippy_lint_decls(
216+
File::open_read_to_cleared_string(file.path(), &mut contents),
217+
&module,
218+
&mut lints,
219+
);
234220
}
235-
(lints, deprecated_lints, renamed_lints)
221+
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
222+
lints
236223
}
237224

238-
pub fn clippy_lints_src_files() -> impl Iterator<Item = DirEntry> {
239-
let iter = WalkDir::new("clippy_lints/src").into_iter();
240-
iter.map(Result::unwrap)
241-
.filter(|f| f.path().extension() == Some(OsStr::new("rs")))
225+
/// Reads the source files from the given root directory
226+
fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> {
227+
WalkDir::new(src_root).into_iter().filter_map(move |e| {
228+
let e = match e {
229+
Ok(e) => e,
230+
Err(ref e) => panic_file(e, FileAction::Read, src_root),
231+
};
232+
let path = e.path().as_os_str().as_encoded_bytes();
233+
if let Some(path) = path.strip_suffix(b".rs")
234+
&& let Some(path) = path.get("clippy_lints/src/".len()..)
235+
{
236+
if path == b"lib" {
237+
Some((e, String::new()))
238+
} else {
239+
let path = if let Some(path) = path.strip_suffix(b"mod")
240+
&& let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\"))
241+
{
242+
path
243+
} else {
244+
path
245+
};
246+
if let Ok(path) = str::from_utf8(path) {
247+
let path = path.replace(['/', '\\'], "::");
248+
Some((e, path))
249+
} else {
250+
None
251+
}
252+
}
253+
} else {
254+
None
255+
}
256+
})
242257
}
243258

244259
macro_rules! match_tokens {
@@ -266,7 +281,7 @@ pub(crate) struct LintDeclSearchResult<'a> {
266281
}
267282

268283
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
269-
fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
284+
fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec<Lint>) {
270285
let mut offset = 0usize;
271286
let mut iter = tokenize(contents).map(|t| {
272287
let range = offset..offset + t.len as usize;
@@ -333,15 +348,40 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
333348
}
334349
}
335350

336-
/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
337-
fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) {
338-
let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else {
339-
return;
340-
};
341-
let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else {
342-
return;
351+
pub struct DeprecatedLints {
352+
pub file: File<'static>,
353+
pub contents: String,
354+
pub deprecated: Vec<DeprecatedLint>,
355+
pub renamed: Vec<RenamedLint>,
356+
pub deprecated_end: u32,
357+
pub renamed_end: u32,
358+
}
359+
360+
#[must_use]
361+
#[expect(clippy::cast_possible_truncation)]
362+
pub fn read_deprecated_lints() -> DeprecatedLints {
363+
let mut res = DeprecatedLints {
364+
file: File::open(
365+
"clippy_lints/src/deprecated_lints.rs",
366+
OpenOptions::new().read(true).write(true),
367+
),
368+
contents: String::new(),
369+
deprecated: Vec::with_capacity(30),
370+
renamed: Vec::with_capacity(80),
371+
deprecated_end: 0,
372+
renamed_end: 0,
343373
};
344374

375+
res.file.read_append_to_string(&mut res.contents);
376+
377+
let (_, contents) = res.contents.split_once("\ndeclare_with_version! { DEPRECATED").unwrap();
378+
let (deprecated_src, contents) = contents.split_once("\n]}").unwrap();
379+
res.deprecated_end = (res.contents.len() - contents.len() - 2) as u32;
380+
381+
let (_, contents) = contents.split_once("\ndeclare_with_version! { RENAMED").unwrap();
382+
let (renamed_src, contents) = contents.split_once("\n]}").unwrap();
383+
res.renamed_end = (res.contents.len() - contents.len() - 2) as u32;
384+
345385
for line in deprecated_src.lines() {
346386
let mut offset = 0usize;
347387
let mut iter = tokenize(line).map(|t| {
@@ -362,7 +402,7 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
362402
// "new_name"),
363403
Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma
364404
);
365-
deprecated.push(DeprecatedLint::new(name, reason));
405+
res.deprecated.push(DeprecatedLint::new(name, reason));
366406
}
367407
for line in renamed_src.lines() {
368408
let mut offset = 0usize;
@@ -384,8 +424,10 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
384424
// "new_name"),
385425
Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
386426
);
387-
renamed.push(RenamedLint::new(old_name, new_name));
427+
res.renamed.push(RenamedLint::new(old_name, new_name));
388428
}
429+
430+
res
389431
}
390432

391433
/// Removes the line splices and surrounding quotes from a string literal
@@ -411,7 +453,7 @@ mod tests {
411453
use super::*;
412454

413455
#[test]
414-
fn test_parse_contents() {
456+
fn test_parse_clippy_lint_decls() {
415457
static CONTENTS: &str = r#"
416458
declare_clippy_lint! {
417459
#[clippy::version = "Hello Clippy!"]
@@ -429,7 +471,7 @@ mod tests {
429471
}
430472
"#;
431473
let mut result = Vec::new();
432-
parse_contents(CONTENTS, "module_name", &mut result);
474+
parse_clippy_lint_decls(CONTENTS, "module_name", &mut result);
433475
for r in &mut result {
434476
r.declaration_range = Range::default();
435477
}

0 commit comments

Comments
 (0)