Skip to content

Component-level static x = 1; throws NullPointerException #63

Description

@ghedwards

A static assignment at component level crashes the parser. This is valid Adobe ColdFusion 2021 and Lucee.

Reproduction

new CFMLParser().parseScript("component { static myStatic = \"v\"; }");
java.lang.NullPointerException: Cannot invoke
  "org.antlr.v4.runtime.tree.TerminalNode.getSymbol()"
  because the return value of "cfml.CFSCRIPTParser$FunctionDeclarationContext.FUNCTION()" is null

It throws rather than reporting an error, so it takes the whole file with it. Via CFLint the same source yields PARSE_ERROR: Unable to parse and no lint results at all for that file.

Cause

static is a functionModifier, and functionDeclaration starts functionModifier* accessType? functionModifier* typeSpec? FUNCTION identifier. Prediction commits to functionDeclaration on seeing static, then no FUNCTION token arrives. The visitor calls ctx.FUNCTION().getSymbol() unconditionally and dereferences null.

This is the failure mode CLAUDE.md describes under Changing the grammar — the grammar admits a shape the visitor was never taught — with the sharper edge that it crashes rather than quietly producing a wrong tree.

Contrast: final works

component { final myConst = "v"; }   ->  OK, decompiles to: component { final myConst = 'v' }
component { static myStatic = "v"; } ->  NullPointerException

final was given a path through localAssignmentExpression in #46/#52. static never got the equivalent, despite sitting beside it in both componentModifier and functionModifier. Worth checking abstract in the same position while fixing this.

Where this came from

cfmleditor/CFLint#50, reported against Adobe ColdFusion 2021. That report has four cases; two now pass — final at component level, and Some::myVar static references — and two do not. Reproduced directly against CFMLParser.parseScript, not through CFLint, so it is a parser fault rather than a linter one.

#64 covers the other failing case from that report, an access modifier inside a static block. Separate faults in adjacent rules, fixable independently, but whoever takes one should read the other.

Suggested fixture

cfml.parsing/src/test/resources/cfml/tests/components/ — per CLAUDE.md, a regression case here is usually just dropping in a .cfc. Cover static x = 1; at component level and final beside it so the working case stays working.

Worth decompiling the result and reading it rather than trusting a green parse: this rule already demonstrates that parsing and building a correct AST are separate questions.

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