Skip to content

Commit ed2d130

Browse files
committed
clippy_dev: Split gathering lint decls from parsing deprecated lints.
1 parent 664bae0 commit ed2d130

File tree

5 files changed

+172
-116
lines changed

5 files changed

+172
-116
lines changed

clippy_dev/src/deprecate_lint.rs

Lines changed: 25 additions & 13 deletions
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

Lines changed: 27 additions & 18 deletions
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

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
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, unescape};
45
use std::collections::{HashMap, HashSet};
5-
use std::ffi::OsStr;
66
use std::fmt::Write;
7-
use std::fs;
7+
use std::fs::OpenOptions;
88
use std::ops::Range;
99
use std::path::Path;
1010
use walkdir::{DirEntry, WalkDir};
@@ -25,8 +25,11 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht
2525
///
2626
/// Panics if a file path could not read from or then written to
2727
pub fn update(update_mode: UpdateMode) {
28-
let (lints, deprecated_lints, renamed_lints) = gather_all();
29-
generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
28+
let lints = find_lint_decls();
29+
let DeprecatedLints {
30+
renamed, deprecated, ..
31+
} = read_deprecated_lints();
32+
generate_lint_files(update_mode, &lints, &deprecated, &renamed);
3033
}
3134

3235
pub fn generate_lint_files(
@@ -35,8 +38,6 @@ pub fn generate_lint_files(
3538
deprecated: &[DeprecatedLint],
3639
renamed: &[RenamedLint],
3740
) {
38-
let mut lints = lints.to_owned();
39-
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
4041
FileUpdater::default().update_files_checked(
4142
"cargo dev lint",
4243
update_mode,
@@ -106,7 +107,7 @@ pub fn generate_lint_files(
106107
}
107108

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

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

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

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

243258
macro_rules! match_tokens {
@@ -265,7 +280,7 @@ pub(crate) struct LintDeclSearchResult<'a> {
265280
}
266281

267282
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
268-
fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
283+
fn parse_clippy_lint_decls(contents: &str, module: &str, lints: &mut Vec<Lint>) {
269284
let mut offset = 0usize;
270285
let mut iter = tokenize(contents).map(|t| {
271286
let range = offset..offset + t.len as usize;
@@ -332,15 +347,40 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
332347
}
333348
}
334349

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

374+
res.file.read_append_to_string(&mut res.contents);
375+
376+
let (_, contents) = res.contents.split_once("\ndeclare_with_version! { DEPRECATED").unwrap();
377+
let (deprecated_src, contents) = contents.split_once("\n]}").unwrap();
378+
res.deprecated_end = (res.contents.len() - contents.len() - 2) as u32;
379+
380+
let (_, contents) = contents.split_once("\ndeclare_with_version! { RENAMED").unwrap();
381+
let (renamed_src, contents) = contents.split_once("\n]}").unwrap();
382+
res.renamed_end = (res.contents.len() - contents.len() - 2) as u32;
383+
344384
for line in deprecated_src.lines() {
345385
let mut offset = 0usize;
346386
let mut iter = tokenize(line).map(|t| {
@@ -361,7 +401,7 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
361401
// "new_name"),
362402
Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma
363403
);
364-
deprecated.push(DeprecatedLint::new(name, reason));
404+
res.deprecated.push(DeprecatedLint::new(name, reason));
365405
}
366406
for line in renamed_src.lines() {
367407
let mut offset = 0usize;
@@ -383,8 +423,10 @@ fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint
383423
// "new_name"),
384424
Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
385425
);
386-
renamed.push(RenamedLint::new(old_name, new_name));
426+
res.renamed.push(RenamedLint::new(old_name, new_name));
387427
}
428+
429+
res
388430
}
389431

390432
/// Removes the line splices and surrounding quotes from a string literal
@@ -410,7 +452,7 @@ mod tests {
410452
use super::*;
411453

412454
#[test]
413-
fn test_parse_contents() {
455+
fn test_parse_clippy_lint_decls() {
414456
static CONTENTS: &str = r#"
415457
declare_clippy_lint! {
416458
#[clippy::version = "Hello Clippy!"]
@@ -428,7 +470,7 @@ mod tests {
428470
}
429471
"#;
430472
let mut result = Vec::new();
431-
parse_contents(CONTENTS, "module_name", &mut result);
473+
parse_clippy_lint_decls(CONTENTS, "module_name", &mut result);
432474
for r in &mut result {
433475
r.declaration_range = Range::default();
434476
}

0 commit comments

Comments
 (0)