1#![allow(clippy::disallowed_methods, reason = "tooling is exempt")]
2use std::process::Command;
3
4use anyhow::{Context as _, Result, bail};
5use clap::Parser;
6
7#[derive(Parser)]
8pub struct ClippyArgs {
9 /// Automatically apply lint suggestions (`clippy --fix`).
10 #[arg(long)]
11 fix: bool,
12
13 /// The package to run Clippy against (`cargo -p <PACKAGE> clippy`).
14 #[arg(long, short)]
15 package: Option<String>,
16}
17
18pub fn run_clippy(args: ClippyArgs) -> Result<()> {
19 let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
20
21 let mut clippy_command = Command::new(&cargo);
22 clippy_command.arg("clippy");
23
24 if let Some(package) = args.package.as_ref() {
25 clippy_command.args(["--package", package]);
26 } else {
27 clippy_command.arg("--workspace");
28 }
29
30 clippy_command
31 .arg("--release")
32 .arg("--all-targets")
33 .arg("--all-features");
34
35 if args.fix {
36 clippy_command.arg("--fix");
37 }
38
39 clippy_command.arg("--");
40
41 // Deny all warnings.
42 clippy_command.args(["--deny", "warnings"]);
43
44 eprintln!(
45 "running: {cargo} {}",
46 clippy_command
47 .get_args()
48 .map(|arg| arg.to_str().unwrap())
49 .collect::<Vec<_>>()
50 .join(" ")
51 );
52
53 let exit_status = clippy_command
54 .spawn()
55 .context("failed to spawn child process")?
56 .wait()
57 .context("failed to wait for child process")?;
58
59 if !exit_status.success() {
60 bail!("clippy failed: {}", exit_status);
61 }
62
63 Ok(())
64}