From 52e046600c78c7450efbdd2b6a5e83c203cbe440 Mon Sep 17 00:00:00 2001 From: Robert Queenin <2177841+ecalifornica@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:56:24 -0400 Subject: [PATCH] chore: clippy disallows std::env access outside the config module clippy.toml adds a disallowed-methods rule for std::env::var, var_os, vars, vars_os, set_var, and remove_var. Readers must take the value as a parameter. Tests construct values instead of mutating the environment. The clippy quality gate runs with --all-targets, so the rule covers unit tests, integration tests, examples, and build scripts. Five sites carry an #[expect] with a reason: - crates/path-cli/src/config.rs search_path: the config module is the one place that reads the environment. - crates/path-cli/src/fuzzy.rs which: the fzf probe keeps its own read until the search path's owner is decided in review. - crates/pathbase-client/build.rs: cargo passes OUT_DIR to a build script only through the environment. - crates/toolpath-cursor/examples/dump_fixture.rs and crates/toolpath-cursor/tests/real_session_sanity.rs: both read the developer's real Cursor store, so both must locate the real home directory. --- clippy.toml | 8 ++++++++ crates/path-cli/src/config.rs | 4 ++++ crates/path-cli/src/fuzzy.rs | 4 ++++ crates/pathbase-client/build.rs | 4 ++++ crates/toolpath-cursor/examples/dump_fixture.rs | 4 ++++ crates/toolpath-cursor/tests/real_session_sanity.rs | 4 ++++ scripts/quality_gates.sh | 2 +- 7 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..302aeedc --- /dev/null +++ b/clippy.toml @@ -0,0 +1,8 @@ +disallowed-methods = [ + { path = "std::env::var", reason = "environment access lives in path-cli's config module; take the value as a parameter" }, + { path = "std::env::var_os", reason = "environment access lives in path-cli's config module; take the value as a parameter" }, + { path = "std::env::vars", reason = "environment access lives in path-cli's config module; take the value as a parameter" }, + { path = "std::env::vars_os", reason = "environment access lives in path-cli's config module; take the value as a parameter" }, + { path = "std::env::set_var", reason = "tests construct values instead of mutating the environment" }, + { path = "std::env::remove_var", reason = "tests construct values instead of mutating the environment" }, +] diff --git a/crates/path-cli/src/config.rs b/crates/path-cli/src/config.rs index ff7f41ca..0aeb9819 100644 --- a/crates/path-cli/src/config.rs +++ b/crates/path-cli/src/config.rs @@ -176,6 +176,10 @@ impl Config { /// /// The environment is read here so consumers take the search path as a /// parameter. +#[expect( + clippy::disallowed_methods, + reason = "this module is the one place that reads the environment" +)] pub(crate) fn search_path() -> Vec { std::env::var_os("PATH") .map(|p| std::env::split_paths(&p).collect()) diff --git a/crates/path-cli/src/fuzzy.rs b/crates/path-cli/src/fuzzy.rs index d37f7848..e4a3b45f 100644 --- a/crates/path-cli/src/fuzzy.rs +++ b/crates/path-cli/src/fuzzy.rs @@ -100,6 +100,10 @@ pub const fn embedded_picker_available() -> bool { false } +#[expect( + clippy::disallowed_methods, + reason = "the fzf probe keeps its own read until the search path's owner is decided in review" +)] fn which(cmd: &str) -> Option { let path = std::env::var_os("PATH")?; for dir in std::env::split_paths(&path) { diff --git a/crates/pathbase-client/build.rs b/crates/pathbase-client/build.rs index 04e9fc6e..2a2a67fa 100644 --- a/crates/pathbase-client/build.rs +++ b/crates/pathbase-client/build.rs @@ -46,6 +46,10 @@ fn main() { let ast = syn::parse2::(tokens).expect("parse generated tokens"); let formatted = prettyplease::unparse(&ast); + #[expect( + clippy::disallowed_methods, + reason = "cargo passes OUT_DIR to a build script only through the environment" + )] let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR set by cargo")); let out_file = out_dir.join("pathbase_client.rs"); fs::write(&out_file, formatted).unwrap_or_else(|e| panic!("write {}: {e}", out_file.display())); diff --git a/crates/toolpath-cursor/examples/dump_fixture.rs b/crates/toolpath-cursor/examples/dump_fixture.rs index 18c3a517..66290a62 100644 --- a/crates/toolpath-cursor/examples/dump_fixture.rs +++ b/crates/toolpath-cursor/examples/dump_fixture.rs @@ -147,6 +147,10 @@ fn referenced_blob_hashes(session: &CursorSession) -> std::collections::HashSet< /// The home directory this example reads Cursor state under. The /// library takes it as an argument, so the caller supplies it. +#[expect( + clippy::disallowed_methods, + reason = "this example reads the developer's real Cursor store, so it must locate the real home directory" +)] fn home_dir() -> Option { std::env::var_os("HOME") .or_else(|| std::env::var_os("USERPROFILE")) diff --git a/crates/toolpath-cursor/tests/real_session_sanity.rs b/crates/toolpath-cursor/tests/real_session_sanity.rs index 014b0d1d..9c923495 100644 --- a/crates/toolpath-cursor/tests/real_session_sanity.rs +++ b/crates/toolpath-cursor/tests/real_session_sanity.rs @@ -71,6 +71,10 @@ fn real_cursor_db_round_trips_when_present() { /// The home directory this test reads Cursor state under. The library /// takes it as an argument, so the caller supplies it. +#[expect( + clippy::disallowed_methods, + reason = "this test reads the developer's real Cursor store, so it must locate the real home directory" +)] fn home_dir() -> Option { std::env::var_os("HOME") .or_else(|| std::env::var_os("USERPROFILE")) diff --git a/scripts/quality_gates.sh b/scripts/quality_gates.sh index 45a72012..9eea48e4 100755 --- a/scripts/quality_gates.sh +++ b/scripts/quality_gates.sh @@ -70,7 +70,7 @@ gate_shellcheck() { # shellcheck disable=SC2329 gate_clippy() { - cargo clippy --workspace -- -D warnings 2>&1 + cargo clippy --workspace --all-targets -- -D warnings 2>&1 } # shellcheck disable=SC2329