Conversation
Sweeps a .cht file (or directory) through `clear fmt` and checks that the emitted Zig is byte-identical before and after formatting. Strategy: transpile original .cht → Zig (before), Formatter.format the source, transpile the formatted .cht → Zig (after), compare. Strips `// CLR:N` line-number debug markers before comparing — those shift when fmt rearranges source layout but carry no semantic value. Wires through `clear fmt --verify <path>...` for direct use against benchmarks/, examples/, transpile-tests/. Returns exit code = number of files where output diverged or transpilation raised, so it composes with shell scripts and pre-merge gates. Why now: about to apply fmt to benchmarks/sequential/ in bulk. The verifier is the safety net that catches "fmt subtly changed semantics" before it gets baked into a checked-in change. Already paid off — the first sweep flagged a real precedence bug in MethodRewriter that the followup commit fixes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Found by FmtVerifier on benchmarks/sequential/11_pipeline_overhead.cht: `toFloat(state MOD 1000)` was being rewritten to `state MOD 1000.toFloat()`, which Zig parses as `state MOD (1000.toFloat())` — integer mod silently became float mod. Real semantics change. Root cause: when MethodRewriter rewrites `f(arg)` to `arg.f()`, the first argument's source text was inlined verbatim. For an arg like `state MOD 1000` the result has different operator precedence than the original. Fix: inspect the AST shape of the first arg. If it's an Identifier, Literal, MethodCall, FuncCall, GetField, GetIndex, StructLit, ListLit, HashLit, or StringLit — i.e. a "tight" form that binds tighter than `.method()` — emit it bare. Otherwise wrap in parens. Also detect already-paren-wrapped source text and skip double-wrapping. Tests: 6 new specs in spec/method_rewriter_spec.rb cover the binary-op case, unary-op case, identifier (no wrap), method chain (no wrap), literal (no wrap), and the no-double-wrap case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Found while auditing fmt output on benchmarks/sequential/ before applying in bulk. The original canonicalize_comment rule was "exactly one space after #," which destroyed deliberate prose indentation in: - ASCII tables in comment blocks (header vs row indent) - Indented bullet/sub-bullet lists - Code samples inside comment blocks Old behavior: `# row 1 | x` became `# row 1 | x`, breaking column alignment with `# COL A | COL B` headers. New rule: ensure AT LEAST one space after `#`. Preserve any additional user-typed spaces. Synthesize the missing first space only when the user wrote `#text` with no separator. Trailing whitespace is still stripped; empty `#` still stays `#`. This matches gofmt / rustfmt's stance — they leave comment internals alone. User-typed indent inside a comment is meaningful; fmt shouldn't rewrite it. Tests: 4 new specs in spec/clear_fmt_spec.rb cover multi-space indent preservation, the `#text` -> `# text` case, an ASCII table roundtrip, and an empty `#` line preserved verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds four auto-fixes to `clear fmt` (driven by semantic analysis,
applied as a source-level pre-pass before the existing formatter)
plus one warn-only lint surfaced through `clear fix`.
1. Drop `MUTABLE` when the binding is read but never reassigned.
Annotator-driven via the existing `MUTABLE-never-reassigned`
finding (auto-confidence). LintFixRewriter drains the finding
and applies the edit.
2. Drop redundant `: Type` annotations like `s: Float64 = 0.0` ->
`s = 0.0` when the literal RHS already determines the type.
Walks the annotated AST, compares declared `type` against the
value's inferred `full_type`. Only triggers on bare types — any
decoration (sigil, capability, array, optional, error union,
generic instance) keeps the annotation, since those carry intent
the value alone may not express.
3. Generic-bracket attach: `Foo<T, U>` instead of `Foo < T , U >`.
Per-line pre-pass marks `<`/`>` tokens that are generic brackets
(TYPE_ID-prefixed open with a clean matching close); the spacing
logic then attaches them tightly without affecting comparison
operators.
4. Struct-literal brace attach + interior padding: `Foo{ field: v }`
instead of `Foo { field: v }` and instead of `Foo{field: v}`.
The TYPE_ID-`{`-pattern is detected and disambiguated from
STRUCT/UNION/ENUM body opens (which keep their leading space).
Empty `Foo{}` stays tight (no padding inserted).
5. (warning only, no auto-fix) Multiple statements on one line
(`a = 1; b = 2; c = 3;`) emits a `:lint` warning during
`clear fix`. Splitting these into separate lines is a layout
judgement call (indent / blank lines / inline comments) that
fmt shouldn't make automatically — surface the smell, let the
user decide.
Implementation:
- src/tools/lint_fix_rewriter.rb (new) — runs the annotator with
FixCollector enabled, drains findings, walks the AST for
type-annotation redundancy, applies all edits in one pass.
Falls back gracefully on annotation failure (fmt must still
format files with compile errors).
- src/tools/multi_statement_linter.rb (new) — tracks `;` at
top-level depth, ignoring strings/comments/bracketed contexts.
Wired into `clear fix` next to PredicateRewriter.lint!.
- src/tools/formatter.rb — calls LintFixRewriter as the first
rewriter in the format pipeline; adds generic-bracket and
struct-literal-brace pre-passes that compute per-line index
sets used by `needs_space?`.
Tests:
- spec/clear_fmt_spec.rb adds 12 specs covering all four rules
end-to-end through `Formatter.format`.
- spec/lint_fix_rewriter_spec.rb (new, 10 specs) targets the
rewriter in isolation including the broken-source robustness
case.
- spec/multi_statement_linter_spec.rb (new, 7 specs) covers the
warn-only lint plus its no-touch-strings/comments edge cases.
Verified: 4005 Ruby specs, 514 transpile-tests (0 leaks), all
14 benchmarks/sequential/*.cht files re-fmt cleanly and the
FmtVerifier sweep shows byte-identical Zig output before and
after on each.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the previous benchmark fmt commit. All 14 files re-fmt'd
under the four new rules introduced in the preceding commit:
- MUTABLE drop where binding is never reassigned.
- `: Type` annotation drop where the literal RHS already
determines the type (Int64, Float64, etc).
- Generic-bracket attach: `HashMap<Int64, Float64>` instead of
`HashMap < Int64, Float64 >`.
- Struct-literal brace attach + interior padding:
`Node{ val: 5 }` instead of `Node {val: 5}`.
Plus 14_iterator/bench.cht: the hand-written iterator helpers
(`hasNext`, `currentVal`, `advance`) switched from `FN` to
`METHOD`. fmt then UFCS-rewrites all call sites
(`hasNext(iter)` -> `iter.hasNext()`, etc), which is the
canonical CLEAR shape for that style of API.
Verified: FmtVerifier sweep — every benchmark transpiles to the
same Zig before and after fmt. 3 width warnings remain on long
print/interpolation lines (09_frame_vs_heap, 11_pipeline_overhead),
which are out of fmt's scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 specs covering the equivalence-check logic without exercising the full transpile pipeline. Stubs `FmtVerifier.transpile` and `Formatter.format` so the tests focus on: - Identical Zig outputs → ok=true - Diverging Zig outputs → ok=false with a unified-diff excerpt - `// CLR:N` line markers stripped before comparison (the only legitimate fmt-induced delta in emitted Zig) - Code-line differences alongside CLR markers still flagged - Transpile / Formatter raises captured as `error` field - `verify_dir` walks recursively and returns one Result per file - `report` prints details and returns the failure count as exit code (matches CLI behaviour) Plus a sub-describe for `.normalize_for_compare` itself: strips standalone `// CLR:N` lines, leaves inline forms alone, no-op on Zig without markers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Production-style hot loops shouldn't pay the cooperative-yield-check tax on every iteration. Switch FOR / WHILE to TIGHT FOR / TIGHT WHILE in the 7 sequential benchmarks where it's safe. NOT converted (and why): - 11_pipeline_overhead — SPECIFICALLY measures pipeline overhead vs handwritten loops including yield-check cost. Non-TIGHT is the experiment. - 03_alloc_throughput — has both `benchRegular` (FOR) and `benchTight` (TIGHT WHILE) by design as a yield-overhead reference. - 06_simd — its FOR body calls `dot4` which is `@reentrant`; TIGHT-loop-cannot-call-reentrant-function compiler error. - 05_string_builder — loop body frame-allocates a String[]@list per iteration. TIGHT skips the per-iteration saveLoopMark / restoreLoopMark (mir_lowering.rb:6441), so the frame arena grew unbounded (10000 iters × ~50KB each = 576MB RSS, 3x slower). - 09_frame_vs_heap — same shape: loop body frame-allocates strings via interpolation each iteration. TIGHT caused 6MB → 29MB RSS and a 56% slowdown. The 05/09 finding is worth filing as a compiler bug: mir_lowering.rb:6441 unconditionally skips saveLoopMark / restoreLoopMark for TIGHT loops, but CLAUDE.md INV-7 says "Every loop body that allocates from the frame arena must have per-iteration mark/rewind. (Enforced by: FRAME_OVERFLOW check)." TIGHT + frame-alloc currently violates that invariant silently. The MIR checker should reject TIGHT loops whose body has frame allocs (or the lowering should still emit the mark under TIGHT — TIGHT means "skip yield check," not "skip arena hygiene"). Filed in the commit message rather than fixed here because this branch is fmt-scope only. Smoke benchmark numbers (single run each, --smoke uses --optimized): benchmark before after RSS before RSS after 04_hashmap 0.104 s 0.100 s 67 MB 67 MB 07_pointer_chase 0.046 s 0.027 s 60 MB 61 MB (-41%) 08_sort 0.099 s 0.097 s 62 MB 62 MB 10_pool_vs_multiowned 0.078 s 0.074 s 181 MB 181 MB 12_weak_ref_graph 0.021 s 0.015 s 30 MB 30 MB (-29%) 13_soa_layout 0.013 s 0.009 s 56 MB 56 MB (-31%) 14_iterator 0.003 s 0.003 s 6 MB 6 MB All wins or no-change. Ranges from ~0% (already optimal) up to 41% faster. RSS unchanged everywhere. Verified: FmtVerifier sweep — all 14 benchmarks transpile to the same Zig before and after fmt. 4017 Ruby specs, 514 transpile-tests (0 leaks) all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three edge cases surfaced applying fmt to benchmarks/server/:
1. `String@raw` annotation was being dropped as "redundant" because
the decoration check used `Type#any_sync?`, which deliberately
excludes `:raw` and `:symbol` (data-access modes, not locks).
But `String@raw` and `String` use different indexing semantics
(byte vs UTF-8 codepoint), so dropping the annotation silently
changes runtime behavior. Switch the check to read `Type#sync`
directly — ANY sync stamp is decoration for fmt purposes.
2. `MUTABLE` was being dropped from bindings passed mutably through
a `BG { ... }` capture. The annotator's lint marks
`info.reg.var_mutated` only for direct reassignment, not for
"passed as MUTABLE arg" — and BG captures don't propagate the
mutation back to the outer scope at all. Dropping MUTABLE there
broke the next compile with "Argument N is MUTABLE, but you
passed immutable variable X." Two-layer fix:
- annotator.rb extends the lint guard to also skip when
`info.mutated` is set (catches direct MUTABLE-arg passes
like `bar(arr)` where `bar` declares `MUTABLE xs`).
- lint_fix_rewriter.rb walks the AST collecting every name
referenced inside a BG/BG-STREAM block, and skips MUTABLE-drop
for those names. Defensive — until the annotator's mutation
analysis propagates through BG captures, this avoids the
specific compile-fail pattern.
3. FmtVerifier was flagging `// CLEAR_PROFILE_TASK_SITE id=N
line=L column=C` debug comments as differences when fmt
rearranged source (line/col shift). Same class of metadata as
`// CLR:N` markers — strip them in `normalize_for_compare`.
Tests: 3 new specs in spec/lint_fix_rewriter_spec.rb cover the
BG-capture skip, the no-skip case (binding not in BG), and the
String@raw preservation case.
Verified: 4017 Ruby specs, 514 transpile-tests (0 leaks) all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
01_tcp_kvstore, 02_json_api, 03_pathological all formatted under the current rules: indent normalization, MUTABLE-drop on read-only bindings, `: Type` drop on literal-RHS declarations (Int64 / Float64 where context determines), generic-bracket attach (`HashMap<K, V>`), struct-literal attach + interior padding, leading- comment whitespace preserved, predicate canonicalization (`x.length() > 0` -> `x.any?()`, etc). All three .cht files build cleanly post-fmt. The FmtVerifier sweep flags some byte-level Zig deltas where the predicate rewriter emits canonical helper calls instead of inline length comparisons (e.g. `@as(i64, @intcast(resp.len)) > 0` -> `resp.len > 0` via `.any?()`). These are intentional — the rewriter exists to produce that output — and the resulting Zig is semantically equivalent. Build success + existing test suite passing is the actual correctness signal here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 .cht files reformatted under the current rules. examples/minivm/
is excluded per its deprecated/active-VM split (CLAUDE.md: scheme
and interpreter paths are deprecated; _bc_runner is hand-tuned
hot-path code).
Files modified:
examples/concurrent_reports/reports.cht
examples/data_pipeline/pipeline.cht
examples/graphdb/graph.cht
examples/json_parser/json.cht
examples/litedb/litedb.cht
examples/parallel_du/du.cht
examples/testing/{basic_test,freeze_candidate,stub_ufcs}.cht
examples/web_crawler/src/main.cht
examples/footguns/{05_toctou,06_memory_ordering,07_causal_ordering,
10_alias_mutation,12_deadlock}/main.cht
Files NOT modified (refused-to-format on parse error):
examples/footguns/{02_data_race, 04_task_leak, 08_buffer_overflow,
09_uninitialized_read, 11_iterator_invalidation}/main.cht
Those five footgun demos contain syntax that no longer parses under
the current language — likely the syntax churn (`s>` → `|>`, `--`
→ `#`, etc) outpaced the demo. fmt's parse-validation refuses to
write rather than corrupting them, which is the right call. Worth
filing as a separate cleanup task.
Build status unchanged by fmt: pre-fmt and post-fmt produce the
same build pass/fail set. The footgun demos that DO parse still
fail to build at the annotator/MIR stage — that's by design (they
demonstrate compiler safety errors). web_crawler fails on a
missing http.zig import in both states.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seven footgun demos had rotted past the language churn and refused
to parse, so the previous "fmt examples/" commit left them
untouched. Bring them current, then format under the same rules.
Per-file changes:
01_use_after_free
- `[]Int64` -> `Int64[]` (suffix-bracket array syntax)
02_data_race
- `REPEAT N TIMES ... END` -> `FOR i IN (0 ..< N) DO ... END`
03_memory_leak
- `RETURNS T OR RAISE` -> `RETURNS !T` (modern fallible signature)
- `expr OR { handler }` block-handler form is no longer
supported; `OR RAISE` / `OR EXIT` are the modern shapes.
Replaced the demo's call-site error block with a comment
explaining the modern propagation idioms.
04_task_leak
- `BG { fn() };` -> `BG { fn(); };` (BG body needs `;`)
- `REPEAT N TIMES ... END` -> `FOR i IN (0 ..< N) DO ... END`
08_buffer_overflow
- `[Int]` -> `Int64[]` (CLEAR has no bare `Int`; it's `Int64`)
- `WHILE cond { ... }` -> `WHILE cond DO ... END`
- `arr[i]! = val` -> `arr[i] = val` (no mutation suffix on
indexed assignment in modern CLEAR)
- Added `MUTABLE` and explicit type on the loop-counter
bindings that the new fmt rules expected.
09_uninitialized_read
- `Int` -> `Int64` (4 sites)
- `WHILE cond { ... }` -> `WHILE cond DO ... END`
- Added `MUTABLE` on `sum` and `i` (they're reassigned).
11_iterator_invalidation
- `list | filter(...) | collect()` -> `list |> WHERE ... |> COUNT _`
(modern pipeline operator + stage keywords)
- `list | reduce(0, ...)` -> `list |> REDUCE(0_i64) acc + _`
- `[Type] @shared:locked` -> `Type[]@list:shared:locked`
(suffix-bracket + capability syntax)
- `list.append!(50)` -> `list.append(50)` (no UFCS-mutation `!`)
Build status (semantic-stage only — fmt is verified separately
by re-parsing post-write):
09_uninitialized_read builds clean
Other 6 fail at annotator/MIR level
The failures are not fmt regressions — these demos exercise
features (struct cleanup with TAKES, polymorphic @shared:locked
under BG, etc) that are still in flight or were never fully
implemented for the demo. Whether each demo's "expected error" is
actually firing for the right reason is a follow-up; this commit
just gets them parseable + formatted again so future fmt sweeps
include them.
Verified: 4020 Ruby specs, 514 transpile-tests (0 leaks).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Formatted under the same rules used in the previous benchmark sweeps:
indent normalization, MUTABLE-drop on read-only bindings, redundant
`: Type` drop on literal-RHS declarations, generic-bracket attach,
struct-literal attach + interior padding, leading-comment whitespace
preserved, predicate canonicalization. METHOD-flagged calls get
UFCS-rewritten where the receiver type matches.
NOT fmt'd (and why):
- 02_concurrent_search/bench.cht — calls `readFile(filepath)`,
which fmt UFCS-rewrites to `filepath.readFile()`. The FSM lowering
for I/O-suspending calls isn't UFCS-form-agnostic and crashes with
`FsmOps arg index 0 out of range (0 args)` after the rewrite.
Pre-existing fmt+FSM interaction bug; reverted to leave the file
untouched.
- 14_nested_lock/bench.cht — has `IF cond THEN s1; s2; END`
multi-statement one-liners that fmt's IF-expansion pass mishandles,
collapsing trailing comments and following blocks into a single
323-char line that no longer parses. Pre-existing fmt bug;
reverted. The file would benefit from the multi-statement-per-line
warning that already exists in this branch — splitting the THEN
body into its own line first would let fmt re-engage cleanly.
Both reverted files build clean before fmt and represent fmt
limitations rather than benchmark-author errors. Worth filing as
separate fmt-bug tickets:
1. UFCS-rewriter should skip stdlib functions whose lowering has
position-sensitive arg-shape requirements (FSM-IO calls).
2. Multi-statement-after-THEN expansion mishandles trailing
comments + following blocks.
Of the 16 files that were fmt'd, all 16 build clean. The
FmtVerifier flags some byte-level Zig deltas where the predicate
rewriter emits canonical helper calls or where guard-counter IDs
shifted due to AST changes — both semantically equivalent.
Profile-data .cht files in `*.profile/` directories are gitignored
and not affected by this commit.
Verified: 4020 Ruby specs, 514 transpile-tests (0 leaks).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two pre-existing fmt bugs surfaced by the concurrent benchmark sweep,
both of which forced reverts in the previous commit. Fixed here with
tests, then the two reverted files re-formatted under the new code.
Bug 1 — UFCS-rewrite of FSM-lowered stdlib (e.g. `readFile`):
`readFile(filepath)` was being rewritten to `filepath.readFile()`
because the registry entry has `is_method: true`. But its lowering
reads positional args via FsmOps templates — once the first arg
moves into the receiver slot, the FSM emitter crashes with
"FsmOps arg index 0 out of range (0 args)."
Fix in src/tools/method_rewriter.rb: skip stdlib entries that have
BOTH `suspends: true` AND any `fsm_*` template key. Detection is
structural so future FSM-lowered helpers get the skip automatically.
Bug 2 — `BG { @parallel -> FOR ... DO ...; ...; END }` collapsing to
one massive line:
Three interacting fmt bugs combined here. `count_statements_in_block`
didn't track DO/THEN nesting, so inner `;` looked like top-level
statements and the wrap fired wrong; `emit_bg_do_wrapped` also
used to NL-on-`;` regardless of nesting; and most importantly,
`expand_method_chains` stripped ALL NLs from chain segments
including ones nested inside an argument-position
`BG { ... }` block.
Fixes in src/tools/formatter.rb:
- count_statements_in_block tracks DO/THEN block depth and
counts a single top-level block (FOR/IF/WHILE/MATCH) as one
statement.
- bg_body_needs_wrap? wraps single-statement bodies when the
single statement opens a nested block, so the body still
gets multi-line layout.
- emit_bg_do_wrapped tracks DO/THEN depth too and only NLs
on `;` at the BG-level top.
- expand_if_while_for tracks DO/THEN depth so inner one-liner
blocks (`IF a > b THEN lo = b; hi = a; END`) don't get torn
apart when expanding their containing FOR/WHILE/IF.
- expand_method_chains strips NLs ONLY at chain-segment top
level — not those nested inside `(args)` / `{ body }`.
Additional fix in src/tools/fmt_verifier.rb: extend
normalize_for_compare to also strip lowering-emitted ID counters
in synthesized variable names (`__bank_guard_3700` /
`__sort_guard_3940_1` etc). Equivalent CLEAR programs whose AST
shifted under fmt produce the same Zig logic with different IDs;
these are diagnostic counters, not semantic.
Tests: 5 new specs total — 2 in spec/method_rewriter_spec.rb
covering the FSM skip + non-FSM still-rewrites cases, 3 in
spec/clear_fmt_spec.rb covering BG-with-FOR-body wrap, NL
preservation through method-chain BG args, and inner-one-liner-IF
preservation through outer-FOR expansion.
Could be folded into the earlier MethodRewriter / lint-rules
commits if a single squash is preferred at PR time; left as a
follow-up commit here so the diff is reviewable in isolation.
Files re-fmt'd under the fixes:
benchmarks/concurrent/02_concurrent_search/bench.cht
benchmarks/concurrent/14_nested_lock/bench.cht
Verified: FmtVerifier sweep — all 19 concurrent benchmarks pass
(02 and 14 now OK; the previously-failing files build clean).
4025 Ruby specs, 514 transpile-tests (0 leaks) all green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #17 +/- ##
==========================================
+ Coverage 89.60% 89.73% +0.12%
==========================================
Files 170 173 +3
Lines 45996 46723 +727
Branches 11290 11604 +314
==========================================
+ Hits 41216 41927 +711
- Misses 4780 4796 +16
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
| Branch | fmting |
| Testbed | ubuntu-latest |
⚠️ WARNING: No Threshold found!Without a Threshold, no Alerts will ever be generated.
Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the--ci-only-thresholdsflag.
Click to view all benchmark results
| Benchmark | leak-build-ms | Measure (units) x 1e3 | leak-count | Measure (units) | leak-run-ms | Measure (units) |
|---|---|---|---|---|---|---|
| benchmarks/concurrent/05_backpressure/bench | 📈 view plot | 5.62 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 1,863.95 units |
| benchmarks/concurrent/10_shard_vs_locked/bench | 📈 view plot | 5.17 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 60,004.94 units |
| benchmarks/concurrent/16_observables/bench | 📈 view plot | 5.20 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 95.84 units |
| benchmarks/inter-clear/03_concurrent_mvcc_vs_rwlock/bench | 📈 view plot | 5.67 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 210.56 units |
| benchmarks/sequential/07_pointer_chase/bench | 📈 view plot | 5.09 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 596.12 units |
| benchmarks/sequential/12_weak_ref_graph/bench | 📈 view plot | 5.13 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 130.57 units |
| benchmarks/server/03_pathological/server | 📈 view plot | 5.25 units x 1e3 | 📈 view plot | 0.00 units | 📈 view plot | 1,002.87 units |
4579413 to
2031fe3
Compare
Builds on the previous round of formatter fixes. Seven bugs total:
- **MATCH `;,` arm separator** — `;` at depth 0 forced a NL before
the trailing arm comma. The statement-terminator helper now absorbs
an immediately-following `,` (idempotently — even if a prior pass
already split them).
- **Inline `@cap:cap` chain** — the default `:` rule (no space before,
space after) treated chains like a type annotation. Added a
capability-chain check that walks back through `:identifier:...:`
segments (skipping `(...)` args) and bottoms out at an `@cap` VAR_ID.
Normal `: Type` annotations and `REQUIRES x: LOCKED` are unaffected.
- **MATCH block layout** — multi-line `MATCH ... START ... END` had
arms at the same column as `START` and END at column 0, multi-line
arm bodies stayed flush with the arm pattern, and `DEFAULT`
outdented like CATCH/DEFAULT in TRY. New `expand_match_blocks` emits
arms at +1 from `START`, multi-statement arm bodies at +2 with the
trailing `,` flush against the body's last `;`, END at MATCH-line
indent. DEFAULT is tagged with `:KEEP_INDENT` to suppress render's
OUTDENT_LEADING for that line.
- **Multi-branch IF/ELSE_IF chain with same-line bodies** — repro from
examples/json_parser/parseString. `expand_then_do_blocks` now also
fires when any branch has inline body, body walker pushes inline
bodies onto their own line at +1, ELSE/ELSE_IF outdent works
naturally. Body walker's keyword-block-depth tracker counts
IF/WHILE/FOR/TEST/WHEN openers (not THEN/DO) so ELSE_IF's own THEN
doesn't falsely nest.
- **BG body with leading `@strategy ->`** — repro from
benchmarks/concurrent/09_kvstore. `BG { @parallel -> body }` had a
`->` whose OPEN_TERMINAL render rule raised body depth, but no END
inside to lower it back; only `}` closes (-1). Statements after the
BG ended up one column too deep. `emit_bg_do_wrapped` now detects a
top-level `->` in the BG body and emits a balancing INDENT_CLOSE
before `}`.
- **Capability chain with parenthesized argument** — repro from
benchmarks/concurrent/09_kvstore (`@shared:sharded(128):locked`).
The chain detector bailed when it hit `)`, leaving the trailing
`: locked` rendered with a space. `capability_chain_colon?` now
skips a trailing `(...)` group before falling back to the
identifier.
- **`END` always opens a new line** — repro from json_parser
jsonToString JBool arm where `END RETURN "false";` stayed glued.
New `nl_after_end` post-pass forces a NL after every END, except
when END is followed by a close-bracket or `;` (`}`, `)`, `]`, `;`).
Re-fmt'd the affected branch files. 4130 unit specs, 0 failures.
litedb / graphdb run clean; json_parser, kvstore, atomic_ptr,
pubsub, false_sharing, rwlock_starvation, nested_lock all build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.