From c8b30f05f824fcfac50d68644414a7c4957c3626 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 04:44:26 +0000 Subject: [PATCH 1/2] Add the script-syntax tags that had no token at all Cross-referenced cfmlFunction against the shipped dictionaries rather than guessing at what was missing: 118 tags across cf8/9/10/11, cfml7, railo and lucee, of which 60 had no token. 21 of those are control flow -- cfif, cftry, cfreturn, cfparam, cffunction and the rest -- and already have dedicated grammar rules. Adding them here would have been wrong, so they stay out. That leaves 39. Those 39 get their cf-prefixed spelling only. The bare names are the reason: dump, trace, map, xml, flush, timer, invoke, login and document are ordinary function and variable names in real code -- dump( ) alone appears in four fixtures in this repo -- and minting bare tokens for them would have reclassified every one of those calls as a tag. cfdump carries no such risk. That is a departure from the cf-prefix aliases added in #29, which map onto an existing bare token; where no safe bare token can exist, the prefixed spelling stands alone. imap is the exception: it is the one bare spelling the corpora actually use, so it gets both, and CFIMAP aliases onto it. Every new name is in the identifier rule too. That is the hazard this repo has hit three times, and adding the entry in the same change is what stops a fourth. The fixture pins it from both directions: the tags parse, and dump, trace, map, xml, flush, cfdump and imap all still work as names. Differential harness 8 disagreements to 7, nothing newly broken. Verified end to end -- 313 cfparser tests, ./gradlew build, CFLint's 675. Closes #47 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01GzpZFd4rnE1Yi2sVHAji35 --- .../src/main/antlr4/cfml/CFSCRIPTLexer.g4 | 46 ++ .../src/main/antlr4/cfml/CFSCRIPTParser.g4 | 79 ++++ .../cfml/tests/tagscript/tag_whitelist_47.cfc | 17 + .../tagscript/tag_whitelist_47.expected.txt | 434 ++++++++++++++++++ 4 files changed, 576 insertions(+) create mode 100644 cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.cfc create mode 100644 cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.expected.txt diff --git a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTLexer.g4 b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTLexer.g4 index c3e04e9..16b5e55 100644 --- a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTLexer.g4 +++ b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTLexer.g4 @@ -381,6 +381,52 @@ CFUPDATE: [cC][fF] UPDATE -> type(UPDATE); CFWDDX: [cC][fF] WDDX -> type(WDDX); CFZIP: [cC][fF] ZIP -> type(ZIP); +// Tags that exist only in their cf-prefixed spelling here. The bare names -- dump, trace, +// map, xml, flush -- are ordinary function and variable names in real code (dump( ) alone +// appears in four fixtures), so minting bare tokens for them would reclassify working code. +// The cf-prefixed form carries no such risk, and each is listed in identifier as well. +CFCALENDAR: [cC][fF][cC][aA][lL][eE][nN][dD][aA][rR]; +CFCHART: [cC][fF][cC][hH][aA][rR][tT]; +CFCHARTDATA: [cC][fF][cC][hH][aA][rR][tT][dD][aA][tT][aA]; +CFCHARTSERIES: [cC][fF][cC][hH][aA][rR][tT][sS][eE][rR][iI][eE][sS]; +CFCLIENT: [cC][fF][cC][lL][iI][eE][nN][tT]; +CFCLIENTSETTINGS: [cC][fF][cC][lL][iI][eE][nN][tT][sS][eE][tT][tT][iI][nN][gG][sS]; +CFDOCUMENT: [cC][fF][dD][oO][cC][uU][mM][eE][nN][tT]; +CFDOCUMENTITEM: [cC][fF][dD][oO][cC][uU][mM][eE][nN][tT][iI][tT][eE][mM]; +CFDOCUMENTSECTION: [cC][fF][dD][oO][cC][uU][mM][eE][nN][tT][sS][eE][cC][tT][iI][oO][nN]; +CFDUMP: [cC][fF][dD][uU][mM][pP]; +CFFILEUPLOAD: [cC][fF][fF][iI][lL][eE][uU][pP][lL][oO][aA][dD]; +CFFLUSH: [cC][fF][fF][lL][uU][sS][hH]; +CFFORMGROUP: [cC][fF][fF][oO][rR][mM][gG][rR][oO][uU][pP]; +CFFORMITEM: [cC][fF][fF][oO][rR][mM][iI][tT][eE][mM]; +CFHTMLTOPDF: [cC][fF][hH][tT][mM][lL][tT][oO][pP][dD][fF]; +CFHTMLTOPDFITEM: [cC][fF][hH][tT][mM][lL][tT][oO][pP][dD][fF][iI][tT][eE][mM]; +CFINVOKE: [cC][fF][iI][nN][vV][oO][kK][eE]; +CFINVOKEARGUMENT: [cC][fF][iI][nN][vV][oO][kK][eE][aA][rR][gG][uU][mM][eE][nN][tT]; +CFLOGIN: [cC][fF][lL][oO][gG][iI][nN]; +CFLOGINUSER: [cC][fF][lL][oO][gG][iI][nN][uU][sS][eE][rR]; +CFLOGOUT: [cC][fF][lL][oO][gG][oO][uU][tT]; +CFMAILPART: [cC][fF][mM][aA][iI][lL][pP][aA][rR][tT]; +CFMAP: [cC][fF][mM][aA][pP]; +CFMAPITEM: [cC][fF][mM][aA][pP][iI][tT][eE][mM]; +CFMEDIAPLAYER: [cC][fF][mM][eE][dD][iI][aA][pP][lL][aA][yY][eE][rR]; +CFMESSAGEBOX: [cC][fF][mM][eE][sS][sS][aA][gG][eE][bB][oO][xX]; +CFNTAUTHENTICATE: [cC][fF][nN][tT][aA][uU][tT][hH][eE][nN][tT][iI][cC][aA][tT][eE]; +CFOAUTH: [cC][fF][oO][aA][uU][tT][hH]; +CFOBJECTCACHE: [cC][fF][oO][bB][jJ][eE][cC][tT][cC][aA][cC][hH][eE]; +CFPROGRESSBAR: [cC][fF][pP][rR][oO][gG][rR][eE][sS][sS][bB][aA][rR]; +CFREPORTPARAM: [cC][fF][rR][eE][pP][oO][rR][tT][pP][aA][rR][aA][mM]; +CFSHAREPOINT: [cC][fF][sS][hH][aA][rR][eE][pP][oO][iI][nN][tT]; +CFSPREADSHEET: [cC][fF][sS][pP][rR][eE][aA][dD][sS][hH][eE][eE][tT]; +CFTEXTAREA: [cC][fF][tT][eE][xX][tT][aA][rR][eE][aA]; +CFTIMER: [cC][fF][tT][iI][mM][eE][rR]; +CFTRACE: [cC][fF][tT][rR][aA][cC][eE]; +CFWEBSOCKET: [cC][fF][wW][eE][bB][sS][oO][cC][kK][eE][tT]; +CFXML: [cC][fF][xX][mM][lL]; +// imap is the one bare spelling the corpora actually use, so it gets both. +IMAP: [iI][mM][aA][pP]; +CFIMAP: [cC][fF] IMAP -> type(IMAP); + CFCUSTOM_IDENTIFIER: [cC][fF]'_' IDENTIFIER+; IDENTIFIER diff --git a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 index cd5cbe6..c04fe4c 100644 --- a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 +++ b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 @@ -44,6 +44,46 @@ componentModifier functionModifier : STATIC | ABSTRACT + // the tag names above, so a variable may still be called cfdump or imap + | CFCALENDAR + | CFCHART + | CFCHARTDATA + | CFCHARTSERIES + | CFCLIENT + | CFCLIENTSETTINGS + | CFDOCUMENT + | CFDOCUMENTITEM + | CFDOCUMENTSECTION + | CFDUMP + | CFFILEUPLOAD + | CFFLUSH + | CFFORMGROUP + | CFFORMITEM + | CFHTMLTOPDF + | CFHTMLTOPDFITEM + | CFINVOKE + | CFINVOKEARGUMENT + | CFLOGIN + | CFLOGINUSER + | CFLOGOUT + | CFMAILPART + | CFMAP + | CFMAPITEM + | CFMEDIAPLAYER + | CFMESSAGEBOX + | CFNTAUTHENTICATE + | CFOAUTH + | CFOBJECTCACHE + | CFPROGRESSBAR + | CFREPORTPARAM + | CFSHAREPOINT + | CFSPREADSHEET + | CFTEXTAREA + | CFTIMER + | CFTRACE + | CFWEBSOCKET + | CFXML + | IMAP | FINAL ; @@ -371,6 +411,45 @@ cfmlFunction | UPDATE | WDDX | ZIP + | CFCALENDAR + | CFCHART + | CFCHARTDATA + | CFCHARTSERIES + | CFCLIENT + | CFCLIENTSETTINGS + | CFDOCUMENT + | CFDOCUMENTITEM + | CFDOCUMENTSECTION + | CFDUMP + | CFFILEUPLOAD + | CFFLUSH + | CFFORMGROUP + | CFFORMITEM + | CFHTMLTOPDF + | CFHTMLTOPDFITEM + | CFINVOKE + | CFINVOKEARGUMENT + | CFLOGIN + | CFLOGINUSER + | CFLOGOUT + | CFMAILPART + | CFMAP + | CFMAPITEM + | CFMEDIAPLAYER + | CFMESSAGEBOX + | CFNTAUTHENTICATE + | CFOAUTH + | CFOBJECTCACHE + | CFPROGRESSBAR + | CFREPORTPARAM + | CFSHAREPOINT + | CFSPREADSHEET + | CFTEXTAREA + | CFTIMER + | CFTRACE + | CFWEBSOCKET + | CFXML + | IMAP | CFCUSTOM_IDENTIFIER ; diff --git a/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.cfc b/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.cfc new file mode 100644 index 0000000..e70b99c --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.cfc @@ -0,0 +1,17 @@ +// Tags that had no token at all. imap is the one bare spelling the corpora use. +imap action="close" connection="testImap"; +cfimap action="open" server="mail.example.com"; +cfspreadsheet action="read" src="report.xls" query="data"; +cfdocument format="pdf" { + echo("body"); +} +// The bare names stay ordinary identifiers -- these are functions and variables in +// real code, and minting bare tokens for them would have reclassified all of it. +dump(results); +trace = 1; +map = {}; +xml = ""; +flush = false; +// ... and the cf-prefixed spellings are still usable as names too. +cfdump = 1; +imapSettings = imap; diff --git a/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.expected.txt b/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.expected.txt new file mode 100644 index 0000000..118c4ef --- /dev/null +++ b/cfml.parsing/src/test/resources/cfml/tests/tagscript/tag_whitelist_47.expected.txt @@ -0,0 +1,434 @@ +/*===TOKENS===*/ +Hidden:LINE_COMMENT +IMAP +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +IMAP +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +CFSPREADSHEET +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +QUERY +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +CFDOCUMENT +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +'{' <{> +Hidden:NEWLINE <> +IDENTIFIER +'(' <(> +OPEN_STRING <"> +STRING_LITERAL +CLOSE_STRING <"> +')' <)> +';' <;> +Hidden:NEWLINE <> +'}' <}> +Hidden:NEWLINE <> +Hidden:LINE_COMMENT +Hidden:LINE_COMMENT +IDENTIFIER +'(' <(> +IDENTIFIER +')' <)> +';' <;> +Hidden:NEWLINE <> +IDENTIFIER +'=' <=> +INTEGER_LITERAL <1> +';' <;> +Hidden:NEWLINE <> +IDENTIFIER +'=' <=> +'{' <{> +'}' <}> +';' <;> +Hidden:NEWLINE <> +IDENTIFIER +'=' <=> +OPEN_STRING <"> +STRING_LITERAL <> +CLOSE_STRING <"> +';' <;> +Hidden:NEWLINE <> +IDENTIFIER +'=' <=> +BOOLEAN_LITERAL +';' <;> +Hidden:NEWLINE <> +Hidden:LINE_COMMENT +CFDUMP +'=' <=> +INTEGER_LITERAL <1> +';' <;> +Hidden:NEWLINE <> +IDENTIFIER +'=' <=> +IMAP +';' <;> +Hidden:NEWLINE <> +/*===TREE===*/ +(scriptBlock + (element + (statement + (tagOperatorStatement + (cfmlfunctionStatement + (cfmlFunction imap) + (paramStatementAttributes + (param + (multipartIdentifier (identifier action)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart close) ")) + ) + ) + ) + ) + ) + (param + (multipartIdentifier (identifier connection)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart testImap) ")) + ) + ) + ) + ) + ) + ) + ; + ) + ) + ) + ) + (element + (statement + (tagOperatorStatement + (cfmlfunctionStatement + (cfmlFunction cfimap) + (paramStatementAttributes + (param + (multipartIdentifier (identifier action)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart open) ")) + ) + ) + ) + ) + ) + (param + (multipartIdentifier (identifier server)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart mail.example.com) ")) + ) + ) + ) + ) + ) + ) + ; + ) + ) + ) + ) + (element + (statement + (tagOperatorStatement + (cfmlfunctionStatement + (cfmlFunction cfspreadsheet) + (paramStatementAttributes + (param + (multipartIdentifier (identifier action)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart read) ")) + ) + ) + ) + ) + ) + (param + (multipartIdentifier (identifier src)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart report.xls) ")) + ) + ) + ) + ) + ) + (param + (multipartIdentifier (identifier (cfmlFunction query))) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart data) ")) + ) + ) + ) + ) + ) + ) + ; + ) + ) + ) + ) + (element + (statement + (tagOperatorStatement + (cfmlfunctionStatement + (cfmlFunction cfdocument) + (paramStatementAttributes + (param + (multipartIdentifier (identifier format)) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart pdf) ")) + ) + ) + ) + ) + ) + ) + (compoundStatement + { + (statement + (startExpression + (baseExpression + (unaryExpression + (memberExpression + (functionCall + (identifier echo) + ( + (argumentList + (argument + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart body) ")) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ; + ) + } + ) + ) + ) + ) + ) + (element + (statement + (startExpression + (baseExpression + (unaryExpression + (memberExpression + (functionCall + (identifier dump) + ( + (argumentList + (argument + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier results)))) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ; + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier trace)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 1)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier map)))) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (implicitStruct { })))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier xml)))) + ) + = + (startExpression + (baseExpression + (unaryExpression + (primaryExpression + (literalExpression (stringLiteral " (stringLiteralPart ) ")) + ) + ) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier flush)))) + ) + = + (startExpression + (baseExpression + (unaryExpression (primaryExpression (literalExpression false))) + ) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression + (unaryExpression (memberExpression (identifier (cfmlFunction cfdump)))) + ) + ) + = + (startExpression + (baseExpression (unaryExpression (primaryExpression (literalExpression 1)))) + ) + ) + (endOfStatement ;) + ) + ) + (element + (statement + (assignmentExpression + (startExpression + (baseExpression (unaryExpression (memberExpression (identifier imapSettings)))) + ) + = + (startExpression + (baseExpression + (unaryExpression (memberExpression (identifier (cfmlFunction imap)))) + ) + ) + ) + (endOfStatement ;) + ) + ) +) +/*======*/ +/*===DECOMPILE===*/ +{ +imap action='close' connection='testImap';; +cfimap action='open' server='mail.example.com';; +cfspreadsheet action='read' query='data' src='report.xls';; +cfdocument format='pdf'{ +echo('body'); + +}; +dump(results); +trace = 1; +map = {}; +xml = ''; +flush = false; +cfdump = 1; +imapSettings = imap; + +} +/*======*/ \ No newline at end of file From cedcb4a8c056186b5591a0ad3a90bcfc30af28bd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 05:02:43 +0000 Subject: [PATCH 2/2] Keep the tag-name whitelist out of functionModifier The tag names belong in cfmlFunction, which identifier already reaches, so a variable may still be called cfdump or imap. They were also inserted into functionModifier, which would have let any of them stand where static, abstract or final are expected in a function declaration. --- .../src/main/antlr4/cfml/CFSCRIPTParser.g4 | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 index c04fe4c..0c3a90a 100644 --- a/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 +++ b/cfml.parsing/src/main/antlr4/cfml/CFSCRIPTParser.g4 @@ -44,46 +44,6 @@ componentModifier functionModifier : STATIC | ABSTRACT - // the tag names above, so a variable may still be called cfdump or imap - | CFCALENDAR - | CFCHART - | CFCHARTDATA - | CFCHARTSERIES - | CFCLIENT - | CFCLIENTSETTINGS - | CFDOCUMENT - | CFDOCUMENTITEM - | CFDOCUMENTSECTION - | CFDUMP - | CFFILEUPLOAD - | CFFLUSH - | CFFORMGROUP - | CFFORMITEM - | CFHTMLTOPDF - | CFHTMLTOPDFITEM - | CFINVOKE - | CFINVOKEARGUMENT - | CFLOGIN - | CFLOGINUSER - | CFLOGOUT - | CFMAILPART - | CFMAP - | CFMAPITEM - | CFMEDIAPLAYER - | CFMESSAGEBOX - | CFNTAUTHENTICATE - | CFOAUTH - | CFOBJECTCACHE - | CFPROGRESSBAR - | CFREPORTPARAM - | CFSHAREPOINT - | CFSPREADSHEET - | CFTEXTAREA - | CFTIMER - | CFTRACE - | CFWEBSOCKET - | CFXML - | IMAP | FINAL ;