Skip to main content

max / audiofiles

lint: deny clippy::unwrap_used in the app harness and plugin sandbox First beachhead for the deferred "deny(unwrap_used) on non-test cfg" hardening item. Enables #![cfg_attr(not(test), deny(clippy::unwrap_used))] on two crates where a stray panic is most costly: audiofiles-app (updater, license, activation, audio/MIDI setup) and audiofiles-rhai (the plugin sandbox host). Both are now clean under the lint: - manifest.rs: replace a chars().count()==1 check + .expect() with a single match on the first two chars (no unwrap/expect, same behavior). - types.rs: the getters generated by #[export_module] expand to unwraps we don't control, so allow the lint on those two modules with a note. Scope is unwrap_used only (the finding's ask); documented-invariant and fatal-startup .expect()s (tokio runtime, exact-size tray icon) stay. Remaining crates (browser ~36, sync, core) are follow-on increments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-11 02:18 UTC
Commit: 6f1d089027134f4ded2e346a649cd070bdcbb6d2
Parent: 8493426
4 files changed, +26 insertions, -11 deletions
@@ -16,6 +16,12 @@
16 16 //! (NSPasteboardItem on macOS, OLE on Windows). A webview can't initiate OS-level drags
17 17 //! with file promises.
18 18
19 + // The app harness (updater, license, activation, audio/MIDI setup) is
20 + // security- and stability-sensitive; a stray unwrap here crashes the app.
21 + // Forbid bare unwrap in production code (tests opt out). `expect` with a
22 + // justification message stays allowed for documented invariants / fatal startup.
23 + #![cfg_attr(not(test), deny(clippy::unwrap_used))]
24 +
19 25 mod activation;
20 26 mod audio;
21 27 mod license;
@@ -23,6 +23,11 @@
23 23 //! - `registry` — plugin storage and lookup
24 24 //! - `bundled` — embedded bundled plugin manifests
25 25
26 + // Panics in the plugin-sandbox host turn a bad manifest/script into a crash, so
27 + // forbid bare unwrap in production code. Test code and `#[export_module]`-
28 + // generated getters (see `types.rs`) opt out locally.
29 + #![cfg_attr(not(test), deny(clippy::unwrap_used))]
30 +
26 31 pub mod bundled;
27 32 pub mod engine;
28 33 pub mod error;
@@ -140,17 +140,16 @@ fn parse_naming(section: &NamingSection) -> Result<NamingRules, PluginError> {
140 140 }
141 141 };
142 142
143 - if section.separator.chars().count() != 1 {
144 - return Err(PluginError::ManifestInvalid(format!(
145 - "separator must be exactly one character, got {:?}",
146 - section.separator,
147 - )));
148 - }
149 - let separator = section
150 - .separator
151 - .chars()
152 - .next()
153 - .expect("separator length checked to be exactly 1 above");
143 + let mut separator_chars = section.separator.chars();
144 + let separator = match (separator_chars.next(), separator_chars.next()) {
145 + (Some(c), None) => c,
146 + _ => {
147 + return Err(PluginError::ManifestInvalid(format!(
148 + "separator must be exactly one character, got {:?}",
149 + section.separator,
150 + )));
151 + }
152 + };
154 153
155 154 Ok(NamingRules {
156 155 case,
@@ -45,6 +45,9 @@ pub struct RhaiExportContext {
45 45 }
46 46
47 47 /// Rhai module exposing `RhaiSampleInfo` getters.
48 + // The unwraps in this block are generated by `#[export_module]`, not written
49 + // here, so the crate-level unwrap_used/expect_used deny can't apply to them.
50 + #[allow(clippy::unwrap_used, clippy::expect_used)]
48 51 #[export_module]
49 52 pub mod sample_info_module {
50 53 use super::RhaiSampleInfo;
@@ -91,6 +94,8 @@ pub mod sample_info_module {
91 94 }
92 95
93 96 /// Rhai module exposing `RhaiExportContext` getters.
97 + // Unwraps below are `#[export_module]`-generated (see note above).
98 + #[allow(clippy::unwrap_used, clippy::expect_used)]
94 99 #[export_module]
95 100 pub mod export_context_module {
96 101 use super::RhaiExportContext;