fix lint: name the unused scope-exit test binding _sb - #7
Conversation
The lint step builds angler from master on every run, and angler's unused-let-binding rule flags the binding in the "owned buffers are deleted automatically at scope exit" assertion. That binding is deliberately unused -- it is the thing under test, bracketed by Debug.reset-memory-balance! / Debug.memory-balance -- so it cannot be deleted without leaving an assertion that measures nothing. A leading underscore is angler's own discard exemption (binding-name in angler.carp), the same spelling core's ignore expands to. The rename is codegen-neutral: diffing carp -b output across the two trees shows only the declaration and the StringBuf_delete call, differing in the variable's name alone.
There was a problem hiding this comment.
Build & Tests
CI is green on both ubuntu-latest and macos-latest, which is the run that matters here since the Lint step builds angler from master each time.
Locally I reproduced the premise with strbuf's exact CI file set and a freshly built angler (~/.carp/out/angler, built today; angler's master HEAD is fd6d682, 2026-08-15). Worth saying explicitly: the angler on my PATH is a symlink to a binary from 2026-08-14, so I used the fresh build by absolute path — a stale linter is the exact thing that hides this class of problem, and CI clones and builds fresh on every run.
| tree | angler exit |
|---|---|
main |
1 — ./test/strbuf.carp:164:7: [unused-let-binding] let binding 'sb' is never used |
| this branch | 0 |
So the failure is real and this fixes it.
Findings
None. One thing I did check rather than take on faith, because it's the whole argument of the PR:
The assertion this PR goes out of its way to preserve does have teeth. The body argues the binding must be renamed rather than deleted because the unused binding is the subject of the test — which is only a good argument if the test measures anything. So I mutated it. Replacing the body with (let [_sb (StringBuf.create)] (Unsafe.leak _sb)) suppresses exactly the scope-exit destructor the test is about, and nothing else:
Test 'owned buffers are deleted automatically at scope exit' failed:
Passed: 19 Failed: 1
mut_rc=1
One failure, and it is precisely that assertion — every other test is unmoved. So the assertion measures that one destructor, it is not vacuous under --log-memory, and deleting the binding really would have left something that measures nothing. (Restored immediately; the tree is clean.)
That also settles the rename's safety more directly than the generated-C diff in the body does: CI runs carp -x --log-memory test/strbuf.carp and is green on both OSes after the rename, and the mutation above shows that run would go red if _sb stopped being deleted. So _sb is provably still dropped at scope exit.
The rest checks out: the diff is the single line, the branch's merge-base is origin/main so nothing is stale, _ is angler's own discard prefix (same spelling ignore expands to), and strbuf has no CHANGELOG.md so adding none is right.
Verdict: merge
One-line fix for a red lint gate, with the one subtle trap in it — that the dead binding is also the thing under test — spotted and handled correctly. Verified both directions locally, and the assertion it preserves is mutation-tested as live.
strbuf's Lint step clones and builds
anglerfrom master on every run. angler'sunused-let-bindingrule now flagstest/strbuf.carp:164, so the lint step exits 1 today — the next push to strbuf, anyone's, goes red for a reason unrelated to their change. strbuf's last CI run was 2026-07-26, before the rule landed.The binding is renamed to
_sb. A leading underscore is angler's own discard exemption (binding-nameinangler.carp, pinned by angler's test suite) and the same spelling core'signoreexpands to.The binding is not deleted, deliberately. It is the body of the assertion at
test/strbuf.carp:160-166, "owned buffers are deleted automatically at scope exit", which bracketsDebug.reset-memory-balance!/Debug.memory-balancearound a buffer that is created and never used. The unused binding is the thing under test; deleting it would leave a passing assertion that measures nothing.Verification
Every command below is strbuf's own CI command, with
anglerfreshly built from master (the local binary was a day stale — worth noting, since a stale linter is exactly what hides this class of problem).Lint — strbuf's exact CI file set:
1(the finding above)0Tests —
carp -x --log-memory test/strbuf.carp:20 passed, 0 failedbefore and after, exit 0 both times.Test 'owned buffers are deleted automatically at scope exit' passedin both runs, so the balance is still 0 — that assertion compares against0l.Codegen-neutral —
carp -b test/strbuf.carpon both trees, diffing the generated C. The entire diff:Two lines, differing in the variable's name alone. The emitted C for the region confirms the assertion still measures the automatic deletion:
Format —
carp-fmt --checkover the same file set: exit 0.strbuf has no
CHANGELOG.md, and none was added. The diff is the one rename.Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.