iter clippy-sweep: clear all 61 cargo clippy warnings

Hygiene sweep across the workspace under `cargo clippy --workspace
--all-targets`. Before: 61 warnings. After: zero. Tests stay 563
green, `cargo doc` stays at zero warnings, and all three bench
scripts exit 0 against existing baselines.

Twelve lint classes, three fix shapes:

- Documentation hygiene (~32 hits): `doc_lazy_continuation` resolved
  by adding blank lines before continuation paragraphs or rephrasing
  the offending line; plus four `empty_line_after_doc_comments` hits
  in workspace.rs where eight `///` blocks left orphan by form-a.1
  T5 test relocation were converted to plain `//`.
- Idiomatic refactors (~16 hits): `.err().expect()` → `.expect_err()`,
  redundant `.into_iter()` and `.into()` removed, `|f| g(f)` →
  `g`, `.trim().split_whitespace()` → `.split_whitespace()`,
  `push_str("x")` → `push('x')`, two manual `impl Default` flipped
  to `#[derive(Default)]` + `#[default]`, two nested `if let Some(_)
  = …` collapsed.
- Block extraction (1 hit): a 13-line block inside an `else if`
  condition in synth's Var-arm extracted to a new helper
  `is_class_method_dispatch(name, env)` next to
  `qualifier_is_class_shape`.

Three `#[allow]`s with inline rationale where clippy's suggestion
would lose meaning: `only_used_in_recursion` on walk_pattern (the
parameter preserves the five-fn walker-framework signature
uniformity), 2× `if_same_then_else` on qualify_local_types shapes
(two branches encode semantically distinct disqualification
reasons), `too_many_arguments` on `Emitter::new` (8 workspace-flat
tables; bundling would just rename boilerplate).
This commit is contained in:
2026-05-14 00:48:17 +02:00
parent 04258c5cc1
commit abcdd05991
19 changed files with 302 additions and 162 deletions
+25 -18
View File
@@ -2363,6 +2363,7 @@ pub(crate) fn parse_method_qualifier(name: &str) -> (&str, Option<String>) {
/// - bare-class qualifier, e.g. `"Show.show"` — class names are
/// PascalCase per repo convention (first character uppercase).
/// - module-qualified class qualifier, e.g. `"prelude.Show.show"`.
///
/// Rejects qualifier shapes that look like cross-module-fn calls
/// (`"std_list.length"`): qualifier starts with lowercase, no
/// inner dot — falls through to the qualified-fn arm.
@@ -2382,6 +2383,19 @@ pub(crate) fn qualifier_is_class_shape(qualifier_opt: &Option<String>) -> bool {
}
}
/// mq.2 dispatch entry predicate: the synth Var-arm runs this to
/// decide whether `name` should be routed through the class-method
/// dispatch helper. Matches a bare method name (e.g. `"show"`) or a
/// class-shaped qualifier (`"Show.show"`, `"prelude.Show.show"`).
/// A 1-dot lowercase name (`"std_list.length"`) is structurally a
/// cross-module-fn call per mq.1's canonical-form rule and falls
/// through to the qualified-fn arm.
fn is_class_method_dispatch(name: &str, env: &Env) -> bool {
let (method_name, qualifier_opt) = parse_method_qualifier(name);
qualifier_is_class_shape(&qualifier_opt)
&& env.method_to_candidate_classes.contains_key(method_name)
}
/// mq.tidy: predicate for the `class-method-shadowed-by-fn`
/// warning emission. The full spec rule (§"Class-fn collisions"
/// 161-162) requires a class candidate WITH AN INSTANCE FOR THE
@@ -2750,20 +2764,7 @@ pub(crate) fn synth(
// Bare name resolves through implicit import; the
// unqualified name in the owner module is the same `name`.
(qualified, Some((owner_module, name.clone())))
} else if {
// mq.2 dispatch entry: the Var arm matches a class
// method either bare (`"show"`) or qualified
// (`"prelude.Show.show"`). `parse_method_qualifier`
// splits at the last dot; a `Some(qualifier)` with no
// inner dot is a 1-dot name (`std_list.length`) —
// structurally a cross-module-fn call per mq.1's
// canonical-form rule (class qualifiers are always
// `<module>.<Class>`), so we fall through to the
// qualified-fn arm below in that case.
let (method_name, qualifier_opt) = parse_method_qualifier(name);
qualifier_is_class_shape(&qualifier_opt)
&& env.method_to_candidate_classes.contains_key(method_name)
} {
} else if is_class_method_dispatch(name, env) {
// mq.2: type-driven dispatch entry. The Var arm runs
// before App-arm unification, so the arg type is not
// available here. The dispatch helper resolves on
@@ -3456,6 +3457,11 @@ fn maybe_instantiate(t: Type, counter: &mut u32) -> Type {
fn qualify_local_types(t: &Type, owner_module: &str, local_types: &IndexMap<String, TypeDef>) -> Type {
match t {
Type::Con { name, args } => {
// The first two branches share the body `name.clone()` but
// express semantically distinct reasons (already qualified;
// primitive needs no qualification). Combining them with
// `||` would obscure why each disqualifies the name.
#[allow(clippy::if_same_then_else)]
let qualified_name = if name.contains('.') {
name.clone()
} else if ailang_core::primitives::is_primitive_name(name) {
@@ -3726,9 +3732,10 @@ pub struct Env {
/// mq.2: workspace-flat inverse of [`Self::class_methods`] — for
/// each method name, the set of qualified class names that declare
/// it. Pre-mq.3, the `MethodNameCollision` invariant guarantees
/// each set is singleton; mq.3 lifts the invariant and cardinality
/// > 1 becomes legal. Synth's `Term::Var` arm consults this index
/// for type-driven dispatch (spec §Architecture's 5-step rule).
/// each set is singleton; mq.3 lifts the invariant and any
/// cardinality above one becomes legal. Synth's `Term::Var` arm
/// consults this index for type-driven dispatch (spec
/// §Architecture's 5-step rule).
pub method_to_candidate_classes: BTreeMap<String, BTreeSet<String>>,
/// mq.3: post-superclass-expansion declared constraints of the
/// active fn body being checked. Populated by `check_fn` before
@@ -5512,7 +5519,7 @@ mod tests {
ret_mode: ParamMode::Implicit,
}),
},
vec!["x".into()],
vec!["x"],
Term::Let {
name: "z".into(),
value: Box::new(Term::Lit { lit: Literal::Int { value: 7 } }),
+1 -1
View File
@@ -198,7 +198,7 @@ pub fn lift_letrecs(m: &Module) -> Result<Module> {
}
}
out.defs.extend(lifter.lifted.into_iter());
out.defs.extend(lifter.lifted);
Ok(out)
}
+1 -1
View File
@@ -714,7 +714,7 @@ fn collect_pattern_binders(p: &Pattern) -> Vec<String> {
Pattern::Var { name } => vec![name.clone()],
Pattern::Ctor { fields, .. } => fields
.iter()
.flat_map(|f| collect_pattern_binders(f))
.flat_map(collect_pattern_binders)
.collect(),
}
}
+19 -19
View File
@@ -1025,25 +1025,25 @@ fn rewrite_mono_calls(
let is_poly_free_fn = !is_class_method && poly_free_fns.contains(name);
let should_advance = (is_class_method || is_poly_free_fn) && !locals.contains(name);
if should_advance {
if let Some(slot) = ordered_targets.get(*cursor) {
if let Some(t) = slot {
let (sym, defining_module) = match t {
MonoTarget::ClassMethod { method, type_, defining_module, .. } => {
(mono_symbol(method, type_), defining_module.as_str())
}
MonoTarget::FreeFn { name: fn_name, type_args, defining_module } => {
(mono_symbol_n(fn_name, type_args.as_slice()), defining_module.as_str())
}
};
let new_name = if defining_module == caller_module {
sym
} else {
format!("{}.{}", defining_module, sym)
};
*name = new_name;
}
// else: residual / observation was non-concrete →
// leave name unchanged.
// The outer `Some` means the cursor slot exists; the inner
// `Some` means the observation at that slot is concrete.
// A `Some(None)` slot is a residual / non-concrete
// observation and leaves the name unchanged.
if let Some(Some(t)) = ordered_targets.get(*cursor) {
let (sym, defining_module) = match t {
MonoTarget::ClassMethod { method, type_, defining_module, .. } => {
(mono_symbol(method, type_), defining_module.as_str())
}
MonoTarget::FreeFn { name: fn_name, type_args, defining_module } => {
(mono_symbol_n(fn_name, type_args.as_slice()), defining_module.as_str())
}
};
let new_name = if defining_module == caller_module {
sym
} else {
format!("{}.{}", defining_module, sym)
};
*name = new_name;
}
*cursor += 1;
}