diff --git a/crates/a11y/src/alt.rs b/crates/a11y/src/alt.rs index 5b13ee86..653a3a5a 100644 --- a/crates/a11y/src/alt.rs +++ b/crates/a11y/src/alt.rs @@ -141,7 +141,7 @@ fn content_bbox(doc: &Document, page: &Dict, ids: &HashSet) -> Option<[f64; let mut path: Vec<(f64, f64)> = Vec::new(); for op in printcraft_content::parse(&page_content(doc, page)).ops { let inside = marks.iter().any(|m| *m); - let top = *ctm.last().expect("never empty"); + let top = ctm.last().copied().unwrap_or(Matrix([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])); match op.op.as_slice() { b"q" => ctm.push(top), b"Q" => { @@ -150,8 +150,10 @@ fn content_bbox(doc: &Document, page: &Dict, ids: &HashSet) -> Option<[f64; } } b"cm" => { - if let Some(m) = Matrix::from_operands(&op.operands) { - *ctm.last_mut().expect("never empty") = m.then(&top); + if let Some(m) = Matrix::from_operands(&op.operands) + && let Some(t) = ctm.last_mut() + { + *t = m.then(&top); } } b"BMC" => marks.push(false), diff --git a/crates/a11y/src/lib.rs b/crates/a11y/src/lib.rs index 51cd220f..281fa1c5 100644 --- a/crates/a11y/src/lib.rs +++ b/crates/a11y/src/lib.rs @@ -6,6 +6,8 @@ //! accessibility report. Rules read the document only; the fixes that need edits (language, //! title, tab order) are applied by the engine. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + use std::collections::BTreeSet; use printcraft_cos::Document; diff --git a/crates/filters/src/flate.rs b/crates/filters/src/flate.rs index 7bb8f53f..5aa6443f 100644 --- a/crates/filters/src/flate.rs +++ b/crates/filters/src/flate.rs @@ -67,9 +67,10 @@ fn inflate_raw(input: &[u8], max: usize) -> Step { } /// zlib at level 6. +// The documented never-crash exception: writing into a `Vec` cannot fail. +#[allow(clippy::expect_used)] pub(crate) fn encode(data: &[u8]) -> Vec { let mut e = flate2::write::ZlibEncoder::new(Vec::with_capacity(data.len() / 2 + 64), Compression::new(6)); - // Writing into a Vec cannot fail. e.write_all(data).expect("in-memory write"); e.finish().expect("in-memory write") } diff --git a/crates/filters/src/lib.rs b/crates/filters/src/lib.rs index ce91500e..6ec4a5de 100644 --- a/crates/filters/src/lib.rs +++ b/crates/filters/src/lib.rs @@ -11,6 +11,7 @@ //! Every decoder is bounded by a caller-supplied `max_output` (decompression-bomb //! defence) and never panics on malformed input. #![forbid(unsafe_code)] +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] mod ascii85; mod asciihex; diff --git a/crates/fonts/src/lib.rs b/crates/fonts/src/lib.rs index e9f07157..0d72c01c 100644 --- a/crates/fonts/src/lib.rs +++ b/crates/fonts/src/lib.rs @@ -3,6 +3,8 @@ //! See the README: the metrics are approximations by character class (no vendor metrics files //! are bundled). The full font subsystem lands in M2.2/M7. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + mod encodings; pub mod pdf; mod script; diff --git a/crates/fonts/src/pdf.rs b/crates/fonts/src/pdf.rs index 6c67b35d..a242f97d 100644 --- a/crates/fonts/src/pdf.rs +++ b/crates/fonts/src/pdf.rs @@ -138,7 +138,7 @@ impl Metrics { ascent: 0.9, descent: -0.25, composite: false, - unicode: (32..127u32).map(|c| (c, char::from_u32(c).expect("ascii").to_string())).collect(), + unicode: (32..127u8).map(|c| (u32::from(c), char::from(c).to_string())).collect(), base_font: "Helvetica".into(), subset: false, code_len: 1, diff --git a/crates/js/src/lib.rs b/crates/js/src/lib.rs index 33e182d2..647f80f1 100644 --- a/crates/js/src/lib.rs +++ b/crates/js/src/lib.rs @@ -21,6 +21,8 @@ //! are bounded. Side effects (alerts, field changes, resets, printing, navigation) are returned //! in an [`Outcome`] for the caller to apply. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + use std::cell::RefCell; use std::collections::BTreeSet; @@ -200,8 +202,9 @@ struct Host { type Shared = RefCell; -fn host(ctx: &Context) -> &Shared { - ctx.get_data::().expect("host state is installed before scripts run") +/// The host state, installed before scripts run. +fn host(ctx: &Context) -> JsResult<&Shared> { + ctx.get_data::().ok_or_else(|| error("the script host is not set up")) } fn s(v: &str) -> JsValue { @@ -267,17 +270,17 @@ fn colour(c: &Option>, ctx: &mut Context) -> JsValue { fn field_index(ctx: &Context, name: &JsString) -> JsResult { let n = name.to_std_string_escaped(); - host(ctx).borrow().fields.iter().position(|f| f.name == n).ok_or_else(|| error(format!("no field named {n}"))) + host(ctx)?.borrow().fields.iter().position(|f| f.name == n).ok_or_else(|| error(format!("no field named {n}"))) } fn read(ctx: &Context, name: &JsString) -> JsResult { let i = field_index(ctx, name)?; - Ok(host(ctx).borrow().fields[i].clone()) + Ok(host(ctx)?.borrow().fields[i].clone()) } fn write(ctx: &Context, name: &JsString, change: impl FnOnce(&mut FieldState)) -> JsResult<()> { let i = field_index(ctx, name)?; - let mut h = host(ctx).borrow_mut(); + let mut h = host(ctx)?.borrow_mut(); change(&mut h.fields[i]); h.changed.insert(i); Ok(()) @@ -428,7 +431,7 @@ fn field_object(ctx: &mut Context, name: &str) -> JsObject { Ok(JsValue::undefined()) } fn set_focus(_: &JsValue, _: &[JsValue], name: &JsString, c: &mut Context) -> JsResult { - host(c).borrow_mut().requests.push(Request::Focus(name.to_std_string_escaped())); + host(c)?.borrow_mut().requests.push(Request::Focus(name.to_std_string_escaped())); Ok(JsValue::undefined()) } @@ -464,30 +467,30 @@ fn field_object(ctx: &mut Context, name: &str) -> JsObject { fn get_field(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let name = text(&arg(args, 0), ctx)?; - let exists = host(ctx).borrow().fields.iter().any(|f| f.name == name); + let exists = host(ctx)?.borrow().fields.iter().any(|f| f.name == name); if exists { return Ok(field_object(ctx, &name).into()); } // A parent name (e.g. "total" for "total.0"): its first kid stands in, as for groups. let prefix = format!("{name}."); - let kid = host(ctx).borrow().fields.iter().find(|f| f.name.starts_with(&prefix)).map(|f| f.name.clone()); + let kid = host(ctx)?.borrow().fields.iter().find(|f| f.name.starts_with(&prefix)).map(|f| f.name.clone()); Ok(kid.map_or(JsValue::null(), |k| field_object(ctx, &k).into())) } fn get_nth_field_name(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let i = arg(args, 0).to_number(ctx)?; - let name = host(ctx).borrow().fields.get(i.max(0.0) as usize).map(|f| f.name.clone()); + let name = host(ctx)?.borrow().fields.get(i.max(0.0) as usize).map(|f| f.name.clone()); Ok(name.map_or(JsValue::null(), |n| s(&n))) } fn reset_form(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let names = strings(&arg(args, 0), ctx)?; - host(ctx).borrow_mut().requests.push(Request::Reset(names)); + host(ctx)?.borrow_mut().requests.push(Request::Reset(names)); Ok(JsValue::undefined()) } fn print(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult { - host(ctx).borrow_mut().requests.push(Request::Print); + host(ctx)?.borrow_mut().requests.push(Request::Print); Ok(JsValue::undefined()) } @@ -500,7 +503,7 @@ fn submit_form(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult text(&a, ctx)?, }; - host(ctx).borrow_mut().requests.push(Request::Submit(url)); + host(ctx)?.borrow_mut().requests.push(Request::Submit(url)); Ok(JsValue::undefined()) } @@ -509,16 +512,16 @@ fn noop(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { } fn num_fields(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult { - Ok(JsValue::from(host(ctx).borrow().fields.len() as f64)) + Ok(JsValue::from(host(ctx)?.borrow().fields.len() as f64)) } fn page_num_get(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult { - Ok(JsValue::from(host(ctx).borrow().doc.page as f64)) + Ok(JsValue::from(host(ctx)?.borrow().doc.page as f64)) } fn page_num_set(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let n = arg(args, 0).to_number(ctx)?; - let mut h = host(ctx).borrow_mut(); + let mut h = host(ctx)?.borrow_mut(); let last = h.doc.num_pages.saturating_sub(1); let p = (n.max(0.0) as usize).min(last); h.doc.page = p; @@ -541,19 +544,19 @@ fn message(v: &JsValue, key: &str, ctx: &mut Context) -> JsResult { fn alert(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let m = message(&arg(args, 0), "cMsg", ctx)?; - host(ctx).borrow_mut().alerts.push(m); + host(ctx)?.borrow_mut().alerts.push(m); // The OK button. Ok(JsValue::from(1)) } fn beep(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult { - host(ctx).borrow_mut().requests.push(Request::Beep); + host(ctx)?.borrow_mut().requests.push(Request::Beep); Ok(JsValue::undefined()) } fn launch_url(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let u = message(&arg(args, 0), "cURL", ctx)?; - host(ctx).borrow_mut().requests.push(Request::LaunchUrl(u)); + host(ctx)?.borrow_mut().requests.push(Request::LaunchUrl(u)); Ok(JsValue::undefined()) } @@ -564,12 +567,12 @@ fn response(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { fn println(_: &JsValue, args: &[JsValue], ctx: &mut Context) -> JsResult { let m = text(&arg(args, 0), ctx)?; - host(ctx).borrow_mut().console.push(m); + host(ctx)?.borrow_mut().console.push(m); Ok(JsValue::undefined()) } fn console_clear(_: &JsValue, _: &[JsValue], ctx: &mut Context) -> JsResult { - host(ctx).borrow_mut().console.clear(); + host(ctx)?.borrow_mut().console.clear(); Ok(JsValue::undefined()) } @@ -889,7 +892,7 @@ fn install(ctx: &mut Context) -> JsResult<()> { let pg = function(ctx, NativeFunction::from_fn_ptr(page_num_get)); let ps = function(ctx, NativeFunction::from_fn_ptr(page_num_set)); let (file, pages, info) = { - let h = host(ctx).borrow(); + let h = host(ctx)?.borrow(); (h.doc.file_name.clone(), h.doc.num_pages, h.doc.info.clone()) }; let mut io = ObjectInitializer::new(ctx); diff --git a/crates/optimize/src/images.rs b/crates/optimize/src/images.rs index 7b6a275f..1b224124 100644 --- a/crates/optimize/src/images.rs +++ b/crates/optimize/src/images.rs @@ -50,7 +50,7 @@ fn walk(doc: &Document, data: &[u8], resources: &Dict, ctm: Matrix, depth: usize let xobjects = resources.get(b"XObject").map(|x| doc.resolve(x)).and_then(|x| x.as_dict().cloned()).unwrap_or_default(); let mut stack = vec![ctm]; for op in printcraft_content::parse(data).ops { - let top = *stack.last().expect("never empty"); + let top = stack.last().copied().unwrap_or(ctm); match op.op.as_slice() { b"q" => stack.push(top), b"Q" => { @@ -59,8 +59,10 @@ fn walk(doc: &Document, data: &[u8], resources: &Dict, ctm: Matrix, depth: usize } } b"cm" => { - if let Some(m) = Matrix::from_operands(&op.operands) { - *stack.last_mut().expect("never empty") = m.then(&top); + if let Some(m) = Matrix::from_operands(&op.operands) + && let Some(t) = stack.last_mut() + { + *t = m.then(&top); } } b"Do" => { diff --git a/crates/optimize/src/lib.rs b/crates/optimize/src/lib.rs index 85e189df..3b0607ca 100644 --- a/crates/optimize/src/lib.rs +++ b/crates/optimize/src/lib.rs @@ -15,6 +15,8 @@ //! fields are discarded by `printcraft-redact`'s Remove Hidden Information, which the engine //! runs alongside. +#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] + mod audit; mod images; mod links;