1 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
3 use clap::{App, Arg, SubCommand};
5 gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list,
6 replace_region_in_file, Lint, DOCS_LINK,
12 mod stderr_length_check;
14 #[derive(Clone, Copy, PartialEq)]
21 let matches = App::new("Clippy developer tooling")
23 SubCommand::with_name("fmt")
24 .about("Run rustfmt on all projects and tests")
26 Arg::with_name("check")
28 .help("Use the rustfmt --check option"),
31 Arg::with_name("verbose")
34 .help("Echo commands run"),
38 SubCommand::with_name("update_lints")
39 .about("Updates lint registration and information from the source code")
42 * the lint count in README.md is correct\n \
43 * the changelog contains markdown link references at the bottom\n \
44 * all lint groups include the correct lints\n \
45 * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
46 * all lints are registered in the lint store",
48 .arg(Arg::with_name("print-only").long("print-only").help(
49 "Print a table of lints to STDOUT. \
50 This does not include deprecated and internal lints. \
51 (Does not modify any files)",
54 Arg::with_name("check")
56 .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
60 SubCommand::with_name("new_lint")
61 .about("Create new lint and run `cargo dev update_lints`")
63 Arg::with_name("pass")
66 .help("Specify whether the lint runs during the early or late pass")
68 .possible_values(&["early", "late"])
72 Arg::with_name("name")
75 .help("Name of the new lint in snake case, ex: fn_too_long")
80 Arg::with_name("category")
83 .help("What category the lint belongs to")
84 .default_value("nursery")
101 Arg::with_name("limit-stderr-length")
102 .long("limit-stderr-length")
103 .help("Ensures that stderr files do not grow longer than a certain amount of lines."),
107 if matches.is_present("limit-stderr-length") {
108 stderr_length_check::check();
111 match matches.subcommand() {
112 ("fmt", Some(matches)) => {
113 fmt::run(matches.is_present("check"), matches.is_present("verbose"));
115 ("update_lints", Some(matches)) => {
116 if matches.is_present("print-only") {
118 } else if matches.is_present("check") {
119 update_lints(UpdateMode::Check);
121 update_lints(UpdateMode::Change);
124 ("new_lint", Some(matches)) => {
125 match new_lint::create(
126 matches.value_of("pass"),
127 matches.value_of("name"),
128 matches.value_of("category"),
130 Ok(_) => update_lints(UpdateMode::Change),
131 Err(e) => eprintln!("Unable to create lint: {}", e),
139 let lint_list = gather_all();
140 let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list).collect();
141 let usable_lint_count = usable_lints.len();
142 let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
144 for (lint_group, mut lints) in grouped_by_lint_group {
145 if lint_group == "Deprecated" {
148 println!("\n## {}", lint_group);
150 lints.sort_by_key(|l| l.name.clone());
154 "* [{}]({}#{}) ({})",
156 clippy_dev::DOCS_LINK,
163 println!("there are {} lints", usable_lint_count);
166 #[allow(clippy::too_many_lines)]
167 fn update_lints(update_mode: UpdateMode) {
168 let lint_list: Vec<Lint> = gather_all().collect();
170 let internal_lints = Lint::internal_lints(lint_list.clone().into_iter());
172 let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
173 let usable_lint_count = usable_lints.len();
175 let mut sorted_usable_lints = usable_lints.clone();
176 sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
178 let mut file_change = replace_region_in_file(
179 Path::new("src/lintlist/mod.rs"),
183 update_mode == UpdateMode::Change,
186 "pub const ALL_LINTS: [Lint; {}] = {:#?};",
187 sorted_usable_lints.len(),
191 .map(ToString::to_string)
197 file_change |= replace_region_in_file(
198 Path::new("README.md"),
199 &format!(r#"\[There are \d+ lints included in this crate!\]\({}\)"#, DOCS_LINK),
202 update_mode == UpdateMode::Change,
205 "[There are {} lints included in this crate!]({})",
206 usable_lint_count, DOCS_LINK
212 file_change |= replace_region_in_file(
213 Path::new("CHANGELOG.md"),
214 "<!-- begin autogenerated links to lint list -->",
215 "<!-- end autogenerated links to lint list -->",
217 update_mode == UpdateMode::Change,
218 || gen_changelog_lint_list(lint_list.clone()),
222 file_change |= replace_region_in_file(
223 Path::new("clippy_lints/src/lib.rs"),
224 "begin deprecated lints",
225 "end deprecated lints",
227 update_mode == UpdateMode::Change,
228 || gen_deprecated(&lint_list),
232 file_change |= replace_region_in_file(
233 Path::new("clippy_lints/src/lib.rs"),
234 "begin register lints",
235 "end register lints",
237 update_mode == UpdateMode::Change,
238 || gen_register_lint_list(&lint_list),
242 file_change |= replace_region_in_file(
243 Path::new("clippy_lints/src/lib.rs"),
244 "begin lints modules",
247 update_mode == UpdateMode::Change,
248 || gen_modules_list(lint_list.clone()),
252 // Generate lists of lints in the clippy::all lint group
253 file_change |= replace_region_in_file(
254 Path::new("clippy_lints/src/lib.rs"),
255 r#"store.register_group\(true, "clippy::all""#,
258 update_mode == UpdateMode::Change,
260 // clippy::all should only include the following lint groups:
261 let all_group_lints = usable_lints
265 l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
269 gen_lint_group_list(all_group_lints)
274 // Generate the list of lints for all other lint groups
275 for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
276 file_change |= replace_region_in_file(
277 Path::new("clippy_lints/src/lib.rs"),
278 &format!("store.register_group\\(true, \"clippy::{}\"", lint_group),
281 update_mode == UpdateMode::Change,
282 || gen_lint_group_list(lints.clone()),
287 if update_mode == UpdateMode::Check && file_change {
289 "Not all lints defined properly. \
290 Please run `cargo dev update_lints` to make sure all lints are defined properly."
292 std::process::exit(1);