Add `cargo xtask clippy` (#8722)

Marshall Bowers created

This PR sets up a `cargo xtask clippy` command for running `cargo
clippy` with our defined set of options.

The intent is to make this easier to manage as we start enabling more
Clippy rules.

Release Notes:

- N/A

Change summary

.cargo/ci-config.toml            |  3 +
.cargo/config.toml               |  3 +
Cargo.lock                       |  8 +++
Cargo.toml                       |  2 
crates/cli/Cargo.toml            |  1 
crates/storybook/Cargo.toml      |  2 
crates/theme_importer/Cargo.toml |  2 
script/clippy                    |  6 --
tooling/xtask/Cargo.toml         | 10 ++++
tooling/xtask/LICENSE-GPL        |  1 
tooling/xtask/src/main.rs        | 80 ++++++++++++++++++++++++++++++++++
11 files changed, 111 insertions(+), 7 deletions(-)

Detailed changes

.cargo/ci-config.toml 🔗

@@ -10,3 +10,6 @@
 # in one spot, that's going to trigger a rebuild of all of the artifacts. Using ci-config.toml we can define these overrides for CI in one spot and not worry about it.
 [build]
 rustflags = ["-D", "warnings"]
+
+[alias]
+xtask = "run --package xtask --"

.cargo/config.toml 🔗

@@ -1,3 +1,6 @@
 [build]
 # v0 mangling scheme provides more detailed backtraces around closures
 rustflags = ["-C", "symbol-mangling-version=v0"]
+
+[alias]
+xtask = "run --package xtask --"

Cargo.lock 🔗

@@ -12208,6 +12208,14 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
 
+[[package]]
+name = "xtask"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap 4.4.4",
+]
+
 [[package]]
 name = "yansi"
 version = "0.5.1"

Cargo.toml 🔗

@@ -93,6 +93,7 @@ members = [
     "crates/zed",
     "crates/zed_actions",
     "extensions/gleam",
+    "tooling/xtask",
 ]
 default-members = ["crates/zed"]
 resolver = "2"
@@ -200,6 +201,7 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f394
 blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
 blade-rwh = { package = "raw-window-handle", version = "0.5" }
 chrono = { version = "0.4", features = ["serde"] }
+clap = "4.4"
 clickhouse = { version = "0.11.6" }
 ctor = "0.2.6"
 core-foundation = { version = "0.9.3" }

crates/cli/Cargo.toml 🔗

@@ -15,6 +15,7 @@ path = "src/main.rs"
 
 [dependencies]
 anyhow.workspace = true
+# TODO: Use workspace version of `clap`.
 clap = { version = "3.1", features = ["derive"] }
 ipc-channel = "0.16"
 serde.workspace = true

crates/storybook/Cargo.toml 🔗

@@ -11,7 +11,7 @@ path = "src/storybook.rs"
 
 [dependencies]
 anyhow.workspace = true
-clap = { version = "4.4", features = ["derive", "string"] }
+clap = { workspace = true, features = ["derive", "string"] }
 collab_ui = { workspace = true, features = ["stories"] }
 ctrlc = "3.4"
 dialoguer = { version = "0.11.0", features = ["fuzzy-select"] }

crates/theme_importer/Cargo.toml 🔗

@@ -7,7 +7,7 @@ license = "GPL-3.0-or-later"
 
 [dependencies]
 anyhow.workspace = true
-clap = { version = "4.4", features = ["derive"] }
+clap = { workspace = true, features = ["derive"] }
 gpui.workspace = true
 indexmap = { version = "1.6.2", features = ["serde"] }
 log.workspace = true

script/clippy 🔗

@@ -2,8 +2,4 @@
 
 set -euxo pipefail
 
-# clippy.toml is not currently supporting specifying allowed lints
-# so specify those here, and disable the rest until Zed's workspace
-# will have more fixes & suppression for the standard lint set
-cargo clippy --release --workspace --all-features --all-targets -- --allow clippy::all --deny clippy::dbg_macro --deny clippy::todo
-cargo clippy --package gpui -- --deny warnings
+cargo xtask clippy

tooling/xtask/Cargo.toml 🔗

@@ -0,0 +1,10 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+edition = "2021"
+publish = false
+license = "GPL-3.0-or-later"
+
+[dependencies]
+anyhow.workspace = true
+clap = { workspace = true, features = ["derive"] }

tooling/xtask/src/main.rs 🔗

@@ -0,0 +1,80 @@
+use std::process::Command;
+
+use anyhow::{bail, Context, Result};
+use clap::{Parser, Subcommand};
+
+#[derive(Parser)]
+#[command(name = "cargo xtask")]
+struct Args {
+    #[command(subcommand)]
+    command: CliCommand,
+}
+
+#[derive(Subcommand)]
+enum CliCommand {
+    /// Runs `cargo clippy`.
+    Clippy(ClippyArgs),
+}
+
+fn main() -> Result<()> {
+    let args = Args::parse();
+
+    match args.command {
+        CliCommand::Clippy(args) => run_clippy(args),
+    }
+}
+
+#[derive(Parser)]
+struct ClippyArgs {
+    /// Whether to deny warnings (`clippy --deny warnings`).
+    #[arg(long)]
+    deny_warnings: bool,
+}
+
+fn run_clippy(args: ClippyArgs) -> Result<()> {
+    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
+
+    let mut clippy_command = Command::new(&cargo);
+    clippy_command
+        .arg("clippy")
+        .arg("--workspace")
+        .arg("--release")
+        .arg("--all-targets")
+        .arg("--all-features");
+
+    clippy_command.arg("--");
+
+    if args.deny_warnings {
+        clippy_command.args(["--deny", "warnings"]);
+    }
+
+    // Allow all Clippy lints by default, as we have a lot of violations at the moment.
+    // We can tighten things up once we have a better handle on them.
+    clippy_command.args(["--allow", "clippy::all"]);
+
+    // Deny `dbg!` and `todo!`s.
+    clippy_command
+        .args(["--deny", "clippy::dbg_macro"])
+        .args(["--deny", "clippy::todo"]);
+
+    eprintln!(
+        "running: {cargo} {}",
+        clippy_command
+            .get_args()
+            .map(|arg| format!("{}", arg.to_str().unwrap()))
+            .collect::<Vec<_>>()
+            .join(" ")
+    );
+
+    let exit_status = clippy_command
+        .spawn()
+        .context("failed to spawn child process")?
+        .wait()
+        .context("failed to wait for child process")?;
+
+    if !exit_status.success() {
+        bail!("clippy failed: {}", exit_status);
+    }
+
+    Ok(())
+}