Skip to content

chore(infra): prune the tree's lint suppressions and make the survivors expect - #720

Merged
AprilNEA merged 9 commits into
masterfrom
chore/prune-clippy-suppressions
Aug 21, 2026
Merged

chore(infra): prune the tree's lint suppressions and make the survivors expect#720
AprilNEA merged 9 commits into
masterfrom
chore/prune-clippy-suppressions

Conversation

@AprilNEA

Copy link
Copy Markdown
Owner

Summary

An audit of every lint suppression in the tree — 207 attributes carrying 247 lints — found that most of them were not carrying information. 78 were test modules restating the same unwrap/expect exemption by hand, 20 no longer suppressed anything at all, and seven files silenced cast_* for their entire contents. This prunes the tree to 130 attributes / 140 lints and changes the default form from allow (silent when it goes stale) to expect (fails the build when it does): the allow:expect ratio goes from 159:48 to 6:124.

The mechanism is worth keeping: rewriting every non-cfg_attr allow( to expect( and running clippy turns each dead suppression into an "unfulfilled lint expectation" warning. That is how the 20 below were found, and .claude/rules/rust.md now documents the recipe so the sweep can be repeated.

Notable finds:

  • openlogi-assets's three module-wide #![allow(dead_code)] blankets have been inert since those modules became pub mod — public items are reachable, so the lint could never fire. They would have silently hidden real dead code the moment a module went private again.
  • LightCapabilities is down to three bools, under struct_excessive_bools. InstanceGuard._handle and CGEventTapInformation are exempt from dead_code on their own.
  • _silence_pickfn was a dead function whose only job was keeping an unused PickFn import "used" — a hand-rolled way around unused_imports. Both are gone.

Three cases where expect provably cannot work keep allow, each with a comment naming which case applies:

  • cfg-conditional: platform::os_version returns Some(…) on macOS (so unnecessary_wraps fires) and None elsewhere (so it does not) — an expect there is green on macOS and red on the other two lanes.
  • lint raised inside a macro expansion: rustc does not credit an expectation with such a lint, so expect suppresses the warning and reports itself unfulfilled — unfixable under -D warnings. function_row's float_cmp on floats compared inside assert_eq! is the case here.
  • fulfilment differing between a crate's targets: a dead_code suppression on a helper only the tests call is fulfilled in the --lib build and unfulfilled in the --test build. These were already cfg_attr-wrapped.

Changes

  • root: new clippy.toml with allow-unwrap-in-tests / allow-expect-in-tests. Clippy's exemption covers #[cfg(test)] modules and #[test] functions; a free helper in a tests/ integration file is the one shape it cannot see, so openlogi-ipc's wire-format test keeps a file-level suppression, as do the three build scripts (a build script is not a test — it fails by panicking).
  • openlogi-camera: capture_windows's file-wide cast blanket is gone entirely — its five sites are cast_signed() / cast_unsigned(), which say "reinterpret the bits" rather than "trust me". uvc builds its descriptor fixture with to_le_bytes and keeps two narrowly scoped suppressions; capture keeps one on the statement that stores frame dimensions. capture_linux keeps its blanket: v4l2-sys-mit runs bindgen, so that backend cannot be cross-linted from macOS and narrowing it would be blind work.
  • openlogi-hook: the Win32 backend's four-lint file blanket is replaced by &raw mut / &raw const at its six FFI pointer coercions (the edition-2024 spelling, and the better one at an FFI boundary since it never materializes the intermediate reference), cast_signed() at three sites, and four function-scoped expects for what is genuinely left.
  • openlogi-agent: tray_windows's blanket narrowed to the five functions that need it; the three build scripts' expect_used restated as expect.
  • openlogi-desktop: camera/controls funnelled eleven ad-hoc i32 as f32 / f32 as i32 conversions through a to_slider/from_slider pair, so the UVC-range argument is made once where it belongs instead of eleven times by omission. function_row's cast blanket narrowed to four functions. Nine dead dead_code/cast suppressions removed.
  • openlogi-core, openlogi-hidpp, openlogi-hid, openlogi-assets, openlogi-cli, openlogi-inject, openlogi-ipc, xtask: dead suppressions removed, survivors converted to expect.
  • docs: .claude/rules/rust.md no longer tells agents to hand-write a test-module #[allow], no longer describes openlogi-hidpp as sitting outside the workspace lint table (its own AGENTS.md retired that), and gains the expect-by-default rule plus the scoping rule for cast suppressions. docs/DECISIONS.md records the decision with the numbers behind it.

Testing

Full local gate on the final tree:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --document-private-items \
  --exclude openlogi-ui --exclude openlogi-desktop --exclude openlogi-overlay --exclude openlogi-agent

Because a platform-gated suppression is only ever evaluated on its own platform, and because an expect that goes unfulfilled is a hard error under -D warnings, the sweep was verified on all three lint lanes rather than just the local one:

cargo clippy --target x86_64-pc-windows-gnu -p openlogi-core -p openlogi-hidpp -p openlogi-hid \
  -p openlogi-hook -p openlogi-agent -p openlogi-agent-core -p openlogi-inject -p openlogi-camera --all-targets
cargo clippy --target aarch64-unknown-linux-musl -p openlogi-core -p openlogi-hidpp -p openlogi-hid \
  -p openlogi-ipc -p openlogi-permissions -p openlogi-hook -p openlogi-inject -p openlogi-agent-core -p openlogi-agent --all-targets

Both clean. Not covered locally: the GPUI crates off macOS (no cross build) and openlogi-camera's v4l2 backend (bindgen) — CI's clippy and clippy (windows) jobs own those.

This is a lint-hygiene change with no behavioural surface, so there is nothing to verify on hardware. The three code rewrites that are not pure attribute edits — cast_signed/cast_unsigned, &raw mut/&raw const, and the to_slider/from_slider pair — are each exactly equivalent to what they replace, and all of them are on Windows or in the camera panel; not runtime-tested on hardware.

Follow-ups

  • capture_linux's file-level cast blanket stays until the v4l2 backend can be linted somewhere other than a Linux box.
  • CI has no macOS clippy job. Every #[cfg(target_os = "macos")] suppression — the ObjC FFI surface in openlogi-inject, openlogi-hook, openlogi-camera, openlogi-permissions, and both GPUI apps — is checked only by the maintainer's local gate. That gap predates this PR, but expect makes it more valuable to close.

@AprilNEA
AprilNEA requested a review from davidbudnick as a code owner August 20, 2026 15:33
Copilot AI lite review requested due to automatic review settings August 20, 2026 15:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@davidbudnick davidbudnick added the area: ci CI, automation, and release workflows label Aug 20, 2026
@davidbudnick

Copy link
Copy Markdown
Collaborator

@greptile-apps

@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR audits workspace lint suppressions, removes obsolete exemptions, and converts most surviving allow attributes to compiler-checked expect attributes.

  • Adds workspace Clippy configuration exempting unwrap and expect usage in tests.
  • Narrows broad cast and dead-code suppressions to specific items.
  • Replaces several casts and FFI pointer coercions with explicit equivalents.
  • Documents the suppression policy and repeatable audit procedure.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
Cargo.toml Enables Clippy checks that require suppression reasons and prefer checked expectations.
clippy.toml Centralizes unwrap and expect exemptions for recognized test contexts.
crates/openlogi-desktop/src/features/camera/controls.rs Consolidates repeated camera slider conversions into narrowly suppressed helpers.
crates/openlogi-hook/src/windows.rs Replaces reference-to-pointer coercions and signed casts with explicit raw-pointer and bit-preserving forms.
.claude/rules/rust.md Documents expectation-first lint suppression policy, exceptions, scoping, and audit steps.

Reviews (2): Last reviewed commit: "fix(camera): drop unfulfilled linux cast..." | Re-trigger Greptile

@davidbudnick

Copy link
Copy Markdown
Collaborator

@greptile-apps

AprilNEA and others added 8 commits August 20, 2026 15:07
The workspace table keeps `unwrap_used`/`expect_used` at warn so production
code has to state its panics, but every test module then re-stated the same
exemption by hand — 78 copies of `#[allow(…, reason = "idiomatic in tests")]`,
roughly 40% of every lint suppression in the tree. Clippy has a config for
exactly this; a two-line `clippy.toml` replaces all of them.

Three build scripts and the `openlogi-ipc` wire-format test keep a suppression,
because neither is a test in clippy's sense: a build script fails by panicking,
and the golden-bytes fixtures sit in free helpers outside any `#[test]` fn —
the one test shape `allow-expect-in-tests` cannot see. Those four are now
`expect`, so they warn if they ever stop being needed.
`#[allow]` is silent when it stops suppressing anything, so suppressions rot
in place: nothing tells you the cast it covered was deleted three refactors
ago. `#[expect]` warns instead. Converting the 77 non-`cfg_attr` suppressions
surfaced 20 that no longer suppress anything, all removed here:

- `openlogi-assets` {index,manifest,metadata}: three module-wide `dead_code`
  blankets that have been inert since the modules became `pub mod` — public
  items are reachable, so the lint could never fire. The dangerous kind: had
  those modules gone private again, the blanket would have hidden real dead
  code forever.
- `LightCapabilities` is down to three bools, under `struct_excessive_bools`.
- `InstanceGuard._handle` and `CGEventTapInformation` are exempt from
  `dead_code` on their own (leading underscore / all fields now read).
- Nine GUI `dead_code` and cast suppressions, and the surplus lint names in
  six multi-lint blankets that only ever needed one or two of them.

Four suppressions stay `allow` on purpose, each with a comment saying why
`expect` is wrong there:

- `platform::os_version` — the lint only fires on the macOS arm; off macOS the
  expectation would go unfulfilled and redden the Linux/Windows lanes.
- `function_row`'s `float_cmp` — rustc does not credit an expectation with a
  lint raised inside a macro expansion (`assert_eq!` here), so `expect` would
  suppress the warning and then report itself unfulfilled: unfixable under
  `-D warnings`.
- The four `cfg_attr`-wrapped ones, whose fulfilment differs between the lib
  and test targets of the same crate.

Also drops `_silence_pickfn`, a dead function that existed only to keep an
unused `PickFn` import "used" — a hand-rolled lint workaround; the import goes
with it. And gives the tree's one reason-less suppression a reason.

Verified on the macOS and windows-gnu lanes; the Linux lane is CI's.
Seven files silenced `cast_possible_truncation` / `cast_sign_loss` /
`cast_possible_wrap` (and in the hook's Win32 backend `borrow_as_ptr` and
`needless_pass_by_value`) for their entire contents. Between them they covered
50 real sites — and every future cast anyone adds to those files, unchecked.

Most of the sites did not need a suppression at all:

- `capture_windows` and the hook's Win32 backend now use `cast_signed()` /
  `cast_unsigned()`, which say "reinterpret the bits" instead of "trust me".
  That is the whole file-level blanket gone from `capture_windows`.
- The hook's six `borrow_as_ptr` sites become `&raw mut` / `&raw const`, the
  edition-2024 spelling — and the better one at an FFI boundary, since it
  never materializes the intermediate reference.
- `uvc`'s descriptor fixture builds its two bytes with `to_le_bytes`.
- `camera/controls` funnelled eleven ad-hoc `i32 as f32` / `f32 as i32`
  conversions through `to_slider`/`from_slider`, so the UVC-range argument is
  made once where it belongs rather than eleven times by omission.

What genuinely needs a cast keeps one, scoped to the function that does it and
with a reason naming the bound that makes it safe.

`capture_linux` keeps its file-level blanket: `v4l2-sys-mit` runs bindgen, so
that backend cannot be cross-linted from macOS and narrowing it would be blind
work. Tracked separately.

Verified on all three lint lanes: macOS native, x86_64-pc-windows-gnu, and
aarch64-unknown-linux-musl (everything but the GPUI crates and the v4l2
backend, neither of which cross-compiles).
`.claude/rules/rust.md` still told agents to hand-write an
`#[allow(clippy::expect_used)]` on every test module — now `clippy.toml`'s job —
and still described `openlogi-hidpp` as sitting outside the workspace lint
table, which its own AGENTS.md retired some time ago.

Replaces both, and adds the rule the sweep produced: `expect` by default, with
the three cases where it provably cannot work (cfg-conditional lints,
fulfilment differing between a crate's lib and test targets, and lints raised
inside a macro expansion), plus the scoping rule for cast suppressions and the
mechanical recipe for re-running the sweep across all three lint lanes.

Logged in `docs/DECISIONS.md` with the numbers behind it.
Several of the new suppression rationales argued the case instead of stating
it. A `reason` earns its keep by naming the bound; the surrounding prose was
restating what the attribute already says.
`allow_attributes` and `allow_attributes_without_reason` turn the two rules the
sweep just wrote down into build failures instead of review notes: reach for
`expect`, and say why.

Adopting them cost three annotated exceptions and nothing else — the sweep had
already left the tree compliant, and `allow_attributes_without_reason` found
zero violations on any lane. The three sites where `expect` provably cannot
work keep their `allow` behind an `#[expect(clippy::allow_attributes)]`, which
is a better home for the "why not expect" note than a bare comment: it fails
the build if the exception ever stops being one.

Two useful things fell out:

- The first pass ruled that a `cfg_attr`-wrapped suppression always needs
  `allow`. Two of the four turned out to work fine as `expect` — the rule was
  too coarse, and the lint is what surfaced it. `state.rs` and the permissions
  page are now `expect`.
- `allow_attributes` only sees outer `#[allow]`. A module-wide `#![allow(…)]`
  is invisible to it — precisely the shape that had rotted in
  `openlogi-assets`. Recorded in the lint table and in the rules so nobody
  reads a green build as full coverage.
@davidbudnick
davidbudnick force-pushed the chore/prune-clippy-suppressions branch from 5961e89 to d3452c4 Compare August 20, 2026 20:10
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown

Too many files changed for review (118 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@davidbudnick davidbudnick added this to the 0.7.4 milestone Aug 20, 2026
@AprilNEA
AprilNEA merged commit 4cc8e55 into master Aug 21, 2026
19 checks passed
@AprilNEA
AprilNEA deleted the chore/prune-clippy-suppressions branch August 21, 2026 05:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI, automation, and release workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants