Skip to content

Deny std methods that panic on bad input with clippy disallowed-methods #800

Closed

Description

@jadamcrain

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.

Activity

  1. jadamcrain commented on Oct 23, 2026

    @jadamcrain
    MemberAuthor

    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.

  2. jadamcrain commented on Oct 24, 2026

    @jadamcrain
    MemberAuthor

    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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions