Skip to content

Commit 684f17e

Browse files
committed
Auto merge of #6441 - ebroto:use_rustflags, r=flip1995
Pass Clippy args also trough RUSTFLAGS This removes a hack (\_\_CLIPPY_HACKERY\_\_) to add another one :) It allows this workflow to work: ```terminal cargo clippy # warning: empty `loop {}` wastes CPU cycles cargo clippy -- -A clippy::empty_loop # no warnings emitted ``` Before this change the new flag was not taken into consideration in cargo's fingerprint and the warning was emitted again. I guess that ideally we could add a specific env var for compiler wrapper arguments, but in the meantime this should do the job. changelog: Pass clippy arguments through RUSTFLAGS so that changing them will trigger a rebuild r? `@flip1995` cc `@ehuss` (I think this may count as another step towards stabilizing `RUSTC_WORKSPACE_WRAPPER` 😄) Fixes #5214 and avoids frustration for users unfamiliar with the issue
2 parents b7db5bf + f93d965 commit 684f17e

File tree

5 files changed

+171
-47
lines changed

5 files changed

+171
-47
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ publish = false
2020

2121
[[bin]]
2222
name = "cargo-clippy"
23-
test = false
2423
path = "src/main.rs"
2524

2625
[[bin]]

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ the lint(s) you are interested in:
208208
```terminal
209209
cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
210210
```
211-
Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`.
212211

213212
### Specifying the minimum supported Rust version
214213

src/driver.rs

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(rustc_private)]
22
#![feature(once_cell)]
3+
#![feature(bool_to_option)]
34
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
45
// warn on lints, that are included in `rust-lang/rust`s bootstrap
56
#![warn(rust_2018_idioms, unused_lifetimes)]
@@ -19,6 +20,7 @@ use rustc_tools_util::VersionInfo;
1920

2021
use std::borrow::Cow;
2122
use std::env;
23+
use std::iter;
2224
use std::lazy::SyncLazy;
2325
use std::ops::Deref;
2426
use std::panic;
@@ -47,20 +49,6 @@ fn arg_value<'a, T: Deref<Target = str>>(
4749
None
4850
}
4951

50-
#[test]
51-
fn test_arg_value() {
52-
let args = &["--bar=bar", "--foobar", "123", "--foo"];
53-
54-
assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
55-
assert_eq!(arg_value(args, "--bar", |_| false), None);
56-
assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
57-
assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
58-
assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
59-
assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
60-
assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
61-
assert_eq!(arg_value(args, "--foo", |_| true), None);
62-
}
63-
6452
struct DefaultCallbacks;
6553
impl rustc_driver::Callbacks for DefaultCallbacks {}
6654

@@ -182,6 +170,28 @@ fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<Pat
182170
})
183171
}
184172

173+
fn remove_clippy_args<'a, T, U, I>(args: &mut Vec<T>, clippy_args: I)
174+
where
175+
T: AsRef<str>,
176+
U: AsRef<str> + ?Sized + 'a,
177+
I: Iterator<Item = &'a U> + Clone,
178+
{
179+
let args_iter = clippy_args.map(AsRef::as_ref);
180+
let args_count = args_iter.clone().count();
181+
182+
if args_count > 0 {
183+
if let Some(start) = args.windows(args_count).enumerate().find_map(|(current, window)| {
184+
window
185+
.iter()
186+
.map(AsRef::as_ref)
187+
.eq(args_iter.clone())
188+
.then_some(current)
189+
}) {
190+
args.drain(start..start + args_count);
191+
}
192+
}
193+
}
194+
185195
#[allow(clippy::too_many_lines)]
186196
pub fn main() {
187197
rustc_driver::init_rustc_env_logger();
@@ -278,20 +288,9 @@ pub fn main() {
278288
args.extend(vec!["--sysroot".into(), sys_root]);
279289
};
280290

281-
let mut no_deps = false;
282-
let clippy_args = env::var("CLIPPY_ARGS")
283-
.unwrap_or_default()
284-
.split("__CLIPPY_HACKERY__")
285-
.filter_map(|s| match s {
286-
"" => None,
287-
"--no-deps" => {
288-
no_deps = true;
289-
None
290-
},
291-
_ => Some(s.to_string()),
292-
})
293-
.chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
294-
.collect::<Vec<String>>();
291+
let clippy_args = env::var("CLIPPY_ARGS").unwrap_or_default();
292+
let clippy_args = clippy_args.split_whitespace();
293+
let no_deps = clippy_args.clone().any(|flag| flag == "--no-deps");
295294

296295
// We enable Clippy if one of the following conditions is met
297296
// - IF Clippy is run on its test suite OR
@@ -304,7 +303,11 @@ pub fn main() {
304303

305304
let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
306305
if clippy_enabled {
307-
args.extend(clippy_args);
306+
remove_clippy_args(&mut args, iter::once("--no-deps"));
307+
args.extend(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]);
308+
} else {
309+
// Remove all flags passed through RUSTFLAGS if Clippy is not enabled.
310+
remove_clippy_args(&mut args, clippy_args);
308311
}
309312

310313
let mut clippy = ClippyCallbacks;
@@ -315,3 +318,58 @@ pub fn main() {
315318
rustc_driver::RunCompiler::new(&args, callbacks).run()
316319
}))
317320
}
321+
322+
#[cfg(test)]
323+
mod tests {
324+
use super::*;
325+
326+
#[test]
327+
fn test_arg_value() {
328+
let args = &["--bar=bar", "--foobar", "123", "--foo"];
329+
330+
assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
331+
assert_eq!(arg_value(args, "--bar", |_| false), None);
332+
assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
333+
assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
334+
assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
335+
assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
336+
assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
337+
assert_eq!(arg_value(args, "--foo", |_| true), None);
338+
}
339+
340+
#[test]
341+
fn removes_clippy_args_from_start() {
342+
let mut args = vec!["-D", "clippy::await_holding_lock", "--cfg", r#"feature="some_feat""#];
343+
let clippy_args = ["-D", "clippy::await_holding_lock"].iter();
344+
345+
remove_clippy_args(&mut args, clippy_args);
346+
assert_eq!(args, &["--cfg", r#"feature="some_feat""#]);
347+
}
348+
349+
#[test]
350+
fn removes_clippy_args_from_end() {
351+
let mut args = vec!["-Zui-testing", "-A", "clippy::empty_loop", "--no-deps"];
352+
let clippy_args = ["-A", "clippy::empty_loop", "--no-deps"].iter();
353+
354+
remove_clippy_args(&mut args, clippy_args);
355+
assert_eq!(args, &["-Zui-testing"]);
356+
}
357+
358+
#[test]
359+
fn removes_clippy_args_from_middle() {
360+
let mut args = vec!["-Zui-testing", "-W", "clippy::filter_map", "-L", "serde"];
361+
let clippy_args = ["-W", "clippy::filter_map"].iter();
362+
363+
remove_clippy_args(&mut args, clippy_args);
364+
assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
365+
}
366+
367+
#[test]
368+
fn no_clippy_args_to_remove() {
369+
let mut args = vec!["-Zui-testing", "-L", "serde"];
370+
let clippy_args: [&str; 0] = [];
371+
372+
remove_clippy_args(&mut args, clippy_args.iter());
373+
assert_eq!(args, &["-Zui-testing", "-L", "serde"]);
374+
}
375+
}

src/main.rs

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![feature(bool_to_option)]
2+
#![feature(command_access)]
13
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
24
// warn on lints, that are included in `rust-lang/rust`s bootstrap
35
#![warn(rust_2018_idioms, unused_lifetimes)]
@@ -62,7 +64,7 @@ struct ClippyCmd {
6264
unstable_options: bool,
6365
cargo_subcommand: &'static str,
6466
args: Vec<String>,
65-
clippy_args: Vec<String>,
67+
clippy_args: Option<String>,
6668
}
6769

6870
impl ClippyCmd {
@@ -99,16 +101,17 @@ impl ClippyCmd {
99101
args.insert(0, "+nightly".to_string());
100102
}
101103

102-
let mut clippy_args: Vec<String> = old_args.collect();
103-
if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
104-
clippy_args.push("--no-deps".into());
104+
let mut clippy_args = old_args.collect::<Vec<String>>().join(" ");
105+
if cargo_subcommand == "fix" && !clippy_args.contains("--no-deps") {
106+
clippy_args = format!("{} --no-deps", clippy_args);
105107
}
106108

109+
let has_args = !clippy_args.is_empty();
107110
ClippyCmd {
108111
unstable_options,
109112
cargo_subcommand,
110113
args,
111-
clippy_args,
114+
clippy_args: has_args.then_some(clippy_args),
112115
}
113116
}
114117

@@ -148,20 +151,24 @@ impl ClippyCmd {
148151
.map(|p| ("CARGO_TARGET_DIR", p))
149152
}
150153

151-
fn into_std_cmd(self) -> Command {
154+
fn into_std_cmd(self, rustflags: Option<String>) -> Command {
152155
let mut cmd = Command::new("cargo");
153-
let clippy_args: String = self
154-
.clippy_args
155-
.iter()
156-
.map(|arg| format!("{}__CLIPPY_HACKERY__", arg))
157-
.collect();
158156

159157
cmd.env(self.path_env(), Self::path())
160158
.envs(ClippyCmd::target_dir())
161-
.env("CLIPPY_ARGS", clippy_args)
162159
.arg(self.cargo_subcommand)
163160
.args(&self.args);
164161

162+
// HACK: pass Clippy args to the driver *also* through RUSTFLAGS.
163+
// This guarantees that new builds will be triggered when Clippy flags change.
164+
if let Some(clippy_args) = self.clippy_args {
165+
cmd.env(
166+
"RUSTFLAGS",
167+
rustflags.map_or(clippy_args.clone(), |flags| format!("{} {}", clippy_args, flags)),
168+
);
169+
cmd.env("CLIPPY_ARGS", clippy_args);
170+
}
171+
165172
cmd
166173
}
167174
}
@@ -172,7 +179,7 @@ where
172179
{
173180
let cmd = ClippyCmd::new(old_args);
174181

175-
let mut cmd = cmd.into_std_cmd();
182+
let mut cmd = cmd.into_std_cmd(env::var("RUSTFLAGS").ok());
176183

177184
let exit_status = cmd
178185
.spawn()
@@ -190,6 +197,7 @@ where
190197
#[cfg(test)]
191198
mod tests {
192199
use super::ClippyCmd;
200+
use std::ffi::OsStr;
193201

194202
#[test]
195203
#[should_panic]
@@ -204,6 +212,7 @@ mod tests {
204212
.split_whitespace()
205213
.map(ToString::to_string);
206214
let cmd = ClippyCmd::new(args);
215+
207216
assert_eq!("fix", cmd.cargo_subcommand);
208217
assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
209218
assert!(cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
@@ -215,7 +224,8 @@ mod tests {
215224
.split_whitespace()
216225
.map(ToString::to_string);
217226
let cmd = ClippyCmd::new(args);
218-
assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
227+
228+
assert!(cmd.clippy_args.unwrap().contains("--no-deps"));
219229
}
220230

221231
#[test]
@@ -224,13 +234,15 @@ mod tests {
224234
.split_whitespace()
225235
.map(ToString::to_string);
226236
let cmd = ClippyCmd::new(args);
227-
assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
237+
238+
assert_eq!(1, cmd.clippy_args.unwrap().matches("--no-deps").count());
228239
}
229240

230241
#[test]
231242
fn check() {
232243
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
233244
let cmd = ClippyCmd::new(args);
245+
234246
assert_eq!("check", cmd.cargo_subcommand);
235247
assert_eq!("RUSTC_WRAPPER", cmd.path_env());
236248
}
@@ -241,7 +253,63 @@ mod tests {
241253
.split_whitespace()
242254
.map(ToString::to_string);
243255
let cmd = ClippyCmd::new(args);
256+
244257
assert_eq!("check", cmd.cargo_subcommand);
245258
assert_eq!("RUSTC_WORKSPACE_WRAPPER", cmd.path_env());
246259
}
260+
261+
#[test]
262+
fn clippy_args_into_rustflags() {
263+
let args = "cargo clippy -- -W clippy::as_conversions"
264+
.split_whitespace()
265+
.map(ToString::to_string);
266+
let cmd = ClippyCmd::new(args);
267+
268+
let rustflags = None;
269+
let cmd = cmd.into_std_cmd(rustflags);
270+
271+
assert!(cmd
272+
.get_envs()
273+
.any(|(key, val)| key == "RUSTFLAGS" && val == Some(OsStr::new("-W clippy::as_conversions"))));
274+
}
275+
276+
#[test]
277+
fn clippy_args_respect_existing_rustflags() {
278+
let args = "cargo clippy -- -D clippy::await_holding_lock"
279+
.split_whitespace()
280+
.map(ToString::to_string);
281+
let cmd = ClippyCmd::new(args);
282+
283+
let rustflags = Some(r#"--cfg feature="some_feat""#.into());
284+
let cmd = cmd.into_std_cmd(rustflags);
285+
286+
assert!(cmd.get_envs().any(|(key, val)| key == "RUSTFLAGS"
287+
&& val == Some(OsStr::new(r#"-D clippy::await_holding_lock --cfg feature="some_feat""#))));
288+
}
289+
290+
#[test]
291+
fn no_env_change_if_no_clippy_args() {
292+
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
293+
let cmd = ClippyCmd::new(args);
294+
295+
let rustflags = Some(r#"--cfg feature="some_feat""#.into());
296+
let cmd = cmd.into_std_cmd(rustflags);
297+
298+
assert!(!cmd
299+
.get_envs()
300+
.any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"));
301+
}
302+
303+
#[test]
304+
fn no_env_change_if_no_clippy_args_nor_rustflags() {
305+
let args = "cargo clippy".split_whitespace().map(ToString::to_string);
306+
let cmd = ClippyCmd::new(args);
307+
308+
let rustflags = None;
309+
let cmd = cmd.into_std_cmd(rustflags);
310+
311+
assert!(!cmd
312+
.get_envs()
313+
.any(|(key, _)| key == "RUSTFLAGS" || key == "CLIPPY_ARGS"))
314+
}
247315
}

tests/dogfood.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn dogfood_clippy() {
2323
.current_dir(root_dir)
2424
.env("CLIPPY_DOGFOOD", "1")
2525
.env("CARGO_INCREMENTAL", "0")
26-
.arg("clippy-preview")
26+
.arg("clippy")
2727
.arg("--all-targets")
2828
.arg("--all-features")
2929
.arg("--")

0 commit comments

Comments
 (0)