Skip to content

Stop the tag scanner ending a tag inside an arrow operator - #50

Merged
ghedwards merged 1 commit into
masterfrom
claude/fix-cfset-lambda-44
Aug 13, 2026
Merged

Stop the tag scanner ending a tag inside an arrow operator#50
ghedwards merged 1 commit into
masterfrom
claude/fix-cfset-lambda-44

Conversation

@ghedwards

Copy link
Copy Markdown

Closes #44.

My diagnosis in the issue was wrong

#44 says the expression rule likely commits before the arrow is seen. It doesn't — the grammar never gets the chance. CFMLStartTag.getEnd() returns at the first unquoted >, and the second character of => is one:

<cfset d = t => t.b()>       scanned as the tag  <cfset d = t =>
<cfset e = (x) -> x + 1>     scanned as the tag  <cfset e = (x) ->

The truncated text e = (x) = then parses to e = (x) — a well-formed expression the visitor accepts, with the body gone. That's why it was silent.

Fix

getEnd already special-cases quotes and the ---> of a comment. An arrow joins them.

The - case needs care: <cfset a = i--> is a decrement followed by the tag end, not an arrow. So - only counts when it doesn't follow another -. The fixture pins that alongside the arrows, alongside gt (where the > never appears) and a quoted subject="x=>y" (already handled by the quote tracking).

Fixture, not a JUnit case

This is the tag path, so TestTagFiles. Nothing under src/test/resources/tag exercised an arrow function, which is exactly why the suite stayed green. The recorded output shows each <cfset> capturing its whole expression and building a lambdaDeclaration:

nested = getURLs().map((target) -> target.toString())
countdown = i--
comparison = total gt 1

Reverting cfml.parsing/src/main fails it.

Verification

  • 313 cfparser tests (including the 26 TestGetStartTagEnd cases, which is what I most expected to break), ./gradlew build
  • Differential harness 8 → 7, nothing newly broken
  • CFLint 675 against a clean build — this changes how every CFML tag is scanned, so the downstream run mattered more than usual here

Generated by Claude Code

<cfset f = (x) => x + 1> was scanned as the tag `<cfset f = (x) =>`, because
CFMLStartTag.getEnd returns at the first unquoted '>' and the second character
of => is one. The truncated text `f = (x) =` then parsed to `f = (x)` -- an
expression the visitor accepted, with the body silently gone.

Nothing to do with the thin arrow or with lambdaDeclaration, both of which work:
`=>` failed identically, and had since long before either was touched. The
grammar was never reached with the whole expression.

getEnd already special-cases quotes and the ---> of a comment; an arrow joins
them. A '-' only counts when it does not follow another '-', so the decrement in
<cfset a = i--> still ends the tag, which the fixture pins alongside the arrows.

A TestTagFiles fixture rather than a JUnit case: this is the tag path, and
nothing under src/test/resources/tag exercised an arrow function, which is why
the suite was green.

Verified end to end -- 313 cfparser tests, ./gradlew build, CFLint's 675.
Differential harness 8 disagreements to 7.

Closes #44

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

No arrow function parses inside &lt;cfset&gt;, and the failure is silent

2 participants