Skip to content

Lint sweeps for STYLE042 / STYLE043 / PERF033, => line continuation, inscope after break/continue - #4217

Merged
borisbat merged 9 commits into
masterfrom
lint-sweep-fixes
Oct 6, 2026
Merged

borisbat merged 9 commits into
masterfrom
lint-sweep-fixes

Conversation

@aleksisch

@aleksisch aleksisch commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

The tree sweeps for three new lint rules (STYLE042, STYLE043, PERF033 - the rules themselves are #4213, which stacks on this branch), plus the three compiler and interpreter fixes the sweeps need and three small lint fixes their CI needs.

Commits

  1. lint: LINT026 counts citations from the documents above the run's folders - check_lint_changed lints only the changed folders, so an anchor cited from a rule document one folder up (modules/REVIEW_SHADER_EMITTERS.md -> dasMetal/ARCHITECTURE.md#kernel-model-asymmetry-ledger) read as uncited whenever a dasMetal source changed. The pass now also reads the .md files above each root, as citers only.
  2. dasLLAMA: the performance checklist cites setup_lcpp_ref.das by its path - LINT033: the bare name never resolved from performance/.
  3. dasLLAMA: fat_start's dasllama_env require takes LINT019 like its strings_boost one - both serve only the llvm_tune half, so the nolint is stale in a build with llvm_tune.
  4. parser: a => at the end of a line continues on the next - the lexer takes the line break right after => as part of the token. Every grammar rule with => requires an expression after it, so a newline there was always a syntax error; no program that compiled before changes meaning. A blank line or a comment after => still ends the statement. tree-sitter already parsed the form; its corpus and the grammar canary now pin it. The STYLE042 sweep needs it for bodies that do not fit beside their signature.
  5. infer: break and continue mark early out like return - a break / continue in a nested block before a var inscope declaration left its slot uninitialized, and the finalizer deleted garbage ("can't delete locked array"). Found by the PERF033 sweep; tests/daslib/test_inscope_early_out.das pins it.
  6. simulate: a while loop with a finalizer stops before its condition on return - a while body that owns a finalizer (a var inscope local) evaluated the loop condition before it looked at stopFlags, and a condition that is a real call cleared the pending return on its way out - the loop went on. read_toml spun forever on bad input in the interpreter (JIT and AOT were fine); the PERF033 sweep's inscope in daslib/toml.das exposed it. tests/language/loop_ret.das pins it.
  7. STYLE042 fixes - def f(...) { return EXPR } -> def f(...) => EXPR (=> <- EXPR for a move), 936 files.
  8. STYLE043 fixes - var a : T / var b : T -> var a, b : T, 257 files.
  9. PERF033 fixes - an array / table declared inside a loop becomes var inscope, 223 files. Without it every iteration leaks the buffer until the heap resets.

Commits 7-9 were written by each rule's --fix and re-linted per file. They cover the code master has; the sweep of the lint code #4213 adds is in #4213.

Verification

@aleksisch
aleksisch force-pushed the lint-sweep-fixes branch 2 times, most recently from df14308 to 29b9814 Compare October 6, 2026 15:20
@aleksisch
aleksisch marked this pull request as ready for review October 6, 2026 15:21
@aleksisch
aleksisch requested a review from borisbat October 6, 2026 15:23
@aleksisch
aleksisch force-pushed the lint-sweep-fixes branch 2 times, most recently from 3b6ac8f to dc7b3e3 Compare October 6, 2026 20:22
…ders

The reverse half of LINT026 - an anchor nothing cites - read citers only under
the run's roots. A rule document binds its subfolders, so it often sits above
the folder whose anchors it names: modules/REVIEW_SHADER_EMITTERS.md cites
dasMetal/ARCHITECTURE.md#kernel-model-asymmetry-ledger. check_lint_changed lints
only the changed folders, so any change to a dasMetal source reported that
anchor as uncited. The pass now also reads the .md files in every folder above
a root, as citers only - their own anchors and citations are judged when their
folder is linted.
LINT033 resolves a cited path from the citing folder, its tree and its
ancestors; benchmarks/ is a sibling of performance/, so the bare name never
resolved. The neighbouring citation already spells ../benchmarks/.
…ings_boost one

Both requires serve only the llvm_tune static_if half. A build with llvm_tune
uses dasllama_home_dir, so STYLE030 stays silent and its nolint reads as stale;
line 14 already carries LINT019 for the same reason, line 9 now does too.
A function, method, block or lambda arrow body that does not fit beside its
signature can move below it:

    def scale(first_argument, second_argument : int; factor : float) : float =>
        float(first_argument * second_argument) * factor

The lexer takes the line break right after `=>` as part of the token, so it
never ends the statement. Every grammar rule with `=>` requires an expression
after it, so a newline there was always a syntax error - no program that
compiled before changes meaning. Only that one break joins: a blank line or a
comment after `=>` still ends the statement. tree-sitter already parsed the
form; its corpus and the grammar canary now pin it.
A var inscope is finalized when its scope ends, and an early exit ends it too -
before the declaration ran when the exit comes first. ExprLet's early_out
relocation zeroes such a variable at the top of the block, but only return
marked the scope chain: a break or a continue in a nested block before the
declaration left the slot holding whatever the stack had, and the finalizer
deleted it - 'can't delete locked array', or worse, in the interpreter (JIT
happened to be fine). break and continue now mark the chain the same way.
… return

A while body that owns a finalizer (a var inscope local) leaves through
loopend_fin, runs the finalizer, and goes back to the loop head. The head
evaluated the condition before it looked at stopFlags, so a return in the body
ran the condition once more - and when the condition is a real call, that call
clears the pending return on its way out, so the loop went on as if the return
never happened. A parser loop such as read_toml's spun forever on bad input in
the interpreter; JIT and AOT were fine. The head now checks stopFlags first.
def f(...) { return EXPR } -> def f(...) => EXPR (=> <- EXPR for a move),
written by the STYLE042 rule's --fix (PR #4213) over the tree and re-linted
per file. A body that does not fit in 100 columns takes the expression on the
line after =>; a single comment in the body moves to the end of the new line.
tests/daslib/ast_cursor_fixture.das becomes _ast_cursor_fixture.das - its
cursor positions are pinned by its test, so lint must leave it alone.
…ration

var a : T / var b : T -> var a, b : T, written by the STYLE043 rule's --fix
(PR #4213) over the tree and re-linted per file; five same-line pairs
(var a : T; var b : T) merged by hand.
var x : array<T> / let x <- ... inside a loop -> var inscope x, written by the
PERF033 rule's --fix (PR #4213) over the tree and re-linted per file; a whole
multi-variable declaration takes inscope when every variable in it is a
container the rule reports.
@borisbat
borisbat merged commit e66bb58 into master Oct 6, 2026
52 of 54 checks passed
@borisbat
borisbat deleted the lint-sweep-fixes branch October 6, 2026 22:52
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.

2 participants