Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
- Retired the remaining 17 production `.expect()` sites in `lading_payload`
and dropped the crate-root `#![allow(clippy::expect_used)]` quarantine.
13 sites became `.unwrap_or_else(... unreachable!("..."))` (structural
infallibles: serde_json on simple structs, RFC-3339 of a known-valid
`OffsetDateTime`, `NonZeroI64` after a documented positivity invariant,
`parts.last()` after `str::split`, `Vec::pop` after a known push, etc.).
4 sites in `dogstatsd/{common,metric,service_check}.rs` are annotated
with fn-level `#[expect(clippy::expect_used, reason = "...")]` because
the panic is the documented contract when upstream config validation
is too loose (empty `templates`/`names` vec, `ConfRange::Inclusive`
with `min > max`). `lading_payload` is now fully covered by the
workspace-level `clippy::expect_used = "deny"`.
- Fixed a latent panic in `lading_payload::block::Block::arbitrary`
(`#[cfg(feature = "arbitrary")]`). `u32::arbitrary` can legitimately
produce 0, but `Block::total_bytes` is `NonZeroU32`; the previous code
Expand Down
7 changes: 3 additions & 4 deletions lading_payload/src/common/strings/random_string_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,9 @@ where
let mut buf = Vec::with_capacity(total);
for _ in 0..total {
let sz = length_range.sample(&mut rng) as usize;
buf.push(String::from(
pool.of_size(&mut rng, sz)
.expect("failed to generate string"),
));
buf.push(String::from(pool.of_size(&mut rng, sz).unwrap_or_else(
|| unreachable!("pool is sized larger than length_range by caller convention"),
)));
}
buf
}
Expand Down
12 changes: 6 additions & 6 deletions lading_payload/src/datadog_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ where
R: rand::Rng + ?Sized,
{
match rng.random_range(0..2) {
0 => Message::Unstructured(
str_pool
.of_size_range(rng, 1_u8..16)
.expect("failed to generate string"),
),
0 => Message::Unstructured(str_pool.of_size_range(rng, 1_u8..16).unwrap_or_else(|| {
unreachable!("str_pool is sized 1_000_000 by construction; 1..16 always fits")
})),
1 => Message::Structured(
serde_json::to_string(&rng.random::<Structured>()).expect("failed to generate string"),
serde_json::to_string(&rng.random::<Structured>()).unwrap_or_else(|_| {
unreachable!("Structured is a struct of primitive fields with derived Serialize")
}),
),
_ => unreachable!(),
}
Expand Down
4 changes: 4 additions & 0 deletions lading_payload/src/dogstatsd/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ pub(crate) enum NumValueGenerator {

impl NumValueGenerator {
#[allow(clippy::cast_possible_truncation)]
#[expect(
clippy::expect_used,
reason = "ConfRange::Inclusive { min, max } does not validate min <= max at deserialization; a misconfigured config with min > max will panic here with the existing diagnostic message"
)]
pub(crate) fn new(conf: ValueConf) -> Self {
match conf.range {
ConfRange::Constant(c) => Self::Constant {
Expand Down
10 changes: 8 additions & 2 deletions lading_payload/src/dogstatsd/metric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ impl<'a> Generator<'a> for MetricGenerator {
type Error = Error;

#[expect(clippy::too_many_lines)]
#[expect(
clippy::expect_used,
reason = "self.templates is validated non-empty at MetricGenerator construction; an empty templates vector here is a serious upstream logic bug"
)]
fn generate<R>(&'a self, mut rng: &mut R) -> Result<Self::Output, Self::Error>
where
R: rand::Rng + ?Sized,
Expand All @@ -147,7 +151,7 @@ impl<'a> Generator<'a> for MetricGenerator {
let sample_rate = if prob < self.sampling_probability {
let sample_rate = self.sampling.sample(&mut rng).clamp(0.0, 1.0);
let sample_rate = common::ZeroToOne::try_from(sample_rate)
.expect("failed to convert sample rate to ZeroToOne");
.unwrap_or_else(|_| unreachable!("clamp(0.0, 1.0) guarantees ZeroToOne range"));
Some(sample_rate)
} else {
None
Expand Down Expand Up @@ -260,7 +264,9 @@ impl<'a> Generator<'a> for MetricGenerator {
.ok_or(Error::StringGenerate)?;
Ok(Metric::Set(Set {
name,
value: values.pop().expect("failed to pop value from Vec"),
value: values
.pop()
.unwrap_or_else(|| unreachable!("values has at least one push above")),
tags,
pools: &self.pools,
container_id,
Expand Down
4 changes: 4 additions & 0 deletions lading_payload/src/dogstatsd/service_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ impl<'a> Generator<'a> for ServiceCheckGenerator {
type Output = ServiceCheck<'a>;
type Error = Error;

#[expect(
clippy::expect_used,
reason = "self.names is validated non-empty at ServiceCheckGenerator construction; an empty names vector here is a serious upstream logic bug"
)]
fn generate<R>(&'a self, mut rng: &mut R) -> Result<Self::Output, Error>
where
R: rand::Rng + ?Sized,
Expand Down
14 changes: 6 additions & 8 deletions lading_payload/src/fluent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,15 @@ where
R: rand::Rng + ?Sized,
{
match rng.random_range(0..2) {
0 => RecordValue::String(
str_pool
.of_size_range(rng, 1_u8..16)
.expect("failed to generate string"),
),
0 => RecordValue::String(str_pool.of_size_range(rng, 1_u8..16).unwrap_or_else(|| {
unreachable!("str_pool is sized by construction; 1..16 always fits")
})),
1 => {
let mut obj = BTreeMap::new();
for _ in 0..rng.random_range(0..128) {
let key = str_pool
.of_size_range(rng, 1_u8..16)
.expect("failed to generate string");
let key = str_pool.of_size_range(rng, 1_u8..16).unwrap_or_else(|| {
unreachable!("str_pool is sized by construction; 1..16 always fits")
});
let val = rng.random();

obj.insert(key, val);
Expand Down
3 changes: 0 additions & 3 deletions lading_payload/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
#![deny(clippy::cargo)]
#![allow(clippy::cast_precision_loss)]
#![allow(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 std::{
io::{self, Write},
Expand Down
10 changes: 7 additions & 3 deletions lading_payload/src/syslog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@ impl Distribution<Member> for StandardUniform {
let range: i64 = 315_360_000; // ~10 years in seconds
let offset: i64 = rng.random_range(0..range);
let ts = OffsetDateTime::from_unix_timestamp(base_ts + offset)
.expect("timestamp in valid range");
ts.format(&Rfc3339).expect("failed to format timestamp")
.unwrap_or_else(|_| unreachable!("base_ts + offset stays within [2020, 2030] which is well inside the valid OffsetDateTime range"));
ts.format(&Rfc3339).unwrap_or_else(|_| {
unreachable!("RFC-3339 formatting of a valid OffsetDateTime cannot fail")
})
},
hostname: HOSTNAMES
.choose(rng)
Expand All @@ -84,7 +86,9 @@ impl Distribution<Member> for StandardUniform {
.unwrap_or_else(|| unreachable!("APP_NAMES is a non-empty const array")),
procid: rng.random_range(100..=9999),
msgid: rng.random_range(1..=999),
message: serde_json::to_string(&rng.random::<Message>()).expect("failed to serialize"),
message: serde_json::to_string(&rng.random::<Message>()).unwrap_or_else(|_| {
unreachable!("Message is a struct of primitive fields with derived Serialize")
}),
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions lading_payload/src/templated_json/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,8 @@ pub(super) struct ArraySpec {
/// between the outer quotation marks if `s` were serialized as a JSON string.
/// Called once at parse time when pre-compiling a `!format` template.
fn json_string_escape_content(s: &str) -> String {
let encoded = serde_json::to_string(s).expect("string serialization cannot fail");
let encoded = serde_json::to_string(s)
.unwrap_or_else(|_| unreachable!("serde_json::to_string on a &str cannot fail"));
encoded[1..encoded.len() - 1].to_string()
}

Expand Down Expand Up @@ -439,8 +440,11 @@ impl<'de> Deserialize<'de> for FormatSpec {
)));
}

let trailing_escaped =
json_string_escape_content(parts.last().expect("split yields at least one part"));
let trailing_escaped = json_string_escape_content(
parts
.last()
.unwrap_or_else(|| unreachable!("str::split always yields at least one part")),
);

let segments = parts[..parts.len() - 1]
.iter()
Expand Down
4 changes: 3 additions & 1 deletion lading_payload/src/templated_json/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,9 @@ impl Timestamp {
let new_ms = base_ms.saturating_add(delta_ms);
// base_ms >= TIMESTAMP_BASE_SECS_MIN * MS_PER_SEC and delta_ms >= 1,
// so new_ms is always nonzero; the error branch is unreachable in practice.
let new_nz = NonZeroI64::new(new_ms).expect("operations above guarantee nonzero");
let new_nz = NonZeroI64::new(new_ms).unwrap_or_else(|| {
unreachable!("base_ms >= TIMESTAMP_BASE_SECS_MIN * MS_PER_SEC and delta_ms >= 1")
});
self.0.set(Some(new_nz));
// Emit only the whole-second part as RFC-3339.
let dt = OffsetDateTime::from_unix_timestamp(new_ms / MS_PER_SEC)
Expand Down
Loading