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