Skip to content

Let final and abstract be used as variable names - #52

Merged
ghedwards merged 1 commit into
masterfrom
claude/fix-final-abstract-names-46
Aug 13, 2026
Merged

Let final and abstract be used as variable names#52
ghedwards merged 1 commit into
masterfrom
claude/fix-final-abstract-names-46

Conversation

@ghedwards

Copy link
Copy Markdown

Closes #46.

Every other modifier keyword already worked as an ordinary name — public, private, package, remote, component, interface, static — because the identifier rule lists them. FINAL and ABSTRACT were missing:

final = 3;      // no viable alternative
abstract = 4;   // no viable alternative

Same shape as the three recorded in CLAUDE.md, and the same fix: add them to identifier alongside default, var, to, include, new.

The part worth reviewing

FINAL now serves double duty. final x = 1; is Lucee's immutable local, added in #34, so the token both starts a declaration and is a name. A green parse proves nothing here — the question is whether the two still build different trees:

input decompiles as meaning
final = 3; final = 3 assignment to a variable called final
final x = 1; final x = 1 immutable local declaration
var final = 1; var final = 1 local declaration of a variable called final
final var y = 2; final y = 2 immutable local

All four distinct, and the fixture pins them together so a later change can't quietly collapse one into another. The modifier positions (abstract component {}, final function f() {}) are unaffected.

One cosmetic note: final var y = 2 decompiles as final y = 2, dropping the var. Both spell the same thing and CFVarDeclExpression carries a single flag rather than tracking both keywords — pre-existing from #34, not introduced here.

Verification

  • 313 cfparser tests, ./gradlew build
  • Differential harness 8 → 6 — both corpus cases that cover this (identifier-capable keywords still work as identifiers and keywords as variable names), nothing newly broken
  • CFLint 675 against a clean build
  • Fixture fails with cfml.parsing/src/main stashed

Generated by Claude Code

Every other modifier keyword already worked as an ordinary name -- public,
private, package, remote, component, interface, static -- because the identifier
rule lists them. FINAL and ABSTRACT were missing, so `final = 3;` and
`abstract = 4;` were syntax errors.

Same shape as the three already recorded in CLAUDE.md, and the fix is the same:
add them to identifier alongside default, var, to, include, new.

The one that needed checking is the pair FINAL now serves. `final x = 1;` is
Lucee's immutable local, added in #34, so FINAL both starts a declaration and is
a name. Both parse and, more to the point, build different trees: `final = 3;`
decompiles as an assignment to a variable called final, `final x = 1;` as a
declaration. `var final = 1;` and `final var y = 2;` are covered too, and the
fixture pins all four together so a later change cannot collapse them.

Differential harness 8 disagreements to 6 -- both corpus cases for this, and
nothing newly broken. Verified end to end -- 313 cfparser tests, ./gradlew
build, CFLint's 675.

Closes #46

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GzpZFd4rnE1Yi2sVHAji35
@ghedwards
ghedwards merged commit fce3a9c 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.

final and abstract cannot be used as variable names

2 participants