Sub-task of #1412.
Problem
Two clusters of structural duplication in the compiler frontend:
1. `lower/items.rs:9-100` — 8 match arms with identical shape
In `lower_item()`, eight match arms (Import, ReExport, TypeDecl, Const, Function, ForBlock, ImplBlock, TestBlock) all do:
```rust
SyntaxKind::FOO_NODE => {
let decl = self.lower_foo(&child);
Item { kind: ItemKind::Foo(decl), span }
}
```
Adding a new top-level item means copy-pasting this template a 9th time.
Fix: This pattern is a textbook case for a macro. `make_item! { Foo => lower_foo }`. Or, since macros add cognitive load, a `fn make_item(self, kind_constructor: fn(T) -> ItemKind, lowered: T, span: Span) -> Item` works too.
Also at lines 57-72: `FOR_BLOCK` and `IMPL_BLOCK` arms are near-identical (both check `exported` and wrap into a ForBlock variant). Merge.
2. `checker/expr.rs:1886-1948` — Ok/Err extraction logic duplicated
Lines 1886-1900 (extract Ok arg type) and 1918-1932 (extract Err arg type) are byte-identical `find_map` blocks. Lines 1901-1914 and 1933-1946 follow the same filter/and_then/or_else/unwrap_or pattern, swapping only the method names `result_ok` ↔ `result_err`.
Fix:
- `fn extract_first_positional_arg_type(&mut self, args: &[Arg]) -> Type` — used twice.
- Pass the type-projection method as a function pointer to a single helper.
Tests
- `cargo test -p floe-core --lib lower` and `--lib checker` pass (snapshots unchanged)
- `cargo clippy --profile ci --workspace -- -D warnings` zero errors
Sub-task of #1412.
Problem
Two clusters of structural duplication in the compiler frontend:
1. `lower/items.rs:9-100` — 8 match arms with identical shape
In `lower_item()`, eight match arms (Import, ReExport, TypeDecl, Const, Function, ForBlock, ImplBlock, TestBlock) all do:
```rust
SyntaxKind::FOO_NODE => {
let decl = self.lower_foo(&child);
Item { kind: ItemKind::Foo(decl), span }
}
```
Adding a new top-level item means copy-pasting this template a 9th time.
Fix: This pattern is a textbook case for a macro. `make_item! { Foo => lower_foo }`. Or, since macros add cognitive load, a `fn make_item(self, kind_constructor: fn(T) -> ItemKind, lowered: T, span: Span) -> Item` works too.
Also at lines 57-72: `FOR_BLOCK` and `IMPL_BLOCK` arms are near-identical (both check `exported` and wrap into a ForBlock variant). Merge.
2. `checker/expr.rs:1886-1948` — Ok/Err extraction logic duplicated
Lines 1886-1900 (extract Ok arg type) and 1918-1932 (extract Err arg type) are byte-identical `find_map` blocks. Lines 1901-1914 and 1933-1946 follow the same filter/and_then/or_else/unwrap_or pattern, swapping only the method names `result_ok` ↔ `result_err`.
Fix:
Tests