From f1b99fdaa0a0fe6d38ebf7ee04c0257629593cd6 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:25:13 +0100 Subject: [PATCH] fix(lint): clear the clippy backlog the revived gate exposed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second wave of fallout from reviving Rust CI. With `clippy --all-targets -- -D warnings` running for the first time, the workspace failed crate after crate. All fixed here; `cargo clippy --all-targets -- -D warnings` now exits 0. Mechanical, behaviour-preserving: - `derivable_impls` — `impl Default for Visibility` -> `#[derive(Default)]` + `#[default]` - `manual_try_fold` (x2) — `.fold(Ok(acc), ..)` -> `.try_fold(acc, ..)` in both the surface and core parsers' statement-sequence desugaring - `collapsible_if` (x2), `match_like_matches_macro` — via `cargo clippy --fix` - `ptr_arg` — `&PathBuf` -> `&Path` in two ephapax-cli signatures - `unnecessary_unsafe` — removed an EMPTY `unsafe {}` block that wrapped only comments in a placeholder test (`ephapax-runtime::list`). Worth noting in a language whose thesis is memory safety. Deliberately allowed, each with an inline reason rather than a blanket crate-level allow: - `approx_constant` — `lex("3.14")` is a float literal UNDER TEST, not an approximation of PI - `type_complexity` — introduced a `TokenPredicate` type alias instead - `too_many_arguments` (x3) — 8 params against clippy's default threshold of 7, on option-heavy CLI entry points and the recursive import resolver - `large_enum_variant` — `Value::Closure` is ~264 bytes vs ~48 for the next largest, so every `Value` pays for it. The fix is to box the payload, which touches every construction and match site in the INTERPRETER. That is a behavioural-risk refactor and does not belong inside a CI-infrastructure change, so it is allowed here with a comment and tracked in #360. Verification, all local and all green: cargo check --locked --all-targets ok cargo clippy --all-targets -- -D warnings exit 0 cargo fmt --all -- --check clean cargo test --workspace 285 passed, 0 failed Co-Authored-By: Claude Opus 5 --- ephapax-linear/src/tests.rs | 10 ++--- src/ephapax-cli/src/import_resolver.rs | 1 + src/ephapax-cli/src/main.rs | 6 ++- src/ephapax-cli/tests/integration.rs | 5 +-- src/ephapax-desugar/src/lib.rs | 7 +--- src/ephapax-interp/src/lib.rs | 7 ++++ src/ephapax-lexer/src/lib.rs | 7 +++- src/ephapax-lsp/src/main.rs | 8 +--- src/ephapax-parser/src/lib.rs | 54 +++++++++++--------------- src/ephapax-parser/src/surface.rs | 9 ++--- src/ephapax-query/src/lib.rs | 2 +- src/ephapax-runtime/src/list.rs | 7 +--- src/ephapax-syntax/src/lib.rs | 11 ++---- src/ephapax-wasm/src/lib.rs | 14 +------ src/ephapax-wasm/src/ownership.rs | 4 +- 15 files changed, 65 insertions(+), 87 deletions(-) diff --git a/ephapax-linear/src/tests.rs b/ephapax-linear/src/tests.rs index 79f35f3e..55b0c4fe 100644 --- a/ephapax-linear/src/tests.rs +++ b/ephapax-linear/src/tests.rs @@ -383,7 +383,7 @@ fn p2p_single_consume_always_accepted_both_disciplines() { let expr = e(ExprKind::LetLin { name: name.clone().into(), ty: Some(Ty::Base(BaseTy::I32)), - value: Box::new(i32_lit(i as i32)), + value: Box::new(i32_lit(i)), body: Box::new(var(&name)), }); @@ -413,7 +413,7 @@ fn p2p_contraction_always_rejected_both_disciplines() { let expr = e(ExprKind::LetLin { name: name.clone().into(), ty: Some(Ty::Base(BaseTy::I32)), - value: Box::new(i32_lit(i as i32)), + value: Box::new(i32_lit(i)), body: Box::new(e(ExprKind::Pair { left: Box::new(var(&name)), right: Box::new(var(&name)), @@ -462,7 +462,7 @@ fn p2p_weakening_linear_forbidden_affine_allowed() { let expr = e(ExprKind::LetLin { name: name.clone().into(), ty: Some(Ty::Base(BaseTy::I32)), - value: Box::new(i32_lit(i as i32)), + value: Box::new(i32_lit(i)), body: Box::new(unit()), }); @@ -485,7 +485,7 @@ fn p2p_weakening_linear_forbidden_affine_allowed() { let affine_expr = e(ExprKind::Let { name: name.clone().into(), ty: Some(Ty::Base(BaseTy::I32)), - value: Box::new(i32_lit(i as i32)), + value: Box::new(i32_lit(i)), body: Box::new(unit()), }); let mut affine = AffineChecker::new(); @@ -510,7 +510,7 @@ fn p2p_unified_api_consistent_with_direct_checkers() { let good_expr = e(ExprKind::LetLin { name: name.clone().into(), ty: Some(Ty::Base(BaseTy::I32)), - value: Box::new(i32_lit(i as i32)), + value: Box::new(i32_lit(i)), body: Box::new(var(&name)), }); diff --git a/src/ephapax-cli/src/import_resolver.rs b/src/ephapax-cli/src/import_resolver.rs index 60c1f6b5..4a90bc4e 100644 --- a/src/ephapax-cli/src/import_resolver.rs +++ b/src/ephapax-cli/src/import_resolver.rs @@ -162,6 +162,7 @@ fn first_module_declaration(source: &str) -> Option { None } +#[allow(clippy::too_many_arguments)] // recursive resolver: 8 params vs the 7 default threshold fn visit( logical: &str, explicit_file: Option<&Path>, diff --git a/src/ephapax-cli/src/main.rs b/src/ephapax-cli/src/main.rs index 70bdddd8..2e471a79 100644 --- a/src/ephapax-cli/src/main.rs +++ b/src/ephapax-cli/src/main.rs @@ -419,8 +419,9 @@ fn check_files(files: &[PathBuf], _mode_str: &str, verbose: bool) -> Result<(), } } +#[allow(clippy::too_many_arguments)] // CLI entry point: 8 options vs the 7 default threshold fn compile_file( - path: &PathBuf, + path: &Path, output: Option, opt_level: u8, debug: bool, @@ -570,10 +571,11 @@ fn compile_file( /// Codegen + optional ownership verification + output write. Shared by /// the multi-module and legacy single-file compile paths. +#[allow(clippy::too_many_arguments)] // CLI entry point: 8 options vs the 7 default threshold fn finish_compile( module: ephapax_syntax::Module, filename: &str, - path: &PathBuf, + path: &Path, output: Option, opt_level: u8, debug: bool, diff --git a/src/ephapax-cli/tests/integration.rs b/src/ephapax-cli/tests/integration.rs index 9434b90d..871ce0d0 100644 --- a/src/ephapax-cli/tests/integration.rs +++ b/src/ephapax-cli/tests/integration.rs @@ -32,9 +32,8 @@ fn assert_checks(source: &str) { /// Helper: assert that source fails type-checking fn assert_rejects(source: &str) { - match check(source) { - Ok(ty) => panic!("Expected type-check failure for `{source}`, got: {ty:?}"), - Err(_) => {} + if let Ok(ty) = check(source) { + panic!("Expected type-check failure for `{source}`, got: {ty:?}") } } diff --git a/src/ephapax-desugar/src/lib.rs b/src/ephapax-desugar/src/lib.rs index 41117a4d..a18806ff 100644 --- a/src/ephapax-desugar/src/lib.rs +++ b/src/ephapax-desugar/src/lib.rs @@ -87,10 +87,7 @@ fn module_qualifier_of(base: &SurfaceExpr) -> Option { /// (`lib/math` → `math`, `Foo.Bar` → `Bar`, `Coproc` → `Coproc`). This is /// the name a `M.member` qualifier must use. fn last_segment(path: &str) -> SmolStr { - path.rsplit(|c| c == '/' || c == '.') - .next() - .unwrap_or(path) - .into() + path.rsplit(['/', '.']).next().unwrap_or(path).into() } /// Desugaring errors. @@ -2014,7 +2011,7 @@ mod tests { assert_eq!(params[0].0.as_str(), "title"); assert_eq!(*ret_ty, Ty::Base(BaseTy::I32)); } else { - panic!("expected ExternItem::Fn, got {:?}", &items[1]); + panic!("expected ExternItem::Fn, got {:?}", items[1]); } } other => panic!("expected Decl::Extern, got {other:?}"), diff --git a/src/ephapax-interp/src/lib.rs b/src/ephapax-interp/src/lib.rs index 7c4e8457..09214f8d 100644 --- a/src/ephapax-interp/src/lib.rs +++ b/src/ephapax-interp/src/lib.rs @@ -106,6 +106,13 @@ pub enum RuntimeError { } /// Runtime values +// +// clippy::large_enum_variant — `Closure` is ~264 bytes against ~48 for the next +// largest variant. The fix is to box the closure payload, but that changes every +// construction and match site in the interpreter, so it is deliberately NOT bundled +// into the CI-revival change that first surfaced this lint. Tracked separately; +// remove this allow when the boxing lands. +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone)] pub enum Value { Unit, diff --git a/src/ephapax-lexer/src/lib.rs b/src/ephapax-lexer/src/lib.rs index ff795bcd..9181bf91 100644 --- a/src/ephapax-lexer/src/lib.rs +++ b/src/ephapax-lexer/src/lib.rs @@ -584,6 +584,8 @@ mod tests { assert_eq!(lex("12345"), vec![TokenKind::Integer(12345)]); } + // `3.14` here is a float literal under test, not an approximation of PI. + #[allow(clippy::approx_constant)] #[test] fn test_floats() { assert_eq!(lex("3.14"), vec![TokenKind::Float(3.14)]); @@ -785,6 +787,9 @@ mod tests { mod property_tests { use super::*; + /// Predicate over a token kind, used by the table-driven P2P tests. + type TokenPredicate = fn(&TokenKind) -> bool; + // ------------------------------------------------------------------------- // P2P: round-trip token spans // ------------------------------------------------------------------------- @@ -891,7 +896,7 @@ mod property_tests { /// exactly one content token of the Integer kind. #[test] fn p2p_integer_literals_tokenise_to_one_token() { - let inputs: Vec<(&str, fn(&TokenKind) -> bool)> = vec![ + let inputs: Vec<(&str, TokenPredicate)> = vec![ ("0", |t| matches!(t, TokenKind::Integer(_))), ("1", |t| matches!(t, TokenKind::Integer(_))), ("42", |t| matches!(t, TokenKind::Integer(_))), diff --git a/src/ephapax-lsp/src/main.rs b/src/ephapax-lsp/src/main.rs index 0169cd26..a620346f 100644 --- a/src/ephapax-lsp/src/main.rs +++ b/src/ephapax-lsp/src/main.rs @@ -173,11 +173,7 @@ impl Backend { if let Some(module) = &state.module { for d in &module.decls { if let Decl::Fn { - name, - body, - params, - type_params: _, - .. + name, body, params, .. } = d { // Check if word matches a parameter @@ -572,7 +568,7 @@ fn extract_declarations(module: &Module, _source: &str) -> Vec { .unwrap_or_default() ), params: Vec::new(), - return_type: ty.as_ref().map(|t| format_ty(t)), + return_type: ty.as_ref().map(format_ty), }), Decl::Extern { abi, items } => { for item in items { diff --git a/src/ephapax-parser/src/lib.rs b/src/ephapax-parser/src/lib.rs index db7f9b1b..84bb75a2 100644 --- a/src/ephapax-parser/src/lib.rs +++ b/src/ephapax-parser/src/lib.rs @@ -287,10 +287,8 @@ fn parse_extern_item(pair: pest::iterators::Pair) -> Result { - if ret_ty.is_none() { - ret_ty = Some(parse_type(sub)?); - } + Rule::ty if ret_ty.is_none() => { + ret_ty = Some(parse_type(sub)?); } _ => {} } @@ -772,8 +770,7 @@ fn parse_seq_expr_core(pair: pest::iterators::Pair) -> Result) -> Result { for param_item in item.into_inner() { - match param_item.as_rule() { - Rule::handler_param => { - let hp_inner = param_item.into_inner().next(); - if let Some(hp) = hp_inner { - match hp.as_rule() { - Rule::resume_param => { - // Check for mode - if let Some(mode_pair) = hp.into_inner().next() - { - resume_mode = - Some(match mode_pair.as_str() { - "once" => ResumeMode::Once, - "multi" => ResumeMode::Multi, - _ => ResumeMode::Once, - }); - } else { - resume_mode = Some(ResumeMode::Once); - // default - } - } - Rule::identifier => { - params.push(parse_identifier(hp)); + if param_item.as_rule() == Rule::handler_param { + let hp_inner = param_item.into_inner().next(); + if let Some(hp) = hp_inner { + match hp.as_rule() { + Rule::resume_param => { + // Check for mode + if let Some(mode_pair) = hp.into_inner().next() { + resume_mode = Some(match mode_pair.as_str() { + "once" => ResumeMode::Once, + "multi" => ResumeMode::Multi, + _ => ResumeMode::Once, + }); + } else { + resume_mode = Some(ResumeMode::Once); + // default } - _ => {} } + Rule::identifier => { + params.push(parse_identifier(hp)); + } + _ => {} } } - _ => {} } } } @@ -2359,7 +2351,7 @@ mod tests { assert_eq!(name.as_str(), "window_open"); assert_eq!(params.len(), 2); } else { - panic!("expected ExternItem::Fn, got {:?}", &items[1]); + panic!("expected ExternItem::Fn, got {:?}", items[1]); } } other => panic!("expected Decl::Extern, got {other:?}"), diff --git a/src/ephapax-parser/src/surface.rs b/src/ephapax-parser/src/surface.rs index 910e8086..be210923 100644 --- a/src/ephapax-parser/src/surface.rs +++ b/src/ephapax-parser/src/surface.rs @@ -201,10 +201,8 @@ fn parse_extern_item(pair: pest::iterators::Pair) -> Result { - if ret_ty.is_none() { - ret_ty = Some(parse_type(sub)?); - } + Rule::ty if ret_ty.is_none() => { + ret_ty = Some(parse_type(sub)?); } _ => {} } @@ -507,8 +505,7 @@ fn parse_seq_expr(pair: pest::iterators::Pair) -> Result Self { - Visibility::Private - } -} - /// Helper for serde skip_serializing_if. fn is_private(v: &Visibility) -> bool { *v == Visibility::Private @@ -797,7 +792,7 @@ mod tests { BaseTy::F64, ] { assert!( - !Ty::Base(base.clone()).is_linear(), + !Ty::Base(base).is_linear(), "Coq bridge: TBase {base:?} should be NOT linear" ); } diff --git a/src/ephapax-wasm/src/lib.rs b/src/ephapax-wasm/src/lib.rs index c9a0de87..66a35ec2 100644 --- a/src/ephapax-wasm/src/lib.rs +++ b/src/ephapax-wasm/src/lib.rs @@ -770,7 +770,6 @@ impl Codegen { name, params, ret_ty, - type_params: _, .. } => { // Build WASM type for this function @@ -1098,11 +1097,7 @@ impl Codegen { for decl in &ast.decls { match decl { Decl::Fn { - name, - params, - body, - type_params: _, - .. + name, params, body, .. } => { let info = self.user_fns.get(name.as_str()).cloned().ok_or_else(|| { CodegenError(format!("BUG: function `{}` not collected", name)) @@ -1271,12 +1266,7 @@ impl Codegen { // Export all user functions by name for decl in &ast.decls { - if let Decl::Fn { - name, - type_params: _, - .. - } = decl - { + if let Decl::Fn { name, .. } = decl { if let Some(info) = self.user_fns.get(name.as_str()) { exports.export(name.as_str(), ExportKind::Func, info.wasm_fn_idx); } diff --git a/src/ephapax-wasm/src/ownership.rs b/src/ephapax-wasm/src/ownership.rs index 977b8ee6..1aec7660 100644 --- a/src/ephapax-wasm/src/ownership.rs +++ b/src/ephapax-wasm/src/ownership.rs @@ -185,7 +185,7 @@ mod tests { #[test] fn single_linear_param_roundtrip() { let e = entry(7, vec![OwnershipKind::Linear], OwnershipKind::Unrestricted); - let bytes = build_ownership_section_payload(&[e.clone()]); + let bytes = build_ownership_section_payload(std::slice::from_ref(&e)); assert_eq!(parse_ownership_section_payload(&bytes), vec![e]); } @@ -201,7 +201,7 @@ mod tests { ], OwnershipKind::Linear, ); - let bytes = build_ownership_section_payload(&[e.clone()]); + let bytes = build_ownership_section_payload(std::slice::from_ref(&e)); assert_eq!(parse_ownership_section_payload(&bytes), vec![e]); }