Skip to content

Commit 79380c6

Browse files
committed
Prepare to split lints into multiple crates
* Move `declare_clippy_lint` to it's own crate * Move lint/group registration into the driver * Make `dev update_lints` handle multiple lint crates
1 parent 5dccb10 commit 79380c6

18 files changed

+475
-431
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ path = "src/driver.rs"
2424
clippy_config = { path = "clippy_config" }
2525
clippy_lints = { path = "clippy_lints" }
2626
clippy_utils = { path = "clippy_utils" }
27+
declare_clippy_lint = { path = "declare_clippy_lint" }
2728
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
2829
clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
2930
tempfile = { version = "3.20", optional = true }

clippy_dev/src/release.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ static CARGO_TOML_FILES: &[&str] = &[
55
"clippy_config/Cargo.toml",
66
"clippy_lints/Cargo.toml",
77
"clippy_utils/Cargo.toml",
8+
"declare_clippy_lint/Cargo.toml",
89
"Cargo.toml",
910
];
1011

clippy_dev/src/update_lints.rs

Lines changed: 168 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use crate::utils::{
44
use itertools::Itertools;
55
use std::collections::HashSet;
66
use std::fmt::Write;
7+
use std::fs;
78
use std::ops::Range;
8-
use std::path::{Path, PathBuf};
9+
use std::path::{self, Path, PathBuf};
910
use walkdir::{DirEntry, WalkDir};
1011

1112
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
@@ -36,123 +37,164 @@ pub fn generate_lint_files(
3637
deprecated: &[DeprecatedLint],
3738
renamed: &[RenamedLint],
3839
) {
39-
FileUpdater::default().update_files_checked(
40+
let mut updater = FileUpdater::default();
41+
updater.update_file_checked(
4042
"cargo dev update_lints",
4143
update_mode,
42-
&mut [
43-
(
44-
"README.md",
45-
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
46-
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
47-
}),
48-
),
49-
(
50-
"book/src/README.md",
51-
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
52-
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
53-
}),
54-
),
55-
(
56-
"CHANGELOG.md",
57-
&mut update_text_region_fn(
58-
"<!-- begin autogenerated links to lint list -->\n",
59-
"<!-- end autogenerated links to lint list -->",
60-
|dst| {
61-
for lint in lints
62-
.iter()
63-
.map(|l| &*l.name)
64-
.chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
65-
.chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
66-
.sorted()
67-
{
68-
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
69-
}
70-
},
71-
),
72-
),
73-
(
74-
"clippy_lints/src/lib.rs",
75-
&mut update_text_region_fn(
76-
"// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
77-
"// end lints modules, do not remove this comment, it’s used in `update_lints`",
78-
|dst| {
79-
for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
80-
writeln!(dst, "mod {lint_mod};").unwrap();
81-
}
82-
},
83-
),
84-
),
85-
("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| {
86-
dst.push_str(GENERATED_FILE_COMMENT);
87-
dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
88-
for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
89-
writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap();
90-
}
91-
dst.push_str("];\n");
92-
UpdateStatus::from_changed(src != dst)
93-
}),
94-
("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| {
95-
let mut searcher = RustSearcher::new(src);
96-
assert!(
97-
searcher.find_token(Token::Ident("declare_with_version"))
98-
&& searcher.find_token(Token::Ident("declare_with_version")),
99-
"error reading deprecated lints"
100-
);
101-
dst.push_str(&src[..searcher.pos() as usize]);
102-
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
103-
for lint in deprecated {
104-
write!(
105-
dst,
106-
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
107-
lint.version, lint.name, lint.reason,
108-
)
109-
.unwrap();
44+
"README.md",
45+
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
46+
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
47+
}),
48+
);
49+
updater.update_file_checked(
50+
"cargo dev update_lints",
51+
update_mode,
52+
"book/src/README.md",
53+
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
54+
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
55+
}),
56+
);
57+
updater.update_file_checked(
58+
"cargo dev update_lints",
59+
update_mode,
60+
"CHANGELOG.md",
61+
&mut update_text_region_fn(
62+
"<!-- begin autogenerated links to lint list -->\n",
63+
"<!-- end autogenerated links to lint list -->",
64+
|dst| {
65+
for lint in lints
66+
.iter()
67+
.map(|l| &*l.name)
68+
.chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
69+
.chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
70+
.sorted()
71+
{
72+
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
11073
}
111-
dst.push_str(
112-
"]}\n\n\
74+
},
75+
),
76+
);
77+
updater.update_file_checked(
78+
"cargo dev update_lints",
79+
update_mode,
80+
"clippy_lints/src/deprecated_lints.rs",
81+
&mut |_, src, dst| {
82+
let mut searcher = RustSearcher::new(src);
83+
assert!(
84+
searcher.find_token(Token::Ident("declare_with_version"))
85+
&& searcher.find_token(Token::Ident("declare_with_version")),
86+
"error reading deprecated lints"
87+
);
88+
dst.push_str(&src[..searcher.pos() as usize]);
89+
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
90+
for lint in deprecated {
91+
write!(
92+
dst,
93+
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
94+
lint.version, lint.name, lint.reason,
95+
)
96+
.unwrap();
97+
}
98+
dst.push_str(
99+
"]}\n\n\
113100
#[rustfmt::skip]\n\
114101
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
115102
",
116-
);
117-
for lint in renamed {
118-
write!(
119-
dst,
120-
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
121-
lint.version, lint.old_name, lint.new_name,
122-
)
123-
.unwrap();
103+
);
104+
for lint in renamed {
105+
write!(
106+
dst,
107+
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
108+
lint.version, lint.old_name, lint.new_name,
109+
)
110+
.unwrap();
111+
}
112+
dst.push_str("]}\n");
113+
UpdateStatus::from_changed(src != dst)
114+
},
115+
);
116+
updater.update_file_checked(
117+
"cargo dev update_lints",
118+
update_mode,
119+
"tests/ui/deprecated.rs",
120+
&mut |_, src, dst| {
121+
dst.push_str(GENERATED_FILE_COMMENT);
122+
for lint in deprecated {
123+
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
124+
}
125+
dst.push_str("\nfn main() {}\n");
126+
UpdateStatus::from_changed(src != dst)
127+
},
128+
);
129+
updater.update_file_checked(
130+
"cargo dev update_lints",
131+
update_mode,
132+
"tests/ui/rename.rs",
133+
&mut move |_, src, dst| {
134+
let mut seen_lints = HashSet::new();
135+
dst.push_str(GENERATED_FILE_COMMENT);
136+
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
137+
for lint in renamed {
138+
if seen_lints.insert(&lint.new_name) {
139+
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
124140
}
125-
dst.push_str("]}\n");
126-
UpdateStatus::from_changed(src != dst)
127-
}),
128-
("tests/ui/deprecated.rs", &mut |_, src, dst| {
129-
dst.push_str(GENERATED_FILE_COMMENT);
130-
for lint in deprecated {
131-
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
141+
}
142+
seen_lints.clear();
143+
for lint in renamed {
144+
if seen_lints.insert(&lint.old_name) {
145+
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
132146
}
133-
dst.push_str("\nfn main() {}\n");
134-
UpdateStatus::from_changed(src != dst)
135-
}),
136-
("tests/ui/rename.rs", &mut move |_, src, dst| {
137-
let mut seen_lints = HashSet::new();
138-
dst.push_str(GENERATED_FILE_COMMENT);
139-
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
140-
for lint in renamed {
141-
if seen_lints.insert(&lint.new_name) {
142-
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
147+
}
148+
dst.push_str("\nfn main() {}\n");
149+
UpdateStatus::from_changed(src != dst)
150+
},
151+
);
152+
for (crate_name, lints) in lints.iter().into_group_map_by(|&l| {
153+
let Some(path::Component::Normal(name)) = l.path.components().next() else {
154+
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
155+
panic!("internal error: can't read crate name from path `{}`", l.path.display());
156+
};
157+
name
158+
}) {
159+
updater.update_file_checked(
160+
"cargo dev update_lints",
161+
update_mode,
162+
Path::new(crate_name).join("src/lib.rs"),
163+
&mut update_text_region_fn(
164+
"// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
165+
"// end lints modules, do not remove this comment, it’s used in `update_lints`",
166+
|dst| {
167+
for lint_mod in lints
168+
.iter()
169+
.filter(|l| !l.module.is_empty())
170+
.map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0))
171+
.sorted()
172+
.dedup()
173+
{
174+
writeln!(dst, "mod {lint_mod};").unwrap();
143175
}
144-
}
145-
seen_lints.clear();
146-
for lint in renamed {
147-
if seen_lints.insert(&lint.old_name) {
148-
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
176+
},
177+
),
178+
);
179+
updater.update_file_checked(
180+
"cargo dev update_lints",
181+
update_mode,
182+
Path::new(crate_name).join("src/declared_lints.rs"),
183+
&mut |_, src, dst| {
184+
dst.push_str(GENERATED_FILE_COMMENT);
185+
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
186+
for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
187+
if module_path.is_empty() {
188+
writeln!(dst, " crate::{lint_name}_INFO,").unwrap();
189+
} else {
190+
writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap();
149191
}
150192
}
151-
dst.push_str("\nfn main() {}\n");
193+
dst.push_str("];\n");
152194
UpdateStatus::from_changed(src != dst)
153-
}),
154-
],
155-
);
195+
},
196+
);
197+
}
156198
}
157199

158200
fn round_to_fifty(count: usize) -> usize {
@@ -186,13 +228,25 @@ pub struct RenamedLint {
186228
pub fn find_lint_decls() -> Vec<Lint> {
187229
let mut lints = Vec::with_capacity(1000);
188230
let mut contents = String::new();
189-
for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) {
190-
parse_clippy_lint_decls(
191-
file.path(),
192-
File::open_read_to_cleared_string(file.path(), &mut contents),
193-
&module,
194-
&mut lints,
195-
);
231+
for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
232+
let e = expect_action(e, ErrAction::Read, ".");
233+
if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() {
234+
continue;
235+
}
236+
let Ok(mut name) = e.file_name().into_string() else {
237+
continue;
238+
};
239+
if name.starts_with("clippy_lints") && name != "clippy_lints_internal" {
240+
name.push_str("/src");
241+
for (file, module) in read_src_with_module(name.as_ref()) {
242+
parse_clippy_lint_decls(
243+
file.path(),
244+
File::open_read_to_cleared_string(file.path(), &mut contents),
245+
&module,
246+
&mut lints,
247+
);
248+
}
249+
}
196250
}
197251
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
198252
lints
@@ -204,7 +258,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirE
204258
let e = expect_action(e, ErrAction::Read, src_root);
205259
let path = e.path().as_os_str().as_encoded_bytes();
206260
if let Some(path) = path.strip_suffix(b".rs")
207-
&& let Some(path) = path.get("clippy_lints/src/".len()..)
261+
&& let Some(path) = path.get(src_root.as_os_str().len() + 1..)
208262
{
209263
if path == b"lib" {
210264
Some((e, String::new()))

clippy_dev/src/utils.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -383,21 +383,6 @@ impl FileUpdater {
383383
self.update_file_checked_inner(tool, mode, path.as_ref(), update);
384384
}
385385

386-
#[expect(clippy::type_complexity)]
387-
pub fn update_files_checked(
388-
&mut self,
389-
tool: &str,
390-
mode: UpdateMode,
391-
files: &mut [(
392-
impl AsRef<Path>,
393-
&mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus,
394-
)],
395-
) {
396-
for (path, update) in files {
397-
self.update_file_checked_inner(tool, mode, path.as_ref(), update);
398-
}
399-
}
400-
401386
pub fn update_file(
402387
&mut self,
403388
path: impl AsRef<Path>,

clippy_lints/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ arrayvec = { version = "0.7", default-features = false }
1313
cargo_metadata = "0.18"
1414
clippy_config = { path = "../clippy_config" }
1515
clippy_utils = { path = "../clippy_utils" }
16+
declare_clippy_lint = { path = "../declare_clippy_lint" }
1617
itertools = "0.12"
1718
quine-mc_cluskey = "0.2"
1819
regex-syntax = "0.8"

0 commit comments

Comments
 (0)