feat(rules): fromrev (U016), fewer false positives in six rules, retire argv - #229
Merged
Merged
Conversation
`fromrev` reports each run of significant tokens whose texts are `String.from_list`, `(`, `List.reverse` and `(`, one finding on the first, and nothing else; a LAWS.bend or a PROOF.bend is exempt. Reversing a char buffer and reading it into a String walks it twice and builds a list only to throw it away, and String.from_list is not a tail call: fold the buffer onto a String with SCon in one pass. On in `suspicious` by default. Measured (bend 2.0.34, a 100k-char reversed buffer 200 times): native --gpu off 0.35 s against 0.17 s folded; JS at 20k chars 4.6 s against 3.1 s, and at 50k the two-pass form overflows the JS stack. BOLT-RULE-U016 is proved by fromrev_counts (a count against an independent counter, zero on a law file), fromrev_exempt (also under BOLT-RULE-EXEMPT) and inert_fromrev (also under BOLT-RULE-INERT). The code table's per-row proof arms cover 35 rows. bolt's own 9 sites now go through src/rchars.bend (Rchars.text, one pass): the lexer's word, noqa's word, word.bend's text, frame's decode.end, config's dir_of.go and home.segs, and trace's cells. The lexer and trace proofs are carried over to the fold (rword.go, trace.text). Refs #98 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
thunk now reports a self-call thunk only when it is the one lambda among the arguments of the call it is passed to: the kids of a `(` group, split at their comma leaves, hold exactly one argument with a `=>` leaf of its own (not inside a bracket). A dispatch, a call given two or more lambdas (`Lazy.either(T, c, _u => go(a), _v => go(b))`), is left alone: carrying one Bool into the next call does not replace it. A lambda that is no call's argument (`x = _u => go(t)`) is no longer reported either. This narrows a guaranteed behavior: BOLT-RULE-U015 is reworded with the rule's header, and thunk_walk_counts' independent counter (thunk.count, now carrying whether the chain is such a call's kids, with thunk.args and thunk.lone) is restated to match and re-proved; thunk_counts, thunk_exempt and inert_thunk follow. Over the corpus (bolt, ez, ezhttp, ezjson, eztoml, snap; law, proof and test files removed): 200 -> 177 findings. bolt 77 -> 65, ez 69 -> 69, ezhttp 19 -> 17, ezjson 6 -> 6, eztoml 27 -> 18, snap 2 -> 2. Every one of the 23 dropped is a call with two or more lambdas. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
…t's own self-call Behavior change to two released rules, both with 0% sampled precision on real code. fuel (U006, BOLT-RULE-U006): a call to a def that returns an effect is no longer reported. A def returns an effect when its header holds a top-level `->` token with the capitalized name token `IO` right after it, or, when the `->` is the header's last token, first on the line under it (`->` then `IO(Unit):`). An effect loop's fuel bounds reads, frames or retries the outside world sets, not the size of an input. The law fuel_slots is restated (fuel.slots keeps only the defs that return no effect); fuel_walk_counts and fuel_counts follow it. bolt's own two `# noqa: U006` (src/lsp/files/disk.bend, src/lsp/transport/stdio.bend) silenced nothing now and are removed. index (U007, BOLT-RULE-U007): a List.get / String.get call whose name is the def's own (the step of a def named List.get or String.get, as Base's are) is no longer reported. index_counts is restated (index.count and index.site take the def's name as self). Counts on the sampled corpus (old -> new U006+U007): bend 6 -> 1 (the one left is Wav.walk, a pure def, still reported), bolt tree 0 -> 0 with its noqa comments gone, every other ecosystem repo 0 -> 0. bolt on this tree: 0 errors, 73 warnings, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
Behavior changes to two released rules, each with its SPEC row, rule header and tagged law restated to match: - unused (BOLT-RULE-U001): a foreign def, whose parameters are exempt, is one whose body's first statement is `import`, wherever its header's `->` and return type fall. A header that does not end in `:` runs on through the body's statements up to one that does, so `def f(..) ->` then `IO(T):` on the next line then `import "./x.c"` is foreign. Law unused_counts restated (Laws unused.imported now takes the header). - unused: a name read in a dependent arrow at the start of a body (`@+x: U32 -> S`) is a use. The tree classified any `@`-led statement as a def (for `@unsafe def`), so the binder skipped the arrow; an `@` followed by a name, quantity-marked or not, and `:` now makes a term. New untagged laws in src/syntax: arrow_term, marked_arrow_term, attribute_def. - doc (BOLT-RULE-S001): the comment block may sit right above a run of `@` lines (`@unsafe`) right above the item. Laws doc_walk_counts and doc_counts restated (Laws doc.lead replaces doc.hash); inert_doc (BOLT-RULE-INERT) now takes lines that start with `#` alike and with `@` alike. Corpus (every group on at warn), old -> new: - bend: U001 150 -> 98 (46 wrapped foreign params in bend2/base.bend, 6 dependent-arrow params in bend3d.bend); S001 300 -> 298 (@unsafe) - selfhost: U001 3815 -> 3815; S001 2592 -> 2508 (84, all @unsafe) - bolt, ez, ezaudio, ezhttp, ezimg, ezjson, eztoml, shake, snap: 0 -> 0 No finding added anywhere, no other rule's findings changed. bolt on its own tree: 0 errors, 73 warnings, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
A false-positive audit of argv put its precision at 8%: every IO.args() reader in the ecosystem already drops the program name, and the rule cannot tell a correct reader from a wrong one, so nearly every finding asked for a noqa. It merged in #226 and was never released, so it goes now: the rule, its registration, its code-table row, its opt-in entry, the laws argv_counts, inert_argv and argv_opt_in with their proofs, the BOLT-RULE-U014 row, and bolt's own setting and noqa for it. U014 stays reserved: codes are append-only, and src/codes.bend and src/README.md list it as retired beside C001, U005 and L004. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
scan stays in bolt as an opt-in audit tool, its rule and laws unchanged; bolt's own tree no longer turns it on. Its 55 noqa comments go with it, since with scan off noqa (S005) would report each as naming a code that no finding has. No logic changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
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.
This follows #226. It adds one rule, and it narrows or fixes the rules that a false-positive audit on real code found noisy.
.bendfiles of 9 Emerging-Patterns repos, bendlang/bend's demos and base, and the self-hosted compiler from Self hosted bend bendlang/bend#1207.bolt.bendturning every rule on, and up to 15 hits per rule were read at the source.Closes #98.
Changes
feat(rules): fromrev (U016)suspiciousfix(rules): thunk (U015) …fix(rules): fuel (U006) … index (U007) …fix(rules): unused (U001) and doc (S001) …fix(rules): retire argv (U014) …chore: turn scan (U013) off …Each narrowing only removes findings; no repo gained one. No law was weakened to pass a proof, and the trust boundary did not grow.
New:
fromrev(U016)String.from_list(List.reverse(..)), where folding the buffer onto aStringwithSCondoes it in one pass.from_listform overflows the stack at 50k characters, becauseString.from_listis not a tail call.src/rchars.bend.Narrowed or fixed
thunkU015Lazy.either(.., _u => go(a), _v => go(b))), where carrying a Bool is the wrong advicefuelU006App.loop,Client.drain, bolt's own reads)IO(..); bolt's 2 stalenoqa: U006removedindexU007List.get/String.getrecursionunusedU001@+x: T -> ..arrowsrc/syntax/tree.bend)docS001@unsafeline was rejected@lines countsinert_docnow also assumes the two sources' lines start with@alike; BOLT-RULE-INERT's row is unchanged. The new untagged lawsarrow_term,marked_arrow_termandattribute_defpin the tree change.Opt-in rules from #226
argv(U014) is retired before its first release.IO.argsdocumentation in AGENTS.md stays.scan(U013) stays opt-in but is off in bolt's ownbolt.bend.# noqa: U013comments are removed.Checks
These ran on bend 2.0.34 with
BEND_LIBset to the pinned shake, ezjson and snap. There is no nix or ez in the environment, so CI'snix flake checkis the full run.PROOF.bendprintALL PROOFS CHECK.bend main.bend -o bin/bolt.bin, thenbin/bolt.bin --gpu offover the tree, gives0 errors, 73 warnings, unchanged from main (all L001).Left open
thunksites.setting.strictextension from Self hosted bend bendlang/bend#1207).strict(82%) andconcat(80%) sit near the line on small samples; neither is changed here.🤖 Generated with Claude Code
https://claude.ai/code/session_01X7i62UAece6mwMRV3Y1b9B
Generated by Claude Code