]> git.lizzy.rs Git - rust.git/blob - src/main.rs
Explain flags missing in cargo check in --help
[rust.git] / 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 rustc_tools_util::VersionInfo;
6 use std::env;
7 use std::ffi::OsString;
8 use std::path::PathBuf;
9 use std::process::{self, Command};
10
11 const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
12
13 Usage:
14     cargo clippy [options] [--] [<opts>...]
15
16 Common options:
17     --fix                    Automatically apply lint suggestions
18     --no-deps                Run Clippy only on the given crate
19     -h, --help               Print this message
20     -V, --version            Print version info and exit
21
22 Other options are the same as `cargo check`.
23
24 To allow or deny a lint from the command line you can use `cargo clippy --`
25 with:
26
27     -W --warn OPT       Set lint warnings
28     -A --allow OPT      Set lint allowed
29     -D --deny OPT       Set lint denied
30     -F --forbid OPT     Set lint forbidden
31
32 You can use tool lints to allow or deny lints from your code, eg.:
33
34     #[allow(clippy::needless_lifetimes)]
35 "#;
36
37 fn show_help() {
38     println!("{}", CARGO_CLIPPY_HELP);
39 }
40
41 fn show_version() {
42     let version_info = rustc_tools_util::get_version_info!();
43     println!("{}", version_info);
44 }
45
46 pub fn main() {
47     // Check for version and help flags even when invoked as 'cargo-clippy'
48     if env::args().any(|a| a == "--help" || a == "-h") {
49         show_help();
50         return;
51     }
52
53     if env::args().any(|a| a == "--version" || a == "-V") {
54         show_version();
55         return;
56     }
57
58     if let Err(code) = process(env::args().skip(2)) {
59         process::exit(code);
60     }
61 }
62
63 struct ClippyCmd {
64     cargo_subcommand: &'static str,
65     args: Vec<String>,
66     clippy_args: Vec<String>,
67 }
68
69 impl ClippyCmd {
70     fn new<I>(mut old_args: I) -> Self
71     where
72         I: Iterator<Item = String>,
73     {
74         let mut cargo_subcommand = "check";
75         let mut args = vec![];
76
77         for arg in old_args.by_ref() {
78             match arg.as_str() {
79                 "--fix" => {
80                     cargo_subcommand = "fix";
81                     continue;
82                 },
83                 "--" => break,
84                 _ => {},
85             }
86
87             args.push(arg);
88         }
89
90         let mut clippy_args: Vec<String> = old_args.collect();
91         if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
92             clippy_args.push("--no-deps".into());
93         }
94
95         ClippyCmd {
96             cargo_subcommand,
97             args,
98             clippy_args,
99         }
100     }
101
102     fn path() -> PathBuf {
103         let mut path = env::current_exe()
104             .expect("current executable path invalid")
105             .with_file_name("clippy-driver");
106
107         if cfg!(windows) {
108             path.set_extension("exe");
109         }
110
111         path
112     }
113
114     fn target_dir() -> Option<(&'static str, OsString)> {
115         env::var_os("CLIPPY_DOGFOOD")
116             .map(|_| {
117                 env::var_os("CARGO_MANIFEST_DIR").map_or_else(
118                     || std::ffi::OsString::from("clippy_dogfood"),
119                     |d| {
120                         std::path::PathBuf::from(d)
121                             .join("target")
122                             .join("dogfood")
123                             .into_os_string()
124                     },
125                 )
126             })
127             .map(|p| ("CARGO_TARGET_DIR", p))
128     }
129
130     fn into_std_cmd(self) -> Command {
131         let mut cmd = Command::new("cargo");
132         let clippy_args: String = self
133             .clippy_args
134             .iter()
135             .map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
136             .collect();
137
138         cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
139             .envs(ClippyCmd::target_dir())
140             .env("CLIPPY_ARGS", clippy_args)
141             .arg(self.cargo_subcommand)
142             .args(&self.args);
143
144         cmd
145     }
146 }
147
148 fn process<I>(old_args: I) -> Result<(), i32>
149 where
150     I: Iterator<Item = String>,
151 {
152     let cmd = ClippyCmd::new(old_args);
153
154     let mut cmd = cmd.into_std_cmd();
155
156     let exit_status = cmd
157         .spawn()
158         .expect("could not run cargo")
159         .wait()
160         .expect("failed to wait for cargo?");
161
162     if exit_status.success() {
163         Ok(())
164     } else {
165         Err(exit_status.code().unwrap_or(-1))
166     }
167 }
168
169 #[cfg(test)]
170 mod tests {
171     use super::ClippyCmd;
172
173     #[test]
174     fn fix() {
175         let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
176         let cmd = ClippyCmd::new(args);
177         assert_eq!("fix", cmd.cargo_subcommand);
178         assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
179     }
180
181     #[test]
182     fn fix_implies_no_deps() {
183         let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
184         let cmd = ClippyCmd::new(args);
185         assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
186     }
187
188     #[test]
189     fn no_deps_not_duplicated_with_fix() {
190         let args = "cargo clippy --fix -- --no-deps"
191             .split_whitespace()
192             .map(ToString::to_string);
193         let cmd = ClippyCmd::new(args);
194         assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
195     }
196
197     #[test]
198     fn check() {
199         let args = "cargo clippy".split_whitespace().map(ToString::to_string);
200         let cmd = ClippyCmd::new(args);
201         assert_eq!("check", cmd.cargo_subcommand);
202     }
203 }