use crate::{ gather_all, gen_changelog_lint_list, gen_deprecated, gen_lint_group_list, gen_modules_list, gen_register_lint_list, replace_region_in_file, Lint, DOCS_LINK, }; use std::fs; use std::path::Path; #[derive(Clone, Copy, PartialEq)] pub enum UpdateMode { Check, Change, } /// Runs the `update_lints` command. /// /// This updates various generated values from the lint source code. /// /// `update_mode` indicates if the files should be updated or if updates should be checked for. /// /// # Panics /// /// Panics if a file path could not read from or then written to #[allow(clippy::too_many_lines)] pub fn run(update_mode: UpdateMode) { let lint_list: Vec = gather_all().collect(); let internal_lints = Lint::internal_lints(&lint_list); let deprecated_lints = Lint::deprecated_lints(&lint_list); let usable_lints = Lint::usable_lints(&lint_list); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); let usable_lint_count = round_to_fifty(usable_lints.len()); let mut file_change = false; file_change |= replace_region_in_file( Path::new("README.md"), &format!( r#"\[There are over \d+ lints included in this crate!\]\({}\)"#, DOCS_LINK ), "", true, update_mode == UpdateMode::Change, || { vec![format!( "[There are over {} lints included in this crate!]({})", usable_lint_count, DOCS_LINK )] }, ) .changed; file_change |= replace_region_in_file( Path::new("CHANGELOG.md"), "", "", false, update_mode == UpdateMode::Change, || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())), ) .changed; if file_change && update_mode == UpdateMode::Check { exit_with_failure(); } for (name, lines) in [ ("mods", gen_modules_list(usable_lints.iter())), ("deprecated", gen_deprecated(deprecated_lints.iter())), ( "register_lints", gen_register_lint_list(internal_lints.iter(), usable_lints.iter()), ), ("register_all", { let all_group_lints = usable_lints.iter().filter(|l| { matches!( &*l.group, "correctness" | "suspicious" | "style" | "complexity" | "perf" ) }); gen_lint_group_list("all", all_group_lints) }), ] { process_file(&format!("clippy_lints/src/lib.{}.rs", name), update_mode, &lines[..]); } for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) { process_file( &format!("clippy_lints/src/lib.register_{}.rs", lint_group), update_mode, &gen_lint_group_list(&lints.get(0).expect("group non-empty").group, lints.iter())[..], ); } } pub fn print_lints() { let lint_list: Vec = gather_all().collect(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); for (lint_group, mut lints) in grouped_by_lint_group { if lint_group == "Deprecated" { continue; } println!("\n## {}", lint_group); lints.sort_by_key(|l| l.name.clone()); for lint in lints { println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc); } } println!("there are {} lints", usable_lint_count); } fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } fn process_file(path: impl AsRef, update_mode: UpdateMode, new_lines: &[String]) { let mut new_content = "// This file was generated by `cargo dev update_lints`.\n\ // Use that command to update this file and do not edit by hand.\n\ // Manual edits will be overwritten.\n\n" .to_string(); new_content.push_str(&new_lines.join("\n")); if update_mode == UpdateMode::Check { let old_content = fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e)); if new_content != old_content { exit_with_failure(); } } else { fs::write(&path, new_content.as_bytes()) .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e)); } } fn exit_with_failure() { println!( "Not all lints defined properly. \ Please run `cargo dev update_lints` to make sure all lints are defined properly." ); std::process::exit(1); }