Skip to content

fix(getter): Groovy var, Java string templates, Perl __END__ (#1704, #1703, #1644) - #1707

Merged
dekobon merged 5 commits into
mainfrom
fix/batch-2026-10-23
Oct 24, 2026
Merged

dekobon merged 5 commits into
mainfrom
fix/batch-2026-10-23

Conversation

@dekobon

@dekobon dekobon commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Fixes #1704, #1703, #1644. Measured and declined #1604 (closed won't-fix with the survey). Files #1706 as the follow-up the Groovy fix uncovered.

Three Halstead classifier fixes written against the post-#1705 role tables, one commit each, plus the consolidated CHANGELOG and a review follow-up commit.

What

#1704 — Groovy var bills as a declaration keyword (955da8d5). The Var leaf of a local_variable_declaration was in no operator arm. It joins the DeclKeyword row — gated on its parent, as trait and record are, because the issue's one-line fix would have billed var = 4 / var++: those fail into an ERROR holding the same leaf (found by review, pinned by control rows). The type_identifier-spelled var of var field = 1 / final var q still bills as a type-name operand; that is #1706, anchored with a FIXME.

#1703 — Java string template wrapper unbilled (7f15a5b7). StringLiteral => string_operand_type(node, roles::java::string_lit::WRAPS) — the issue comment's option 2, the interpolation set read from the row rather than restated. Both FIXME(#1703)s removed. Text-block templates covered.

#1644 — Perl __END__ section bills nothing, __FILE__ bills once (ca68dfc4). The shape differs from the one the issue comment prescribed, on purpose: the leaf is the keeper, not the wrapper. __SUB__->(1) emits the bare __SUB__ token with no special_literal around it (verified with bca dump), so gating the wrapper and dropping the leaves would score it zero (grammar-dispatch §6). Dropping the wrapper instead needs no gate, makes __END__ / __DATA__ / data_not_for_compiler fall out unbilled, and fixes a second defect the issue did not name — every wrapped constant was billed twice. tokens is unchanged and now pinned.

#1604 — surveyed 2,314 system Perl files: zero value-position -bareword with a sub of that name in the same file, ~0.03 occurrences per file, and the "visible sub" approximation needs a per-parse symbol table inside the #1571 workaround. Closed as not planned with the table in the issue.

Verification

  • make pre-commit → BCA_GATE: pass; check-test-lang-gates-compare COMPARE_REF=main OK; role census / coverage / bypass OK (no baseline entries added; Var moved from excludes to owns).
  • Test-via-revert on each fix fails exactly its new test; a scripted sweep with the count assertions stripped confirms the vocabulary (Role) assertions discriminate on their own; the Groovy gate perturbed to ungated fails exactly the assigned var row.
  • INSTA_FORCE_PASS=1 corpus + parity run: 0 pending snapshots. The corpora snapshot no Groovy or Perl, and no corpus Java file spells a template.
  • Patch coverage 100 % (45/45 instrumented changed lines, cargo llvm-cov on a clean target); project line coverage 97.38 %, unchanged.
  • Performance: measured at noise for Groovy and Java (one O(1) parent read; one O(direct-children) scan per string literal, the cost every other interpolating language already pays); Perl removes an O(section) hash of the whole data section (main keyed a 5 MB operand).

Reviews

Per fix: simplify-rust, rust-optimize, review, audit-tests, and astra-review with an explicit algorithmic-performance question. Over the branch: a performance-only agent, the review skill, code-review max, a gap review targeted at what the first three missed, and astra-review of the follow-up commit (44184ad7). Findings and what became of them are in the follow-up commit's message; the one astra finding on that commit (two () undercounts in derivation comments) is amended in.

Groovy's `var` is the keyword leaf of a `local_variable_declaration`,
the same shape as `def` and as Kotlin's `var`, but no arm ever billed
it: `def f() { var x = 1 }` scored n1 4 where `def f() { def x = 1 }`
scored 5. The `DeclKeyword` row in `roles/groovy.rs` now owns `Var`,
gated on its parent as `trait` and `record` are, because a statement
that opens with `var` as a name (`var = 4`, `var++`) fails into an
`ERROR` holding the same leaf and must keep billing nothing.

The `type_identifier` the grammar reads for a field's `var field = 1`
and for `final var q` is a separate kind and is unchanged; a `var` used
as a name elsewhere is an `identifier` and stays an operand.

The only Groovy in the corpora is DeepSpeech's four `.gradle` files,
which the integration suite does not snapshot and which spell no `var`,
so no snapshot moves.

Fixes #1704
A Java 21 string template (`STR."Hi \{name}!"`) is a `string_literal`
holding `string_interpolation` children, whose expressions the walk
bills on their own. `getter/java.rs` listed `StringLiteral` as an
unconditional operand and never called `Getter::string_operand_type`,
the #420 helper every other interpolating language reaches, so the
wrapper's text and the `name` inside it were both operands: two for
one.

The operand arm now calls the helper with the `StringLit` row's `wraps`
set from `roles/java.rs`, the one place the interpolation kind is
spelled, and the two `FIXME(#1703)`s on that row go with it. A plain
literal has no such child and stays one operand; a template with no
interpolation bills its text as before.

No corpus Java file spells a template, so no integration snapshot
moves.

Fixes #1703
tree-sitter-perl holds two different things in one `special_literal`
node: a compile-time constant (`__FILE__`, `__LINE__`, `__PACKAGE__`,
`__SUB__`, each a keyword leaf) and the program terminator (`__END__`
or `__DATA__`, followed by a `data_not_for_compiler` leaf holding every
byte after it). The Perl getter billed the wrapper by text as well as
the four leaves, so a data section was one Halstead operand spelling
its whole contents — two files differing only after `__END__` scored
differently — and every constant billed twice, as the wrapper and as
the keyword inside it.

The wrapper leaves the operand arm; the keyword leaf is the keeper, as
grammar-dispatch §6 asks, because it is the node present in every
spelling: `__SUB__->(1)` emits the bare token under
`call_expression_recursive` with no wrapper at all. `__END__`,
`__DATA__` and the section after them end the program and bill
nothing. `tokens` is unchanged: the marker and the data leaf are
leaves, and it counts them as before.

Perl is not sampled by the integration corpus, so no snapshot moves.
Ids 8, 9, 212 and 220 stay in `.role-coverage-baseline.txt` until R08
declares Perl's operand rows.

Fixes #1644
Update CHANGELOG.md with entries from the three fixes on this branch
(#1704, #1703, #1644).
Findings from the `review`, `code-review max` and gap-review passes
over the branch, each applied where it stood:

- `roles/groovy.rs`: the `type_identifier`-spelled `var` (`var field`,
  `final var`) still bills as a type-name operand — the reading Java's
  getter rejects for its own `var`. Anchored with `FIXME(#1706)` on the
  row, and said plainly in the CHANGELOG and the book instead of
  "bills as it did".
- `halstead.rs`: the #1703 rows gain the text-block template
  (`STR."""…\{x}…"""`), the #1704 rows gain `var` in a C-style `for`
  init and a for-in header (the latter under the grammar's `ERROR`);
  each test now says which perturbation it cannot observe — the Groovy
  gate's narrowness, and direct-child versus recursive `wraps_any` —
  since no valid spelling tells them apart.
- `tokens.rs`: the claim that `tokens` still counts the `__END__`
  marker and the data leaf is pinned (`1;\n__END__\naaa\n` is 4, two
  more than `1;\n`).
- `.rustfmt-bail-baseline.txt`: the `getter/java.rs` 6 -> 7 increase
  is recorded in the header's ledger beside the #1380 one.
- `abc.rs`, `lessons_learned.md`: `special_literal` is also the
  `__END__` wrapper; lesson #420's "a new language gets the skip for
  free" now notes Java reached it only in #1703.
@dekobon dekobon added the bug Something isn't working label Oct 24, 2026
@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.30%. Comparing base (bdd70eb) to head (44184ad).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1707   +/-   ##
=======================================
  Coverage   98.30%   98.30%           
=======================================
  Files         380      380           
  Lines      110026   110089   +63     
  Branches   109591   109654   +63     
=======================================
+ Hits       108159   108222   +63     
  Misses       1202     1202           
  Partials      665      665           
Flag Coverage Δ
python 100.00% <ø> (ø)
rust 98.29% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
big-code-analysis-ast/src/getter/java.rs 98.33% <100.00%> (+0.02%) ⬆️
big-code-analysis-ast/src/getter/perl.rs 100.00% <ø> (ø)
big-code-analysis-ast/src/roles/groovy.rs 100.00% <ø> (ø)
big-code-analysis-ast/src/roles/java.rs 100.00% <ø> (ø)
src/metrics/abc.rs 99.73% <ø> (ø)
src/metrics/halstead.rs 99.31% <100.00%> (+<0.01%) ⬆️
src/metrics/tokens.rs 99.41% <100.00%> (+0.01%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dekobon
dekobon merged commit 44184ad into main Oct 24, 2026
54 checks passed
@dekobon
dekobon deleted the fix/batch-2026-10-23 branch October 24, 2026 05:04

This branch was successfully deployed

1 active deployment
github-pages — 44184ad7 Deployed Oct 24, 2026 by dekobon via deploy to GitHub Pages #855
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(getter/groovy): bill var as a declaration keyword

1 participant