You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Correction (2026-09-11): the premise below is wrong — Groovy::Superis emitted, in a wildcard type bound (? super T), where it correctly bills as an operator. Both proposed options would break something. See the correction comment for the measurement and the right fix (a Java-style parent gate).
Summary
big-code-analysis-ast/src/getter/groovy.rs:137 lists Super in the
operator arm of get_op_type. The Groovy grammar never emits Groovy::Super (id 74) for a super reference — it emits a plain identifier (id 1) — so the arm is dead, and super is classified as an operand via the identifier path.
That happens to be the right answer since #1380, which made a
super-reference an operand in Java, C# and Kotlin to match the eleven
other languages. But it is right by accident: the only thing keeping
Groovy on the correct side is that its grammar does not produce the kind
the arm names. A grammar bump that starts emitting a dedicated super
node would silently flip Groovy to operator and reintroduce exactly the
inconsistency #1380 closed — with no test failing, because no fixture
exercises a kind that is currently unreachable.
Measured
At the pinned grammars, on the integration branch carrying the #1380 fix:
$ bca dump --no-config -p g.groovy | rg ': super' │ │ ├─ {identifier:1} from (2, 9) to (2, 14) : super │ │ │ ├─ {identifier:1} from (3, 13) to (3, 18) : super
$ bca ops --no-config -p g.groovy |- operators | |- () |- . |- def |- extends |- {} |- operands | |- 1 |- A |- B |- g |- h `- super
Both the constructor-delegation spelling and the qualified-access
spelling reach identifier. A.super.h() and super::h were checked
too, with the same result.
The enum variant exists (language_groovy.rs:88Super = 74, rendered
at :404), so the arm compiles and reads as live.
Why this is the §2 case
.claude/rules/grammar-dispatch.md §2 covers the shape: a variant that
exists in the enum but the parser never emits. Its prescription is to
keep the defensive arm and pin its unreachability with an assertion
naming the rule, "otherwise a future grammar that promotes the rule
changes behaviour invisibly". That is precisely the risk here, and there
is no such pin.
Options
Pin it. Keep the arm and add !ast_has_kind_id(&parser, Groovy::Super as u16) asserting the kind is
unreachable, so a grammar bump that promotes it fails loudly instead of
silently reclassifying.
Option 2 plus option 1's assertion is probably right: agreement between
the two paths, and a loud failure if the grammar changes.
Check This at the same time — Groovy's this is also a plain identifier and the same question applies.
Found while fixing #1380, which moved This / Super / Base to the
operand arm in Java, C# and Kotlin. Not fixed there because Groovy needed
no behaviour change and a dead-arm cleanup is a different claim from a
reclassification.
Resolution
Fixed in 7f2a1feb (branch fix/issue-1419, integration branch fix/batch-2026-09-14).
Super is pulled out of the ungated operator alternation in big-code-analysis-ast/src/getter/groovy.rs and given Java's parent
gate:
Groovy::Wildcard = 233 has no numeric-suffix alias. The gate uses Ancestors::parent_has_kind — the same O(1) chain lookup Java's ancestors.parent(node).map(...) performs, and the spelling the SLASH slashy-string guard in the same match already uses.
No metric moves. The corrected premise in the comments above is
confirmed against the grammar source: dekobon-tree-sitter-groovy 0.2.2
spells 'super' only inside the wildcard production, so every
super-reference was already a plain identifier and already an operand.
The fix is forward compatibility — it removes the grammar accident that
a bump routing a reference to kind 74 would have turned into a
misclassification. The workspace suite is green with no
integration-snapshot drift.
Tests.groovy_wildcard_super_bound_stays_an_operator gains a
reference-direction assertion over all five super-reference spellings;
its bound half now exercises the gate's operator branch and was verified
by inverting the gate (perturbation, not a file-level revert). The
gate's operand branch is unreachable under the pin — only error recovery
on invalid Groovy (List<? super>) detaches the token from its wildcard — so it is left untested per grammar-dispatch section 6,
with the existing node census as the drift marker.
Side effect..rustfmt-bail-baseline.txt ratcheted for getter/groovy.rs 5 → 7: two more arms in a match that already bails on
pre-existing in-pattern section comments, exactly as the same split did
for Java.
Summary
big-code-analysis-ast/src/getter/groovy.rs:137listsSuperin theoperator arm of
get_op_type. The Groovy grammar never emitsGroovy::Super(id 74) for asuperreference — it emits a plainidentifier(id 1) — so the arm is dead, andsuperis classified as anoperand via the identifier path.
That happens to be the right answer since #1380, which made a
super-reference an operand in Java, C# and Kotlin to match the eleven
other languages. But it is right by accident: the only thing keeping
Groovy on the correct side is that its grammar does not produce the kind
the arm names. A grammar bump that starts emitting a dedicated
supernode would silently flip Groovy to operator and reintroduce exactly the
inconsistency #1380 closed — with no test failing, because no fixture
exercises a kind that is currently unreachable.
Measured
At the pinned grammars, on the integration branch carrying the #1380 fix:
Both the constructor-delegation spelling and the qualified-access
spelling reach
identifier.A.super.h()andsuper::hwere checkedtoo, with the same result.
The enum variant exists (
language_groovy.rs:88Super = 74, renderedat
:404), so the arm compiles and reads as live.Why this is the §2 case
.claude/rules/grammar-dispatch.md§2 covers the shape: a variant thatexists in the enum but the parser never emits. Its prescription is to
keep the defensive arm and pin its unreachability with an assertion
naming the rule, "otherwise a future grammar that promotes the rule
changes behaviour invisibly". That is precisely the risk here, and there
is no such pin.
Options
!ast_has_kind_id(&parser, Groovy::Super as u16)asserting the kind isunreachable, so a grammar bump that promotes it fails loudly instead of
silently reclassifying.
move
Superto the operand arm so the defensive path agrees with thelive path — the answer fix(getter): self- and super-references split three ways across languages #1380 settled for every other language.
Option 2 plus option 1's assertion is probably right: agreement between
the two paths, and a loud failure if the grammar changes.
Check
Thisat the same time — Groovy'sthisis also a plainidentifierand the same question applies.Found while fixing #1380, which moved
This/Super/Baseto theoperand arm in Java, C# and Kotlin. Not fixed there because Groovy needed
no behaviour change and a dead-arm cleanup is a different claim from a
reclassification.
Resolution
Fixed in
7f2a1feb(branchfix/issue-1419, integration branchfix/batch-2026-09-14).Superis pulled out of the ungated operator alternation inbig-code-analysis-ast/src/getter/groovy.rsand given Java's parentgate:
Groovy::Wildcard = 233has no numeric-suffix alias. The gate usesAncestors::parent_has_kind— the same O(1) chain lookup Java'sancestors.parent(node).map(...)performs, and the spelling theSLASHslashy-string guard in the same match already uses.No metric moves. The corrected premise in the comments above is
confirmed against the grammar source:
dekobon-tree-sitter-groovy 0.2.2spells
'super'only inside thewildcardproduction, so everysuper-reference was already a plain
identifierand already an operand.The fix is forward compatibility — it removes the grammar accident that
a bump routing a reference to kind 74 would have turned into a
misclassification. The workspace suite is green with no
integration-snapshot drift.
Tests.
groovy_wildcard_super_bound_stays_an_operatorgains areference-direction assertion over all five super-reference spellings;
its bound half now exercises the gate's operator branch and was verified
by inverting the gate (perturbation, not a file-level revert). The
gate's operand branch is unreachable under the pin — only error recovery
on invalid Groovy (
List<? super>) detaches the token from itswildcard— so it is left untested per grammar-dispatch section 6,with the existing node census as the drift marker.
Side effect.
.rustfmt-bail-baseline.txtratcheted forgetter/groovy.rs5 → 7: two more arms in a match that already bails onpre-existing in-pattern section comments, exactly as the same split did
for Java.
make pre-commit→BCA_GATE: pass.