You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Deny std methods that panic on bad input with clippy disallowed-methods #800
The panic lints denied in dnp3/src/lib.rs (unwrap_used, expect_used, indexing_slicing, arithmetic_side_effects, ...) only catch panic sites written in our own code. They don't flag std methods that panic internally on bad arguments. Checked with clippy 0.1.99:
arithmetic_side_effects already flags Instant + Duration and Instant - Duration.
slice::split_at, copy_from_slice and chunks(0) aren't flagged by any lint we deny.
clippy::disallowed_methods with a clippy.toml entry such as { path = "slice::split_at", reason = "panics if mid > len; use split_at_checked" } does flag them. There's no lint that knows which std methods panic, so it's a hand-maintained list.
Rough count in dnp3/src outside test directories (in-file #[cfg(test)] modules not excluded): split_at ~13, chunks 6, borrow_mut 4, copy_from_slice 2, split_off 1. Calls on the decode path are covered by the no-panic link check (dnp3/src/no_panic_check.rs); the rest (outstation request handling, sessions, master tasks) isn't.
Proposal:
Deny clippy::disallowed_methods and list the panicking std methods the crate uses, each with a reason naming the checked alternative:
split_at / split_at_mut -> split_at_checked / split_at_mut_checked (stable since 1.80, within the 1.88 MSRV)
copy_from_slice / clone_from_slice -> check lengths with get / get_mut first
RefCell::borrow / borrow_mut -> try_borrow / try_borrow_mut, or a narrow #[expect]
chunks / chunks_exact / windows have no checked form: a nonzero size by construction, or a narrow #[expect(clippy::disallowed_methods, reason = "...")]
Same exception policy as the other panic lints: allowed only where input can't reach the call, each with a narrow #[expect] and reason. Decide whether test code may use them, as clippy.toml already allows for unwrap and indexing.
Lint every shipped feature configuration, like the existing clippy jobs.
Update: don't build the list from the rough grep above. Start from the disallowed-methods list the rodbus session is finishing (its item L1, not merged yet), and add entries for any panicking std methods dnp3 uses that rodbus doesn't.
What that list establishes:
116 entries in rodbus's clippy.toml, each shown to fire on a planted call with clippy 0.1.99, with clippy::disallowed_methods denied in lib.rs.
Path spelling matters, so check that every entry fires. slice::copy_from_slice, str::split_at, u32::ilog2, std::vec::Vec::remove, std::time::Duration::new and std::iter::Iterator::step_by resolve. core::slice::<impl [T]>::... warns as unreachable. <[T]>::... and [T]::... are silently ignored, so an entry that matches nothing looks like coverage and isn't.
Excluded on purpose: allocation failure, tokio's runtime precondition (deterministic), lock poisoning in the approved lock helper, and methods that already return Option/Result.
rodbus rewrote its production uses rather than adding #[expect]: copy_from_slice became a zip with the length carried by a length type, chunks became as_chunks::<N>, and copy_within became a front-to-back copy through Cell. Cost: +512 B in the FFI library, performance flat.
Same patterns already spotted in dnp3: dnp3/src/link/reader.rs:112 (copy_within), dnp3/src/link/reader.rs:441 (copy_from_slice), dnp3/src/udp/layer.rs:336.
The rodbus list is merged: stepfunc/rodbus#343 (the 116-entry disallowed-methods list in rodbus/clippy.toml, clippy::disallowed_methods denied in rodbus/src/lib.rs), with the production rewrites in stepfunc/rodbus#311. Start from that file.
Production needed three rewrites, with no #[expect]: copy_from_slice became a zip copy with the length carried by the type, chunks became as_chunks::<N>, and copy_within became a front-to-back copy through Cell.
Test code needed 15 mechanical changes: split_at_checked(..).unwrap(), as_chunks, iter().map(slice::from_ref), and mem::take instead of drain.
Clippy with the no-panic cfg reports mem_forget from the no_panic attribute's expansion. This was already the case before the list; CI doesn't lint that cfg.
Activity
jadamcrain commentedon Oct 23, 2026
Update: don't build the list from the rough grep above. Start from the
disallowed-methodslist the rodbus session is finishing (its item L1, not merged yet), and add entries for any panicking std methods dnp3 uses that rodbus doesn't.What that list establishes:
clippy.toml, each shown to fire on a planted call with clippy 0.1.99, withclippy::disallowed_methodsdenied inlib.rs.slice::copy_from_slice,str::split_at,u32::ilog2,std::vec::Vec::remove,std::time::Duration::newandstd::iter::Iterator::step_byresolve.core::slice::<impl [T]>::...warns as unreachable.<[T]>::...and[T]::...are silently ignored, so an entry that matches nothing looks like coverage and isn't.Option/Result.#[expect]:copy_from_slicebecame a zip with the length carried by a length type,chunksbecameas_chunks::<N>, andcopy_withinbecame a front-to-back copy throughCell. Cost: +512 B in the FFI library, performance flat.Same patterns already spotted in dnp3:
dnp3/src/link/reader.rs:112(copy_within),dnp3/src/link/reader.rs:441(copy_from_slice),dnp3/src/udp/layer.rs:336.jadamcrain commentedon Oct 24, 2026
The rodbus list is merged: stepfunc/rodbus#343 (the 116-entry
disallowed-methodslist inrodbus/clippy.toml,clippy::disallowed_methodsdenied inrodbus/src/lib.rs), with the production rewrites in stepfunc/rodbus#311. Start from that file.#[expect]:copy_from_slicebecame a zip copy with the length carried by the type,chunksbecameas_chunks::<N>, andcopy_withinbecame a front-to-back copy throughCell.split_at_checked(..).unwrap(),as_chunks,iter().map(slice::from_ref), andmem::takeinstead ofdrain.mem_forgetfrom theno_panicattribute's expansion. This was already the case before the list; CI doesn't lint that cfg.