Skip to content

Remove the unreachable VAR (FINAL | STATIC) alternative #68

Description

@ghedwards

localAssignmentExpression declares a VAR (FINAL | STATIC) alternative that can never match. It should be deleted rather than repaired.

Rewritten. This issue originally proposed making var final x = 1; parse. Checking whether that form is valid CFML turned up evidence that it probably is not, and that var final already means something else. The recommendation is now removal. The original framing is preserved at the end.

The dead alternative

localAssignmentExpression
  : (VAR | (FINAL | STATIC) VAR? | VAR (FINAL | STATIC)) left=startExpression ...

FINAL and STATIC are both carried in the identifier rule — added in #46 so that code using them as ordinary names keeps working. So for var final x, the plain VAR alternative matches with final as the identifier, prediction commits, and x is then unexpected:

var final x = 1;   ->  { var final; x = 1; }   no viable alternative at input 'x'
var static x = 1;  ->  { var static; x = 1; }  no viable alternative at input 'x'

No input can reach the third alternative. It is unreachable for any program.

Why removal rather than repair

var final already has a meaning, and cfparser gets it right:

final = getValue();       ->  final = getValue()
var final = getValue();   ->  var final = getValue()
static = getValue();      ->  static = getValue()
var static = getValue();  ->  var static = getValue()

That is not incidental. tree-sitter-cfml's corpus tests exactly these under a case named "keywords as variable names", with the expected tree showing left: (identifier)var final = getValue(); declares a local named final.

Making var final x = 1; parse would mean resolving that ambiguity in favour of a form with no evidence behind it, at the expense of one that is documented and tested. Removal is safe under either answer: if the form is not valid CFML, deleting it is correct; if it turns out to be valid, deleting it changes nothing today, because the alternative is already unreachable.

What I could and could not establish

final x = 1; is not in doubt — Lucee's immutable local, and the reason FINAL was added to this rule in 292ba9c. That form works and should stay.

var final x = 1;, var static x = 1; and static final x = 1; are unestablished. Sources checked:

Source Result
tree-sitter-cfml corpus contradicts it — tests var final = getValue() as a variable named final
tree-sitter-cfml grammar.js models no final/static modifier at all
Shipped dictionaries (cf9cf11, lucee5) no final/static entries
CFLint's real-world test corpus no usage of any of the three forms
cfdocs (foundeo/cfdocs) cannot answer — 806 functions, 170 tags, 40 listings, 1 component. No language keywords at all: no var, abstract, component, new or import either. Its silence carries no information.
cfdocs.org, Adobe and Lucee docs unreachable from this environment (egress-blocked)

The one source that would settle it is Lucee's language documentation, which I could not reach. If someone can check var final x = 1; against a live Lucee, that answers it directly.

Provenance

VAR FINAL came in with 292ba9c, whose commit message says final "takes VAR's shape and may combine with it" — an assertion, with no corpus case behind it. VAR STATIC was added in #65 mirroring it, so that half is mine and inherited the same defect. Neither has ever matched anything.

Also here

static final x = 1; fails too, but differently — both modifiers together are not declared in the rule at all, so it is a gap rather than a dead alternative. Same lack of evidence. Noted here rather than filed separately, since it is the same line of the same rule.

Suggested fixture

cfml.parsing/src/test/resources/cfml/tests/expressions/. Worth pinning the keyword-as-name forms — var final = getValue(); and friends — since those are the documented behaviour and the thing a future change to this rule could quietly break. Read the decompiled output rather than trusting a green parse.

Original framing, before the evidence check

Filed as: "var final x = 1; is declared in the grammar but unreachable", proposing that the grammar advertises a form it does not support and that the fix was to make it parse. That reading assumed var final x = 1; was valid CFML. It may not be, and var final already parses as something else, so the proposed fix risked breaking a documented case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions