]> git.lizzy.rs Git - rust.git/blob - clippy_dev/src/main.rs
Auto merge of #5029 - flip1995:wildcard_imports, r=phansch
[rust.git] / clippy_dev / src / main.rs
1 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
2
3 use clap::{App, Arg, SubCommand};
4 use clippy_dev::{
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,
7 };
8 use std::path::Path;
9
10 mod fmt;
11 mod new_lint;
12 mod stderr_length_check;
13
14 #[derive(Clone, Copy, PartialEq)]
15 enum UpdateMode {
16     Check,
17     Change,
18 }
19
20 fn main() {
21     let matches = App::new("Clippy developer tooling")
22         .subcommand(
23             SubCommand::with_name("fmt")
24                 .about("Run rustfmt on all projects and tests")
25                 .arg(
26                     Arg::with_name("check")
27                         .long("check")
28                         .help("Use the rustfmt --check option"),
29                 )
30                 .arg(
31                     Arg::with_name("verbose")
32                         .short("v")
33                         .long("verbose")
34                         .help("Echo commands run"),
35                 ),
36         )
37         .subcommand(
38             SubCommand::with_name("update_lints")
39                 .about("Updates lint registration and information from the source code")
40                 .long_about(
41                     "Makes sure that:\n \
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",
47                 )
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)",
52                 ))
53                 .arg(
54                     Arg::with_name("check")
55                         .long("check")
56                         .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
57                 ),
58         )
59         .subcommand(
60             SubCommand::with_name("new_lint")
61                 .about("Create new lint and run `cargo dev update_lints`")
62                 .arg(
63                     Arg::with_name("pass")
64                         .short("p")
65                         .long("pass")
66                         .help("Specify whether the lint runs during the early or late pass")
67                         .takes_value(true)
68                         .possible_values(&["early", "late"])
69                         .required(true),
70                 )
71                 .arg(
72                     Arg::with_name("name")
73                         .short("n")
74                         .long("name")
75                         .help("Name of the new lint in snake case, ex: fn_too_long")
76                         .takes_value(true)
77                         .required(true),
78                 )
79                 .arg(
80                     Arg::with_name("category")
81                         .short("c")
82                         .long("category")
83                         .help("What category the lint belongs to")
84                         .default_value("nursery")
85                         .possible_values(&[
86                             "style",
87                             "correctness",
88                             "complexity",
89                             "perf",
90                             "pedantic",
91                             "restriction",
92                             "cargo",
93                             "nursery",
94                             "internal",
95                             "internal_warn",
96                         ])
97                         .takes_value(true),
98                 ),
99         )
100         .arg(
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."),
104         )
105         .get_matches();
106
107     if matches.is_present("limit-stderr-length") {
108         stderr_length_check::check();
109     }
110
111     match matches.subcommand() {
112         ("fmt", Some(matches)) => {
113             fmt::run(matches.is_present("check"), matches.is_present("verbose"));
114         },
115         ("update_lints", Some(matches)) => {
116             if matches.is_present("print-only") {
117                 print_lints();
118             } else if matches.is_present("check") {
119                 update_lints(UpdateMode::Check);
120             } else {
121                 update_lints(UpdateMode::Change);
122             }
123         },
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"),
129             ) {
130                 Ok(_) => update_lints(UpdateMode::Change),
131                 Err(e) => eprintln!("Unable to create lint: {}", e),
132             }
133         },
134         _ => {},
135     }
136 }
137
138 fn print_lints() {
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());
143
144     for (lint_group, mut lints) in grouped_by_lint_group {
145         if lint_group == "Deprecated" {
146             continue;
147         }
148         println!("\n## {}", lint_group);
149
150         lints.sort_by_key(|l| l.name.clone());
151
152         for lint in lints {
153             println!(
154                 "* [{}]({}#{}) ({})",
155                 lint.name,
156                 clippy_dev::DOCS_LINK,
157                 lint.name,
158                 lint.desc
159             );
160         }
161     }
162
163     println!("there are {} lints", usable_lint_count);
164 }
165
166 #[allow(clippy::too_many_lines)]
167 fn update_lints(update_mode: UpdateMode) {
168     let lint_list: Vec<Lint> = gather_all().collect();
169
170     let internal_lints = Lint::internal_lints(lint_list.clone().into_iter());
171
172     let usable_lints: Vec<Lint> = Lint::usable_lints(lint_list.clone().into_iter()).collect();
173     let usable_lint_count = usable_lints.len();
174
175     let mut sorted_usable_lints = usable_lints.clone();
176     sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
177
178     let mut file_change = replace_region_in_file(
179         Path::new("src/lintlist/mod.rs"),
180         "begin lint list",
181         "end lint list",
182         false,
183         update_mode == UpdateMode::Change,
184         || {
185             format!(
186                 "pub const ALL_LINTS: [Lint; {}] = {:#?};",
187                 sorted_usable_lints.len(),
188                 sorted_usable_lints
189             )
190             .lines()
191             .map(ToString::to_string)
192             .collect::<Vec<_>>()
193         },
194     )
195     .changed;
196
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),
200         "",
201         true,
202         update_mode == UpdateMode::Change,
203         || {
204             vec![format!(
205                 "[There are {} lints included in this crate!]({})",
206                 usable_lint_count, DOCS_LINK
207             )]
208         },
209     )
210     .changed;
211
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 -->",
216         false,
217         update_mode == UpdateMode::Change,
218         || gen_changelog_lint_list(lint_list.clone()),
219     )
220     .changed;
221
222     file_change |= replace_region_in_file(
223         Path::new("clippy_lints/src/lib.rs"),
224         "begin deprecated lints",
225         "end deprecated lints",
226         false,
227         update_mode == UpdateMode::Change,
228         || gen_deprecated(&lint_list),
229     )
230     .changed;
231
232     file_change |= replace_region_in_file(
233         Path::new("clippy_lints/src/lib.rs"),
234         "begin register lints",
235         "end register lints",
236         false,
237         update_mode == UpdateMode::Change,
238         || gen_register_lint_list(&lint_list),
239     )
240     .changed;
241
242     file_change |= replace_region_in_file(
243         Path::new("clippy_lints/src/lib.rs"),
244         "begin lints modules",
245         "end lints modules",
246         false,
247         update_mode == UpdateMode::Change,
248         || gen_modules_list(lint_list.clone()),
249     )
250     .changed;
251
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""#,
256         r#"\]\);"#,
257         false,
258         update_mode == UpdateMode::Change,
259         || {
260             // clippy::all should only include the following lint groups:
261             let all_group_lints = usable_lints
262                 .clone()
263                 .into_iter()
264                 .filter(|l| {
265                     l.group == "correctness" || l.group == "style" || l.group == "complexity" || l.group == "perf"
266                 })
267                 .collect();
268
269             gen_lint_group_list(all_group_lints)
270         },
271     )
272     .changed;
273
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),
279             r#"\]\);"#,
280             false,
281             update_mode == UpdateMode::Change,
282             || gen_lint_group_list(lints.clone()),
283         )
284         .changed;
285     }
286
287     if update_mode == UpdateMode::Check && file_change {
288         println!(
289             "Not all lints defined properly. \
290              Please run `cargo dev update_lints` to make sure all lints are defined properly."
291         );
292         std::process::exit(1);
293     }
294 }