Skip to content

Commit d87c012

Browse files
goxberryclaude
andcommitted
refactor(lading): annotate intentional-panic network generators
Attach fn-level #[expect(clippy::expect_used, reason = "...")] to the construction and spin sites in the network protocol generators, plus Cat-3 mechanical conversions for procfs path literals. - generator/http.rs::{new, spin} (OnceCell + Semaphore) - generator/grpc.rs::{new, connect} (target_uri parsing, FIXME on new) - generator/splunk_hec.rs::{new, spin, send_hec_request} (FIXME on parse) - generator/tcp.rs::new (FIXME: addr parsing) - generator/procfs.rs (Cat-3 /proc paths) - generator/kubernetes/resource.rs::get_name (set_name contract) - generator/splunk_hec/acknowledgements.rs::send (ack-id contract) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e24474b commit d87c012

7 files changed

Lines changed: 49 additions & 6 deletions

File tree

lading/src/generator/grpc.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ impl Grpc {
192192
/// Function will panic if user has passed zero values for any byte
193193
/// values. Sharp corners.
194194
#[expect(clippy::cast_possible_truncation)]
195+
#[expect(
196+
clippy::expect_used,
197+
reason = "FIXME: config.target_uri is user-supplied; parsing and the required path_and_query check should surface as Error variants instead of panicking. Tracked for follow-up."
198+
)]
195199
pub fn new(
196200
general: General,
197201
config: Config,
@@ -241,6 +245,10 @@ impl Grpc {
241245
}
242246

243247
/// Establish a connection with the configured RPC server
248+
#[expect(
249+
clippy::expect_used,
250+
reason = "Uri::from_parts is reconstructing self.target_uri's parts after replacing path_and_query with an empty static; the parts already came from a valid Uri"
251+
)]
244252
async fn connect(&self) -> Result<client::Grpc<transport::Channel>, Error> {
245253
let mut parts = self.target_uri.clone().into_parts();
246254
parts.path_and_query = Some(PathAndQuery::from_static(""));

lading/src/generator/http.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ impl Http {
141141
/// Function will panic if user has passed non-zero values for any byte
142142
/// values. Sharp corners.
143143
#[expect(clippy::cast_possible_truncation)]
144+
#[expect(
145+
clippy::expect_used,
146+
reason = "OnceCell::set is called exactly once at HTTP generator startup"
147+
)]
144148
pub fn new(
145149
general: General,
146150
config: Config,
@@ -212,6 +216,10 @@ impl Http {
212216
///
213217
/// Function will panic if it is unable to create HTTP requests for the
214218
/// target.
219+
#[expect(
220+
clippy::expect_used,
221+
reason = "OnceCell::get on a value set during `new`, and Semaphore::acquire panics only after `Semaphore::close`; we never close the throttle semaphore"
222+
)]
215223
pub async fn spin(mut self) -> Result<(), Error> {
216224
let client = Client::builder(TokioExecutor::new())
217225
.pool_max_idle_per_host(self.concurrency.connection_count() as usize)

lading/src/generator/kubernetes/resource.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ impl Resource {
7676
}
7777
}
7878

79+
#[expect(
80+
clippy::expect_used,
81+
reason = "callers must set the name via `set_name` before calling `get_name`; this is a documented internal API contract"
82+
)]
7983
pub(super) fn get_name(&self) -> &str {
8084
self.meta()
8185
.name

lading/src/generator/procfs.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ pub enum Error {
3333

3434
fn default_copy_from_host() -> Vec<PathBuf> {
3535
vec![
36-
PathBuf::from_str("/proc/uptime").expect("failed to convert /proc/uptime to PathBuf"),
37-
PathBuf::from_str("/proc/stat").expect("failed to convert /proc/stat to PathBuf"),
36+
PathBuf::from_str("/proc/uptime")
37+
.unwrap_or_else(|_| unreachable!("\"/proc/uptime\" is a valid PathBuf")),
38+
PathBuf::from_str("/proc/stat")
39+
.unwrap_or_else(|_| unreachable!("\"/proc/stat\" is a valid PathBuf")),
3840
]
3941
}
4042

@@ -141,7 +143,8 @@ impl ProcFs {
141143
}
142144

143145
// SAFETY: By construction this pathbuf cannot fail to be created.
144-
let prefix = PathBuf::from_str("/proc").expect("failed to convert /proc to PathBuf");
146+
let prefix = PathBuf::from_str("/proc")
147+
.unwrap_or_else(|_| unreachable!("\"/proc\" is a valid PathBuf"));
145148

146149
for path in &config.copy_from_host {
147150
let base = path.strip_prefix(&prefix)?;

lading/src/generator/splunk_hec.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ impl SplunkHec {
192192
/// Function will panic if user has passed non-zero values for any byte
193193
/// values. Sharp corners.
194194
#[expect(clippy::cast_possible_truncation)]
195+
#[expect(
196+
clippy::expect_used,
197+
reason = "OnceCell::set is called exactly once at Splunk HEC generator startup"
198+
)]
195199
pub fn new(
196200
general: General,
197201
config: Config,
@@ -267,6 +271,10 @@ impl SplunkHec {
267271
///
268272
/// Function will panic if it is unable to create HTTP requests for the
269273
/// target.
274+
#[expect(
275+
clippy::expect_used,
276+
reason = "channel iterator is constructed from a non-empty Vec; OnceCell::get and Semaphore::acquire follow the same contract as the HTTP generator"
277+
)]
270278
pub async fn spin(mut self) -> Result<(), Error> {
271279
let client = Client::builder(TokioExecutor::new())
272280
.pool_max_idle_per_host(self.parallel_connections as usize)
@@ -339,6 +347,10 @@ impl SplunkHec {
339347
}
340348

341349
#[expect(clippy::too_many_arguments)]
350+
#[expect(
351+
clippy::expect_used,
352+
reason = "FIXME: server response parsing on Splunk HEC ack-id should surface as an Error variant rather than panic on malformed remote responses. Tracked for follow-up."
353+
)]
342354
async fn send_hec_request<B>(
343355
permit: SemaphorePermit<'_>,
344356
block_length: usize,

lading/src/generator/splunk_hec/acknowledgements.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ impl Channel {
5252
}
5353
}
5454

55+
#[expect(
56+
clippy::expect_used,
57+
reason = "callers route Some(ack_id) producers into Channel::Ack; the None branch is unreachable per the worker/ack-service contract"
58+
)]
5559
pub(crate) async fn send<Fut>(&self, msg: Fut) -> Result<(), Error>
5660
where
5761
Fut: Future<Output = Option<AckId>>,

lading/src/generator/tcp.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ impl Tcp {
131131
/// Function will panic if user has passed zero values for any byte
132132
/// values. Sharp corners.
133133
#[expect(clippy::cast_possible_truncation)]
134+
#[expect(
135+
clippy::expect_used,
136+
reason = "FIXME: config.addr is user-supplied; socket address parsing failures should surface as Error variants instead of panicking. Tracked for follow-up."
137+
)]
134138
pub fn new(
135139
general: General,
136140
config: &Config,
@@ -168,9 +172,9 @@ impl Tcp {
168172
for i in 0..worker_count {
169173
let throttle =
170174
create_throttle(config.throttle.as_ref(), config.bytes_per_second.as_ref())?
171-
.divide(
172-
NonZeroU32::new(worker_count.into()).expect("worker_count is always >= 1"),
173-
)?;
175+
.divide(NonZeroU32::new(worker_count.into()).unwrap_or_else(|| {
176+
unreachable!("worker_count is NonZeroU16, always >= 1")
177+
}))?;
174178

175179
let mut worker_labels = labels.clone();
176180
if worker_count > 1 {

0 commit comments

Comments
 (0)