]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_dev/src/main.rs
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
[rust.git] / src / tools / clippy / clippy_dev / src / main.rs
1 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
2 // warn on lints, that are included in `rust-lang/rust`s bootstrap
3 #![warn(rust_2018_idioms, unused_lifetimes)]
4
5 use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
6 use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
7 use indoc::indoc;
8
9 fn main() {
10     let matches = get_clap_config();
11
12     match matches.subcommand() {
13         Some(("bless", matches)) => {
14             bless::bless(matches.contains_id("ignore-timestamp"));
15         },
16         Some(("dogfood", matches)) => {
17             dogfood::dogfood(
18                 matches.contains_id("fix"),
19                 matches.contains_id("allow-dirty"),
20                 matches.contains_id("allow-staged"),
21             );
22         },
23         Some(("fmt", matches)) => {
24             fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
25         },
26         Some(("update_lints", matches)) => {
27             if matches.contains_id("print-only") {
28                 update_lints::print_lints();
29             } else if matches.contains_id("check") {
30                 update_lints::update(update_lints::UpdateMode::Check);
31             } else {
32                 update_lints::update(update_lints::UpdateMode::Change);
33             }
34         },
35         Some(("new_lint", matches)) => {
36             match new_lint::create(
37                 matches.get_one::<String>("pass"),
38                 matches.get_one::<String>("name"),
39                 matches.get_one::<String>("category"),
40                 matches.contains_id("msrv"),
41             ) {
42                 Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
43                 Err(e) => eprintln!("Unable to create lint: {}", e),
44             }
45         },
46         Some(("setup", sub_command)) => match sub_command.subcommand() {
47             Some(("intellij", matches)) => {
48                 if matches.contains_id("remove") {
49                     setup::intellij::remove_rustc_src();
50                 } else {
51                     setup::intellij::setup_rustc_src(
52                         matches
53                             .get_one::<String>("rustc-repo-path")
54                             .expect("this field is mandatory and therefore always valid"),
55                     );
56                 }
57             },
58             Some(("git-hook", matches)) => {
59                 if matches.contains_id("remove") {
60                     setup::git_hook::remove_hook();
61                 } else {
62                     setup::git_hook::install_hook(matches.contains_id("force-override"));
63                 }
64             },
65             Some(("vscode-tasks", matches)) => {
66                 if matches.contains_id("remove") {
67                     setup::vscode::remove_tasks();
68                 } else {
69                     setup::vscode::install_tasks(matches.contains_id("force-override"));
70                 }
71             },
72             _ => {},
73         },
74         Some(("remove", sub_command)) => match sub_command.subcommand() {
75             Some(("git-hook", _)) => setup::git_hook::remove_hook(),
76             Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
77             Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
78             _ => {},
79         },
80         Some(("serve", matches)) => {
81             let port = *matches.get_one::<u16>("port").unwrap();
82             let lint = matches.get_one::<String>("lint");
83             serve::run(port, lint);
84         },
85         Some(("lint", matches)) => {
86             let path = matches.get_one::<String>("path").unwrap();
87             let args = matches.get_many::<String>("args").into_iter().flatten();
88             lint::run(path, args);
89         },
90         Some(("rename_lint", matches)) => {
91             let old_name = matches.get_one::<String>("old_name").unwrap();
92             let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
93             let uplift = matches.contains_id("uplift");
94             update_lints::rename(old_name, new_name, uplift);
95         },
96         Some(("deprecate", matches)) => {
97             let name = matches.get_one::<String>("name").unwrap();
98             let reason = matches.get_one("reason");
99             update_lints::deprecate(name, reason);
100         },
101         _ => {},
102     }
103 }
104
105 fn get_clap_config() -> ArgMatches {
106     Command::new("Clippy developer tooling")
107         .arg_required_else_help(true)
108         .subcommands([
109             Command::new("bless").about("bless the test output changes").arg(
110                 Arg::new("ignore-timestamp")
111                     .long("ignore-timestamp")
112                     .help("Include files updated before clippy was built"),
113             ),
114             Command::new("dogfood").about("Runs the dogfood test").args([
115                 Arg::new("fix").long("fix").help("Apply the suggestions when possible"),
116                 Arg::new("allow-dirty")
117                     .long("allow-dirty")
118                     .help("Fix code even if the working directory has changes")
119                     .requires("fix"),
120                 Arg::new("allow-staged")
121                     .long("allow-staged")
122                     .help("Fix code even if the working directory has staged changes")
123                     .requires("fix"),
124             ]),
125             Command::new("fmt")
126                 .about("Run rustfmt on all projects and tests")
127                 .args([
128                     Arg::new("check").long("check").help("Use the rustfmt --check option"),
129                     Arg::new("verbose").short('v').long("verbose").help("Echo commands run"),
130                 ]),
131             Command::new("update_lints")
132                 .about("Updates lint registration and information from the source code")
133                 .long_about(
134                     "Makes sure that:\n \
135                     * the lint count in README.md is correct\n \
136                     * the changelog contains markdown link references at the bottom\n \
137                     * all lint groups include the correct lints\n \
138                     * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
139                     * all lints are registered in the lint store",
140                 )
141                 .args([
142                     Arg::new("print-only").long("print-only").help(
143                         "Print a table of lints to STDOUT. \
144                         This does not include deprecated and internal lints. \
145                         (Does not modify any files)",
146                     ),
147                     Arg::new("check")
148                         .long("check")
149                         .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
150                 ]),
151             Command::new("new_lint")
152                 .about("Create new lint and run `cargo dev update_lints`")
153                 .args([
154                     Arg::new("pass")
155                         .short('p')
156                         .long("pass")
157                         .help("Specify whether the lint runs during the early or late pass")
158                         .takes_value(true)
159                         .value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
160                         .required(true),
161                     Arg::new("name")
162                         .short('n')
163                         .long("name")
164                         .help("Name of the new lint in snake case, ex: fn_too_long")
165                         .takes_value(true)
166                         .required(true),
167                     Arg::new("category")
168                         .short('c')
169                         .long("category")
170                         .help("What category the lint belongs to")
171                         .default_value("nursery")
172                         .value_parser([
173                             PossibleValue::new("style"),
174                             PossibleValue::new("correctness"),
175                             PossibleValue::new("suspicious"),
176                             PossibleValue::new("complexity"),
177                             PossibleValue::new("perf"),
178                             PossibleValue::new("pedantic"),
179                             PossibleValue::new("restriction"),
180                             PossibleValue::new("cargo"),
181                             PossibleValue::new("nursery"),
182                             PossibleValue::new("internal"),
183                             PossibleValue::new("internal_warn"),
184                         ])
185                         .takes_value(true),
186                     Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
187                 ]),
188             Command::new("setup")
189                 .about("Support for setting up your personal development environment")
190                 .arg_required_else_help(true)
191                 .subcommands([
192                     Command::new("intellij")
193                         .about("Alter dependencies so Intellij Rust can find rustc internals")
194                         .args([
195                             Arg::new("remove")
196                                 .long("remove")
197                                 .help("Remove the dependencies added with 'cargo dev setup intellij'")
198                                 .required(false),
199                             Arg::new("rustc-repo-path")
200                                 .long("repo-path")
201                                 .short('r')
202                                 .help("The path to a rustc repo that will be used for setting the dependencies")
203                                 .takes_value(true)
204                                 .value_name("path")
205                                 .conflicts_with("remove")
206                                 .required(true),
207                         ]),
208                     Command::new("git-hook")
209                         .about("Add a pre-commit git hook that formats your code to make it look pretty")
210                         .args([
211                             Arg::new("remove")
212                                 .long("remove")
213                                 .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
214                                 .required(false),
215                             Arg::new("force-override")
216                                 .long("force-override")
217                                 .short('f')
218                                 .help("Forces the override of an existing git pre-commit hook")
219                                 .required(false),
220                         ]),
221                     Command::new("vscode-tasks")
222                         .about("Add several tasks to vscode for formatting, validation and testing")
223                         .args([
224                             Arg::new("remove")
225                                 .long("remove")
226                                 .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
227                                 .required(false),
228                             Arg::new("force-override")
229                                 .long("force-override")
230                                 .short('f')
231                                 .help("Forces the override of existing vscode tasks")
232                                 .required(false),
233                         ]),
234                 ]),
235             Command::new("remove")
236                 .about("Support for undoing changes done by the setup command")
237                 .arg_required_else_help(true)
238                 .subcommands([
239                     Command::new("git-hook").about("Remove any existing pre-commit git hook"),
240                     Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
241                     Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
242                 ]),
243             Command::new("serve")
244                 .about("Launch a local 'ALL the Clippy Lints' website in a browser")
245                 .args([
246                     Arg::new("port")
247                         .long("port")
248                         .short('p')
249                         .help("Local port for the http server")
250                         .default_value("8000")
251                         .value_parser(clap::value_parser!(u16)),
252                     Arg::new("lint").help("Which lint's page to load initially (optional)"),
253                 ]),
254             Command::new("lint")
255                 .about("Manually run clippy on a file or package")
256                 .after_help(indoc! {"
257                     EXAMPLES
258                         Lint a single file:
259                             cargo dev lint tests/ui/attrs.rs
260
261                         Lint a package directory:
262                             cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
263                             cargo dev lint ~/my-project
264
265                         Run rustfix:
266                             cargo dev lint ~/my-project -- --fix
267
268                         Set lint levels:
269                             cargo dev lint file.rs -- -W clippy::pedantic
270                             cargo dev lint ~/my-project -- -- -W clippy::pedantic
271                 "})
272                 .args([
273                     Arg::new("path")
274                         .required(true)
275                         .help("The path to a file or package directory to lint"),
276                     Arg::new("args")
277                         .action(ArgAction::Append)
278                         .help("Pass extra arguments to cargo/clippy-driver"),
279                 ]),
280             Command::new("rename_lint").about("Renames the given lint").args([
281                 Arg::new("old_name")
282                     .index(1)
283                     .required(true)
284                     .help("The name of the lint to rename"),
285                 Arg::new("new_name")
286                     .index(2)
287                     .required_unless_present("uplift")
288                     .help("The new name of the lint"),
289                 Arg::new("uplift")
290                     .long("uplift")
291                     .help("This lint will be uplifted into rustc"),
292             ]),
293             Command::new("deprecate").about("Deprecates the given lint").args([
294                 Arg::new("name")
295                     .index(1)
296                     .required(true)
297                     .help("The name of the lint to deprecate"),
298                 Arg::new("reason")
299                     .long("reason")
300                     .short('r')
301                     .required(false)
302                     .takes_value(true)
303                     .help("The reason for deprecation"),
304             ]),
305         ])
306         .get_matches()
307 }