Skip to content

fix lint: name the unused scope-exit test binding _sb - #7

Merged
hellerve merged 1 commit into
mainfrom
claude/lint-unused-let-binding
Aug 17, 2026
Merged

fix lint: name the unused scope-exit test binding _sb#7
hellerve merged 1 commit into
mainfrom
claude/lint-unused-let-binding

Conversation

@carpentry-agent

Copy link
Copy Markdown

strbuf's Lint step clones and builds angler from master on every run. angler's unused-let-binding rule now flags test/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.

./test/strbuf.carp:164:7: [unused-let-binding] let binding 'sb' is never used
  at: (let [sb (StringBuf.create)] ())

The binding is renamed to _sb. A leading underscore is angler's own discard exemption (binding-name in angler.carp, pinned by angler's test suite) and the same spelling core's ignore expands 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 brackets Debug.reset-memory-balance! / Debug.memory-balance around 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 angler freshly 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:

exit
before 1 (the finding above)
after 0

Testscarp -x --log-memory test/strbuf.carp: 20 passed, 0 failed before and after, exit 0 both times. Test 'owned buffers are deleted automatically at scope exit' passed in both runs, so the balance is still 0 — that assertion compares against 0l.

Codegen-neutralcarp -b test/strbuf.carp on both trees, diffing the generated C. The entire diff:

14916c14916
<             StringBuf sb = _917;
---
>             StringBuf _sb = _917;
14918c14918
<             StringBuf_delete(sb);
---
>             StringBuf_delete(_sb);

Two lines, differing in the variable's name alone. The emitted C for the region confirms the assertion still measures the automatic deletion:

Debug_reset_MINUS_memory_MINUS_balance_BANG_();
/* let */ {
    StringBuf _917 = StringBuf_create();
    StringBuf _sb = _917;
    /* () */
    StringBuf_delete(_sb);
}
Long _922 = Debug_memory_MINUS_balance();

Formatcarp-fmt --check over 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.

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.

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hellerve
hellerve merged commit 6805ca8 into main Aug 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant