Skip to content

Accept an access modifier on a static block member, and keep the block - #66

Merged
ghedwards merged 1 commit into
masterfrom
claude/fix-static-block-modifier-64
Aug 13, 2026
Merged

Accept an access modifier on a static block member, and keep the block#66
ghedwards merged 1 commit into
masterfrom
claude/fix-static-block-modifier-64

Conversation

@ghedwards

Copy link
Copy Markdown

Closes #64.

component { static { public myVar = "v"; } }
no viable alternative at input 'publicmyVar='

staticBlock took a bare statement, and an accessType is not part of one. The block itself was fine — static { myVar = "v"; } parsed — so only members carrying public, private, remote or package were rejected. All four are legal there in ACF.

A staticMember rule now carries the optional accessType, putting it on the member rather than the block, which is where CFML puts it.

The block was being thrown away

This is the part worth reviewing. staticBlock had no visitor at all, so ANTLR's default visitChildren flattened it:

before:  component { static { myVar = "v"; } }  ->  component { myVar = 'v' }
after:   component { static { myVar = "v"; } }  ->  component { static { myVar = 'v'; } }

The block vanished and its members leaked out as ordinary component members — the static-ness silently discarded.

I fixed it here rather than filing it separately because doing only the grammar half would have made things worse: adding accessType to a rule the visitor ignores means the modifier gets folded into whatever is nearby. That is precisely the failure CLAUDE.md documents for arrow functions in #16 and array slicing in #18.

So CFStaticBlockStatement models the block, holds its members in source order for decomposeScript(), and records each member's access type positionally.

No fixture covered static blocks

Searching the corpus found none — which is how a construct that parsed cleanly and then disappeared from the tree went unnoticed. The new fixture pins all three shapes:

static {
    public myPublic = "p";
    private myPrivate = "q";
    myPlain = "r";
}

decompiling to:

static {
  public myPublic = 'p';
  private myPrivate = 'q';
  myPlain = 'r';
};

Access types preserved where written, absent where not.

Verification

  • 323 tests, ./gradlew build
  • Differential harness unchanged at 1, nothing newly broken case-by-case
  • CFLint 675 against a clean build
  • components/static_block_modifier_64.cfc fails with cfml.parsing/src/main stashed

Relationship to #65

#65 fixes the other half of cfmleditor/CFLint#50 — the static x = 1; NullPointerException. Different rules (localAssignmentExpression vs staticBlock), no textual overlap, mergeable in either order. Both are needed before CFLint#50 can close.


Generated by Claude Code

component { static { public myVar = "v"; } } failed to parse:

  no viable alternative at input 'publicmyVar='

staticBlock took a bare statement, and an accessType is not part of one. The
block itself was fine -- static { myVar = "v"; } parsed -- so only members
carrying public, private, remote or package were rejected. All four are legal
there in ACF.

A staticMember rule now carries the optional accessType, which puts it on the
member rather than the block, where CFML puts it.

## The block was being thrown away

Fixed here because adding the modifier without it would have made things
worse. staticBlock had no visitor at all, so ANTLR's default visitChildren
flattened it:

  before:  component { static { myVar = "v"; } }  ->  component { myVar = 'v' }
  after:   component { static { myVar = "v"; } }  ->  component { static { myVar = 'v'; } }

Adding accessType to a rule the visitor ignores would have folded the modifier
into whatever was nearby -- the exact failure CLAUDE.md describes for arrow
functions in #16 and array slicing in #18. So CFStaticBlockStatement models
the block, holds its members in source order for decomposeScript, and records
each member's access type positionally.

No fixture covered a CFML static block before this, which is how a construct
that parsed and then vanished went unnoticed.

323 tests, ./gradlew build, differential harness unchanged at 1 with nothing
newly broken, CFLint's 675 against a clean build. The new fixture fails with
cfml.parsing/src/main stashed.

Closes #64
@ghedwards
ghedwards merged commit 6dde65d into master Aug 13, 2026
3 checks passed
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.

Access modifier on a member inside a static block is rejected

2 participants