diff --git a/lading/src/bin/captool/main.rs b/lading/src/bin/captool/main.rs index 23c39a396..2b152fd4b 100644 --- a/lading/src/bin/captool/main.rs +++ b/lading/src/bin/captool/main.rs @@ -1,9 +1,6 @@ //! Capture analysis tool for lading capture files. #![expect(clippy::print_stdout)] -// Quarantine: workspace denies `clippy::expect_used`, but this binary still has -// production `.expect()` sites awaiting cleanup. Remove once cleaned up. -#![allow(clippy::expect_used)] mod analyze; @@ -75,6 +72,10 @@ pub enum Error { #[tokio::main(flavor = "current_thread")] #[expect(clippy::too_many_lines)] +#[expect( + clippy::expect_used, + reason = "FIXME: line read and JSON deserialization should surface as Error variants rather than panicking; tracked for follow-up. Task join panics are intentional propagation of inner panics." +)] async fn main() -> Result<(), Error> { tracing_subscriber::fmt() .with_span_events(FmtSpan::FULL) @@ -230,6 +231,10 @@ async fn main() -> Result<(), Error> { Ok(()) } +#[expect( + clippy::expect_used, + reason = "FIXME: line read and JSON deserialization should surface as Error variants rather than panicking; tracked for follow-up. Task join panics are intentional propagation of inner panics." +)] async fn validate_capture(capture_path_str: &str, min_seconds: Option) -> Result<(), Error> { let capture_path = path::Path::new(capture_path_str); if !capture_path.exists() { @@ -286,6 +291,10 @@ async fn validate_capture(capture_path_str: &str, min_seconds: Option) -> R report_validation_result(&result, min_seconds) } +#[expect( + clippy::expect_used, + reason = "ValidationResult::first_error is Some whenever is_valid() returns false; the unreachable None branch indicates a programming error in the validator" +)] fn report_validation_result( result: &lading_capture::validate::ValidationResult, min_seconds: Option, diff --git a/lading/src/bin/lading.rs b/lading/src/bin/lading.rs index 932af5f5f..b0f3042ca 100644 --- a/lading/src/bin/lading.rs +++ b/lading/src/bin/lading.rs @@ -1,9 +1,5 @@ //! Main lading binary for load testing. -// Quarantine: workspace denies `clippy::expect_used`, but this binary still has -// production `.expect()` sites awaiting cleanup. Remove once cleaned up. -#![allow(clippy::expect_used)] - use std::{ env, fmt::{self, Display}, @@ -105,6 +101,10 @@ impl Display for CliKeyValues { impl FromStr for CliKeyValues { type Err = String; + #[expect( + clippy::expect_used, + reason = "compile-time-constant regex literal; capture group 0 always exists on a successful match" + )] fn from_str(input: &str) -> Result { // A key matches `[[:alnum:]_-]+` (letters, digits, underscores, // hyphens) and a value conforms to `[[:alpha:]_:,`. A key is always @@ -307,6 +307,10 @@ fn validate_config(config_path: &str) -> Result { } } +#[expect( + clippy::expect_used, + reason = "PIDs from --target-pid CLI argument fit in i32 on supported platforms; a failure indicates a platform invariant violation" +)] fn get_config(args: &LadingArgs, config: Option) -> Result { let mut config = if let Some(contents) = config { // Config provided via environment variable - parse as single file @@ -401,6 +405,10 @@ fn get_config(args: &LadingArgs, config: Option) -> Result, } +#[expect( + clippy::expect_used, + reason = "Byte::from_u128 only fails on 0; total_bytes is NonZeroU32, and the diagnostic-path total_generated_bytes path inherits the same invariant from the caller" +)] fn generate_and_check( config: &lading_payload::Config, seed: [u8; 32], @@ -367,6 +368,10 @@ fn generate_and_check( } #[expect(clippy::too_many_lines)] +#[expect( + clippy::expect_used, + reason = "FIXME: maximum_prebuild_cache_size_bytes is user-supplied config; a zero value should surface as a recoverable error rather than panicking. Tracked for follow-up." +)] fn check_generator(config: &generator::Config, args: &Args) -> Result> { match &config.inner { generator::Inner::FileGen(g) => { diff --git a/lading/src/generator/process_tree.rs b/lading/src/generator/process_tree.rs index f475716c6..6da852b0c 100644 --- a/lading/src/generator/process_tree.rs +++ b/lading/src/generator/process_tree.rs @@ -441,6 +441,10 @@ impl Process { /// /// Function will panic if the process execution fails. /// +#[expect( + clippy::expect_used, + reason = "the iterator is populated from the caller's pre-validated process tree; missing nodes or missing exit codes indicate a programming error in the tree construction" +)] pub fn spawn_tree(nodes: &VecDeque, sleep_ns: u32) -> Result<(), Error> { let mut iter = nodes.iter().peekable(); let mut pids_to_wait: FxHashSet = FxHashSet::default(); diff --git a/lading/src/generator/tcp_rr.rs b/lading/src/generator/tcp_rr.rs index ca39e3ca1..e6ed6a883 100644 --- a/lading/src/generator/tcp_rr.rs +++ b/lading/src/generator/tcp_rr.rs @@ -128,6 +128,10 @@ impl TcpRr { /// # Panics /// /// Panics if `addr` cannot be resolved to a socket address. + #[expect( + clippy::expect_used, + reason = "FIXME: config.addr is user-supplied; parse failure should surface as an Error variant instead of panicking. Tracked for follow-up." + )] pub async fn spin(self) -> Result<(), Error> { if self.config.threads > self.config.flows { return Err(Error::Config(format!( @@ -233,6 +237,10 @@ impl TcpRr { } } +#[expect( + clippy::expect_used, + reason = "mio Poll creation, nonblocking setup, and registry registration fail only on system resource exhaustion; documented contract for the per-thread client startup" +)] fn client_thread_main( addr: SocketAddr, num_flows: u16, diff --git a/lading/src/generator/udp.rs b/lading/src/generator/udp.rs index ecbcf2a66..f4ebf2c0a 100644 --- a/lading/src/generator/udp.rs +++ b/lading/src/generator/udp.rs @@ -142,6 +142,10 @@ impl Udp { /// Function will panic if user has passed zero values for any byte /// values. Sharp corners. #[expect(clippy::cast_possible_truncation)] + #[expect( + clippy::expect_used, + reason = "FIXME: config.addr is user-supplied; socket address parsing failures should surface as Error variants instead of panicking. Tracked for follow-up." + )] pub fn new( general: General, config: &Config, @@ -179,9 +183,9 @@ impl Udp { for i in 0..worker_count { let throttle = create_throttle(config.throttle.as_ref(), config.bytes_per_second.as_ref())? - .divide( - NonZeroU32::new(worker_count.into()).expect("worker_count is always >= 1"), - )?; + .divide(NonZeroU32::new(worker_count.into()).unwrap_or_else(|| { + unreachable!("worker_count is NonZeroU16, always >= 1") + }))?; let mut worker_labels = labels.clone(); if worker_count > 1 { @@ -236,6 +240,10 @@ struct UdpWorker { } impl UdpWorker { + #[expect( + clippy::expect_used, + reason = "the wait_for_block branch is gated on `connection.is_some()` in the tokio::select! arm; the Option is guaranteed Some when this branch fires" + )] async fn spin(mut self) -> Result<(), Error> { debug!("UDP generator worker running"); let mut connection = Option::::None; diff --git a/lading/src/lib.rs b/lading/src/lib.rs index 4c1aac577..f301d63ee 100644 --- a/lading/src/lib.rs +++ b/lading/src/lib.rs @@ -8,9 +8,6 @@ #![deny(clippy::cargo)] #![expect(clippy::cast_precision_loss)] #![expect(clippy::multiple_crate_versions)] -// Quarantine: workspace denies `clippy::expect_used`, but this crate still has -// production `.expect()` sites awaiting cleanup. Remove once cleaned up. -#![allow(clippy::expect_used)] use http_body_util::BodyExt;