Repository navigation
feat(rules): empty (U018), a kept length compared to zero is is_empty - #270
Merged
Merged
Conversation
`empty` reports `Nat.is_eq(List.length(.., x), 0n)` and
`Nat.is_eq(String.length(x), 0n)` when the test is an argument (right
after `(`, `[`, `{` or `,`) and x, a plain name, is read again after it in
the same group. Base's lengths count every cell and are not tail calls;
`List.is_empty(.., x)` / `String.is_empty(x)` take the same arguments,
look at one cell and give the same Bool.
Only a kept x is reported: when the test is x's last use, the native lane
frees a list the program never walked more slowly than it walks one, so the
fix loses there (bend 2.0.38, a 0-1000 char String: kept 1.57x native and
1.31x JS; dropped 0.27x native, 1.65x JS). A law, a def that is a proof
(no type, or `-> {..}`), a LAWS.bend and a PROOF.bend are skipped. Over the
public Bend 2 corpus it reports 8 sites in 2 repos (BendKV 6, birc 2), each
read by hand. On in `suspicious` by default.
Laws: empty_reports_{list,string}_kept, empty_reports_in_typed_def,
empty_skips_{list,string}_{dropped,before,bare} (no read after, a read only
before, outside any group), empty_skips_in_{law,untyped_def,proof_def},
empty_exempt, role_empty, role_empty_exempt and inert_empty (comments and
string contents never change the findings). The code table proof gains
its 37th row. bolt's own dir_of tested a kept list's length; it uses
List.is_empty now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0186KEnst9PZ9GVQD8BtKhu4
Resolve the rule regroup (#267): `empty` (U018) stays in `suspicious` beside `prefix` and `fromrev`; strings and chars are `perf` as main has them. The code table proof keeps main's arms, which already reach past the 30 rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0186KEnst9PZ9GVQD8BtKhu4
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.
emptyreportsNat.is_eq(List.length(.., x), 0n)andNat.is_eq(String.length(x), 0n)when the test is an argument (right after(,[,{or,) andx, a plain name, is read again after it in the same group. Base's lengths count every cell and are not tail calls.List.is_empty(.., x)/String.is_empty(x)take the same arguments, look at one cell and give the same Bool. On insuspiciousby default, besideprefixandfromrev.Why only a kept
xOn the native lane, freeing a list the program never walked costs more than walking it: callgrind puts about 155 instructions a cell in the runtime's
term_dropagainst about 28 for a compiled walk. When the test isx's last use, the original length walk freesxcheaply and the fix is slower on native. Measured on bend 2.0.38 (a 0–1000 char String, old/new):So the rule leaves alone:
xafter it in its group,xonly before it,v = Nat.is_eq(..)).It also skips a
lawand a def that is a proof (no type, or one returning-> {..}) in any file, plus LAWS.bend and PROOF.bend. A first version reported 4 law statements and proof defs in bendlib's mathlib, which is why the proof skip is there.Evidence
Laws (each proved)
empty_reports_{list,string}_kept,empty_reports_in_typed_def.empty_skips_{list,string}_{dropped,before,bare}empty_skips_in_{law,untyped_def,proof_def}empty_exempt,role_empty,role_empty_exempt.inert_empty. Comment and string contents never change the findings. The rule reads token text only throughcode(k, t), so this needs no "as the lexer makes them" hypotheses.The code table gains a row, U018. Its proof's arms already reach past the 30 rows, so it is unchanged.
Also
dir_of(src/rules/imports.bend) tested a kept list's length; it usesList.is_emptynow.emptystays insuspicious.nix flake checkpasses locally and in CI.pick,strictandeager's suggested fix slower when the searched list is fresh and dropped right after the search (0.62–0.94× on native in my runs; faster when the list is kept). This PR leaves those rules unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_0186KEnst9PZ9GVQD8BtKhu4
BEGIN_COMMIT_OVERRIDE
feat(rules): empty (U018), a kept length compared to zero is is_empty
END_COMMIT_OVERRIDE