Skip to content

refactor(parser): simplify arrow start tracking#17838

Merged
nicolo-ribaudo merged 5 commits intobabel:mainfrom
JLHwung:simplify-arrow-start-tracking
Mar 2, 2026
Merged

refactor(parser): simplify arrow start tracking#17838
nicolo-ribaudo merged 5 commits intobabel:mainfrom
JLHwung:simplify-arrow-start-tracking

Conversation

@JLHwung
Copy link
Copy Markdown
Contributor

@JLHwung JLHwung commented Feb 27, 2026

Q                       A
Fixed Issues?
Patch: Bug Fix?
Major: Breaking Change?
Minor: New Feature?
Tests Added + Pass? Yes
Documentation PR Link
Any Dependency Changes?
License MIT

In this PR we simplify the tokenizer state potentialArrowAt (backed by 4-byte SMI in V8 in most cases1) to a boolean bit canStartArrow. The boolean bit is set to true in the start of parseMaybeAssign and FSharpPipelinebody and set to false when

  1. A prefix operator has been consumed in UnaryExpression
  2. A binary operator has been consumed in BinaryExpression
  3. we are parsing LeftHandSideExpression in a class heritage
  4. we are parsing LeftHandSideExpression in legacy decorator

The reason we have to track canStartArrow is that arrow function can only be formed in the start of the AssignmentExpression production. However because of its ambiguity with ParenthesizedExpression, a production that we handle in parseExprAtom, we can not know if this is an arrow function until a => is seen after a ParenthesizedExpression in parseExprAtom. Therefore, we should set canStartArrow to false to any sub production within parseMaybeAssign that satisfies the following conditions:

  1. It has consumed at least one token
  2. It may call parseExprAtom without calling parseMaybeAssign first.

We also added more test cases where an arrow function is formed in the non-leading positions of an assignment expression.

Context: This PR is inspired from the F# pipeline body parsing, where we unconditionally set potentialArrowAt without ever testing if it starts with a left paren or an identifier like we did in maybeAssign. At first I thought there may be a potential bug but I cannot construct an edge case that would otherwise require the token test. Then I realized the detection is redundant, there will be no side effect if potentialArrowAt is recorded in for example, function expression as long as it still records the position of the start of the current assignment expression. And that ultimately leads to the simplification of the tokenizer state to a boolean flag.

Footnotes

  1. Unless the code length is over 2**31 or the parser is invoked with a huge startIndex option.

@babel-bot
Copy link
Copy Markdown
Collaborator

babel-bot commented Feb 27, 2026

Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/61080

@nicolo-ribaudo
Copy link
Copy Markdown
Member

The test262 failures seem to be related. Example:

var C = class extends () => {} {
  
};

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 27, 2026

Open in StackBlitz

commit: c64be0f

@JLHwung
Copy link
Copy Markdown
Contributor Author

JLHwung commented Feb 27, 2026

The test262 failures seem to be related. Example:

var C = class extends () => {} {
  
};

Yes, I am working on a fix and I just found another Babel parser bug affecting both 7 and 8. We forgot to check the canBeArrow state when parsing async single name binding arrow, all of the following cases should be rejected but we currently allow:

typeof async x => {}
0 + async x => {}
class C extends async x => {} {}
// decorator-legacy
var C = class { @async x => {} method () {} }

I will work on a bugfix first and will adapt to the bugfix in this PR.

@JLHwung JLHwung force-pushed the simplify-arrow-start-tracking branch from 7c1e995 to f1d72ce Compare February 27, 2026 20:41
Copy link
Copy Markdown
Member

@nicolo-ribaudo nicolo-ribaudo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@nicolo-ribaudo nicolo-ribaudo merged commit 2391ce6 into babel:main Mar 2, 2026
55 checks passed
@nicolo-ribaudo nicolo-ribaudo deleted the simplify-arrow-start-tracking branch March 2, 2026 22:00
@nicolo-ribaudo nicolo-ribaudo added PR: Internal (next major) 🏠 A type of pull request used for our changelog categories for next major release PR: Internal 🏠 A type of pull request used for our changelog categories labels Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: parser PR: Internal 🏠 A type of pull request used for our changelog categories PR: Internal (next major) 🏠 A type of pull request used for our changelog categories for next major release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants