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.
A
staticassignment at component level crashes the parser. This is valid Adobe ColdFusion 2021 and Lucee.Reproduction
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 parseand no lint results at all for that file.Cause
staticis afunctionModifier, andfunctionDeclarationstartsfunctionModifier* accessType? functionModifier* typeSpec? FUNCTION identifier. Prediction commits tofunctionDeclarationon seeingstatic, then noFUNCTIONtoken arrives. The visitor callsctx.FUNCTION().getSymbol()unconditionally and dereferences null.This is the failure mode
CLAUDE.mddescribes 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:
finalworksfinalwas given a path throughlocalAssignmentExpressionin #46/#52.staticnever got the equivalent, despite sitting beside it in bothcomponentModifierandfunctionModifier. Worth checkingabstractin 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 —
finalat component level, andSome::myVarstatic references — and two do not. Reproduced directly againstCFMLParser.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
staticblock. 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/— perCLAUDE.md, a regression case here is usually just dropping in a.cfc. Coverstatic x = 1;at component level andfinalbeside 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.