clippy: enable panic-discipline lints
Assisted-by: Claude Opus 5 via Claude Code
change qokouqnstpkmqqoszutnwtwwmyksyztr
commit f9ab4503c217c14d59a1831573a88e7a587c4077
author Alpha Chen <alpha@kejadlen.dev>
date
parent kmkqkkzm
diff --git a/.clippy.toml b/.clippy.toml
index 152ece5..ff12a93 100644
--- a/.clippy.toml
+++ b/.clippy.toml
@@ -18,9 +18,15 @@ disallowed-methods = [
     { path = "std::fs::read_dir", reason = "use fs_err::read_dir" },
     { path = "std::fs::read_link", reason = "use fs_err::read_link" },
     { path = "std::fs::set_permissions", reason = "use fs_err::set_permissions" },
+    { path = "std::string::String::from_utf8_lossy", reason = "lossy at OS boundaries — consider OsStr/Path or stay in &[u8]; if input really is text, suppress with an #[allow] and a comment" },
 ]
 
 disallowed-types = [
     { path = "std::fs::File", reason = "use fs_err::File for better error messages" },
     { path = "std::fs::OpenOptions", reason = "use fs_err::OpenOptions" },
+    { path = "chrono::DateTime", reason = "use jiff::Zoned or jiff::Timestamp" },
+    { path = "chrono::NaiveDateTime", reason = "use jiff::civil::DateTime" },
+    { path = "chrono::NaiveDate", reason = "use jiff::civil::Date" },
+    { path = "chrono::NaiveTime", reason = "use jiff::civil::Time" },
+    { path = "chrono::Duration", reason = "use jiff::Span or jiff::SignedDuration" },
 ]
diff --git a/frork-cli/Cargo.toml b/frork-cli/Cargo.toml
index ee02148..94e44bc 100644
--- a/frork-cli/Cargo.toml
+++ b/frork-cli/Cargo.toml
@@ -24,4 +24,12 @@ tracing-subscriber = { version = "*", features = ["env-filter"] }
 assert_cmd = "*"
 predicates = "*"
 hegeltest = "*"
-tempfile = "*"
\ No newline at end of file
+tempfile = "*"
+
+[lints.clippy]
+self_named_module_files = "warn"
+unwrap_used = "warn"
+expect_used = "warn"
+panic = "warn"
+indexing_slicing = "warn"
+arithmetic_side_effects = "warn"
diff --git a/frork-cli/src/lib.rs b/frork-cli/src/lib.rs
index 2c6ff14..4f80e5e 100644
--- a/frork-cli/src/lib.rs
+++ b/frork-cli/src/lib.rs
@@ -1,4 +1,15 @@
-#![warn(clippy::self_named_module_files)]
+// Panic discipline applies to code that handles Fennel scripts and shell
+// output; test code asserts on known-good fixtures.
+#![cfg_attr(
+    test,
+    allow(
+        clippy::unwrap_used,
+        clippy::expect_used,
+        clippy::panic,
+        clippy::indexing_slicing,
+        clippy::arithmetic_side_effects
+    )
+)]
 
 pub mod assertions;
 pub mod error;
diff --git a/frork-cli/src/main.rs b/frork-cli/src/main.rs
index aa4052c..3847e01 100644
--- a/frork-cli/src/main.rs
+++ b/frork-cli/src/main.rs
@@ -1,3 +1,16 @@
+// Panic discipline applies to code that handles Fennel scripts and shell
+// output; test code asserts on known-good fixtures.
+#![cfg_attr(
+    test,
+    allow(
+        clippy::unwrap_used,
+        clippy::expect_used,
+        clippy::panic,
+        clippy::indexing_slicing,
+        clippy::arithmetic_side_effects
+    )
+)]
+
 mod assertions;
 mod error;
 mod utils;
diff --git a/frork-cli/src/utils.rs b/frork-cli/src/utils.rs
index 95ca14b..f47bad6 100644
--- a/frork-cli/src/utils.rs
+++ b/frork-cli/src/utils.rs
@@ -104,6 +104,8 @@ impl Utils {
     }
 
     pub fn expand_path(path: &str) -> Result<String> {
+        // The pattern is a literal, so it either always compiles or never does.
+        #[allow(clippy::unwrap_used)]
         static ENV_VAR_REGEX: LazyLock<Regex> =
             LazyLock::new(|| Regex::new(r"\$([A-Za-z_][A-Za-z0-9_]*)").unwrap());
 
@@ -118,18 +120,20 @@ impl Utils {
 
         // Expand environment variables
         let mut missing = Vec::new();
-        expanded = ENV_VAR_REGEX
-            .replace_all(&expanded, |caps: &regex::Captures| {
-                let var_name = caps.get(1).unwrap().as_str();
-                match env::var(var_name) {
-                    Ok(value) => value,
-                    Err(_) => {
-                        missing.push(var_name.to_string());
-                        caps.get(0).unwrap().as_str().to_string()
-                    }
+        // Groups 0 and 1 are non-optional in the pattern, so a match
+        // guarantees both are present.
+        #[allow(clippy::unwrap_used)]
+        let replaced = ENV_VAR_REGEX.replace_all(&expanded, |caps: &regex::Captures| {
+            let var_name = caps.get(1).unwrap().as_str();
+            match env::var(var_name) {
+                Ok(value) => value,
+                Err(_) => {
+                    missing.push(var_name.to_string());
+                    caps.get(0).unwrap().as_str().to_string()
                 }
-            })
-            .to_string();
+            }
+        });
+        expanded = replaced.to_string();
 
         if !missing.is_empty() {
             return Err(miette!(
@@ -171,6 +175,9 @@ impl Utils {
             .output()
             .map_err(|e| miette!("Failed to execute command '{}': {e}", cmd))?;
 
+        // `sh` hands stdout to Fennel as a string, and scripts compare it as
+        // text. Non-UTF-8 output would be unusable there either way.
+        #[allow(clippy::disallowed_methods)]
         let stdout = String::from_utf8_lossy(&output.stdout).to_string();
         let status = output
             .status
diff --git a/frork-cli/tests/cli.rs b/frork-cli/tests/cli.rs
index 86fcfbd..511847c 100644
--- a/frork-cli/tests/cli.rs
+++ b/frork-cli/tests/cli.rs
@@ -3,7 +3,7 @@ use assert_cmd::cargo::cargo_bin_cmd;
 use predicates::str::contains;
 
 fn cmd() -> Command {
-    Command::from(cargo_bin_cmd!("frork"))
+    cargo_bin_cmd!("frork")
 }
 
 #[test]