Skip to content

fix(parse/js): fix a case where valid js was being interpretted as ts#9550

Merged
dyc3 merged 1 commit intomainfrom
dyc3/fix-js-as-ts-missparse
Mar 19, 2026
Merged

fix(parse/js): fix a case where valid js was being interpretted as ts#9550
dyc3 merged 1 commit intomainfrom
dyc3/fix-js-as-ts-missparse

Conversation

@dyc3
Copy link
Contributor

@dyc3 dyc3 commented Mar 19, 2026

Summary

I used gpt 5.4 to find the bug and fix it. I can post the agent's rationale if needed, because the fix looks a bit odd at a glance.

fixes #9548

Test Plan

snapshots

Docs

@changeset-bot
Copy link

changeset-bot bot commented Mar 19, 2026

🦋 Changeset detected

Latest commit: 67ff502

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Parser Area: parser L-JavaScript Language: JavaScript and super languages labels Mar 19, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53139 53139 0
Passed 51919 51919 0
Failed 1178 1178 0
Panics 42 42 0
Coverage 97.70% 97.70% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

markdown/commonmark

Test result main count This PR count Difference
Total 652 652 0
Passed 652 652 0
Failed 0 0 0
Panics 0 0 0
Coverage 100.00% 100.00% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5466 5466 0
Passed 1915 1915 0
Failed 3551 3551 0
Panics 0 0 0
Coverage 35.03% 35.03% 0.00%

ts/babel

Test result main count This PR count Difference
Total 636 636 0
Passed 568 567 ❌ ⏬ -1
Failed 68 69 ❌ ⏫ +1
Panics 0 0 0
Coverage 89.31% 89.15% -0.16%
🔥 Regression (1):
conditional/arrow-ambiguity

ts/microsoft

Test result main count This PR count Difference
Total 18875 18875 0
Passed 13014 13013 ❌ ⏬ -1
Failed 5860 5861 ❌ ⏫ +1
Panics 1 1 0
Coverage 68.95% 68.94% -0.01%
🔥 Regression (1):
conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression8.ts

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d3205dd5-85a3-46f9-a304-b9de53bcd74e

📥 Commits

Reviewing files that changed from the base of the PR and between 5caccc6 and 67ff502.

⛔ Files ignored due to path filters (5)
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_async_destructured_arrow_return_type.ts.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_destructured_arrow_return_type_in_consequent.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (8)
  • .changeset/fix-js-parser-9548.md
  • crates/biome_js_parser/src/syntax/expr.rs
  • crates/biome_js_parser/src/syntax/function.rs
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.js
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.ts
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_async_destructured_arrow_return_type.ts
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_destructured_arrow_return_type_in_consequent.ts
✅ Files skipped from review due to trivial changes (5)
  • .changeset/fix-js-parser-9548.md
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.js
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_destructured_arrow_return_type_in_consequent.ts
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_async_destructured_arrow_return_type.ts
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.ts

Walkthrough

The parser was updated to correctly parse conditional expressions whose consequent is an arrow function returning a parenthesised object. An ExpressionContext flag for being in a conditional consequent was added and threaded into arrow parsing. parse_arrow_function_expression now accepts context; arrow-body parsing chooses a no-arrow variant when in a conditional consequent and the body starts with ( followed by { or [. A parse_assignment_expression_or_higher_no_arrow helper was added. Tests covering conditional arrow consequents and a changeset entry were also included.

Suggested reviewers

  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: the parser was incorrectly interpreting valid JavaScript as TypeScript, which aligns with the changeset modifications to arrow function parsing in conditional expressions.
Description check ✅ Passed The description is clearly related to the changeset, disclosing AI assistance and linking to issue #9548 that the changes directly address.
Linked Issues check ✅ Passed The code changes fully address issue #9548 by threading context through arrow function parsing to correctly handle arrow functions with parenthesized object returns in conditional consequents.
Out of Scope Changes check ✅ Passed All changes are scoped to the parser's arrow function and conditional expression handling, plus test fixtures validating the fix—no extraneous modifications present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dyc3/fix-js-as-ts-missparse
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can get early access to new features in CodeRabbit.

Enable the early_access setting to enable early access features such as new models, tools, and more.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts`:
- Around line 1-3: Update the test fixture so it hits the guard in function.rs
that only fires when the parameter list starts with `{` or `[` by adding a
destructured-parameter variant of the arrow function; specifically, in the
conditional_arrow_return_type_in_consequent.ts fixture where you currently have
the slotFn ternary with a typed simple parameter, replace or add a branch that
uses an object- or array-destructured parameter with the same return type
annotation (e.g., a parameter like an object destructuring rather than `i`) so
the parser path in crates/biome_js_parser/src/syntax/function.rs (the guard
around return type annotation parsing) is exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20c65c2e-57b3-4d9c-b0de-78beb845ede9

📥 Commits

Reviewing files that changed from the base of the PR and between 607ebf9 and a0f4428.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/fix-js-parser-9548.md
  • crates/biome_js_parser/src/syntax/function.rs
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_object_body_in_consequent.js
  • crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts

Comment on lines +1 to +3
const slotFn = isFirstMount
? ((i): Slot => ({ [CONTENT_SLOT]: i }))
: i => wrapSlotExpr(newExprs[i]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's check the fixture file
cat -n crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts

Repository: biomejs/biome

Length of output: 184


🏁 Script executed:

# Now let's look at the changed parser code around lines 591-605
sed -n '580,620p' crates/biome_js_parser/src/syntax/function.rs | cat -n

Repository: biomejs/biome

Length of output: 1537


🏁 Script executed:

# Let's get more context around the Ambiguity enum and the speculative parsing logic
rg "enum Ambiguity" -A 10 crates/biome_js_parser/src/

Repository: biomejs/biome

Length of output: 944


🏁 Script executed:

# Search for other test fixtures with destructured parameters and return types
fd "conditional_arrow.*\.ts$" crates/biome_js_parser/tests/ | head -20

Repository: biomejs/biome

Length of output: 149


🏁 Script executed:

# Let's also check if there are any existing tests with destructured params and TypeScript return types
rg '\{\s*\[' crates/biome_js_parser/tests/js_test_suite/ok/ --type ts -B2 -A2 | head -50

Repository: biomejs/biome

Length of output: 2617


🏁 Script executed:

# Let's look at the broader context of the changed code
sed -n '550,650p' crates/biome_js_parser/src/syntax/function.rs | cat -n

Repository: biomejs/biome

Length of output: 4129


🏁 Script executed:

# Search for what PR or commit made this change
git log --oneline -20 -- crates/biome_js_parser/src/syntax/function.rs

Repository: biomejs/biome

Length of output: 136


🏁 Script executed:

# Check if there are existing test fixtures that test destructured arrow parameters with return types
rg 'const.*?(\{.*?\}):.*?=>' crates/biome_js_parser/tests/js_test_suite/ok/ --type ts -A1 | head -40

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Look for has_ambiguous_destructured_parameters to understand when it's set
rg 'has_ambiguous_destructured_parameters' crates/biome_js_parser/src/ -B3 -A3

Repository: biomejs/biome

Length of output: 1146


🏁 Script executed:

# Let's see the actual commit that made this change
git show a0f44283e --stat

Repository: biomejs/biome

Length of output: 50371


🏁 Script executed:

# Look at the full diff of that commit
git show a0f44283e

Repository: biomejs/biome

Length of output: 50377


🏁 Script executed:

# Let's look at the exact condition and understand when it fires
sed -n '570,620p' crates/biome_js_parser/src/syntax/function.rs

Repository: biomejs/biome

Length of output: 1713


🏁 Script executed:

# Let's check what has_ambiguous_destructured_parameters checks
sed -n '573,576p' crates/biome_js_parser/src/syntax/function.rs

Repository: biomejs/biome

Length of output: 309


🏁 Script executed:

# Let's create a simple test to understand the parser behavior
cat > /tmp/test_parser.ts << 'EOF'
// Current fixture - unambiguous parameter
const slotFn = isFirstMount
  ? ((i): Slot => ({ [CONTENT_SLOT]: i }))
  : i => wrapSlotExpr(newExprs[i]);

// Suggested fixture - ambiguous parameter with return type
const slotFn2 = isFirstMount
  ? ({ [CONTENT_SLOT]: i }): Slot => ({ [CONTENT_SLOT]: i })
  : i => wrapSlotExpr(newExprs[i]);
EOF
cat /tmp/test_parser.ts

Repository: biomejs/biome

Length of output: 393


🏁 Script executed:

# Look for similar test patterns in the codebase
find crates/biome_js_parser/tests/js_test_suite/ok -name "*.ts" -type f -exec grep -l "}: .* =>" {} \; | head -5

Repository: biomejs/biome

Length of output: 39


🏁 Script executed:

# Check if there are existing arrow function tests with destructured params and return types
rg '{\s*\[.*\].*}:\s*\w+\s*=>' crates/biome_js_parser/tests/ -A2 -B2 | head -40

Repository: biomejs/biome

Length of output: 157


🏁 Script executed:

# Let's verify the fixture works by checking if there are similar patterns nearby
rg 'Slot\s*=>' crates/biome_js_parser/tests/ -B2 -A2 | head -30

Repository: biomejs/biome

Length of output: 1051


🏁 Script executed:

# Let's trace through the logic manually - check what the second token would be
# for the current fixture
cat > /tmp/analyze.txt << 'EOF'
Current fixture: ((i): Slot => ...)
After opening (: [
  Token 0: i
  Token 1: )

has_ambiguous_destructured_parameters = matches!(p.nth(1), T!['['] | T!['{'])
  = matches!(T![')'], T!['['] | T!['{'])
  = FALSE

So the new guard condition:
  if !(ambiguity.is_disallowed()
      && has_ambiguous_destructured_parameters  ← FALSE, so whole condition becomes FALSE
      && !TypeScript.is_supported(p)
      && p.at(T![:]))
  
Since has_ambiguous_destructured_parameters is FALSE, the negation becomes TRUE,
so the return type annotation WILL be parsed (the guard is NOT triggered).

---

Suggested fixture: ({ [CONTENT_SLOT]: i }): Slot => ...
After opening (: [
  Token 0: {
  Token 1: [CONTENT_SLOT] or space

has_ambiguous_destructured_parameters = matches!(p.nth(1), T!['['] | T!['{'])
  = matches!(T!['{'], T!['['] | T!['{'])
  = TRUE

So the new guard condition becomes:
  if !(ambiguity.is_disallowed()
      && TRUE
      && !TypeScript.is_supported(p)  ← TRUE in JS mode
      && p.at(T![:]))                ← TRUE, we're about to see ':'

If ALL conditions are TRUE, the negation makes the whole if FALSE,
so we SKIP the return type annotation parsing (the guard IS triggered).
EOF
cat /tmp/analyze.txt

Repository: biomejs/biome

Length of output: 1243


This fixture misses the branch you just changed.

The guard that skips return type annotation parsing in crates/biome_js_parser/src/syntax/function.rs (lines 591–605) only fires when the parameter list starts with { or [. The current fixture ((i): Slot => ...) has a simple parameter, not destructuring, so it doesn't exercise this branch. Add a destructured parameter case:

Suggested fixture
 const slotFn = isFirstMount
-  ? ((i): Slot => ({ [CONTENT_SLOT]: i }))
+  ? ({ [CONTENT_SLOT]: i }): Slot => ({ [CONTENT_SLOT]: i })
   : i => wrapSlotExpr(newExprs[i]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const slotFn = isFirstMount
? ((i): Slot => ({ [CONTENT_SLOT]: i }))
: i => wrapSlotExpr(newExprs[i]);
const slotFn = isFirstMount
? ({ [CONTENT_SLOT]: i }): Slot => ({ [CONTENT_SLOT]: i })
: i => wrapSlotExpr(newExprs[i]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@crates/biome_js_parser/tests/js_test_suite/ok/conditional_arrow_return_type_in_consequent.ts`
around lines 1 - 3, Update the test fixture so it hits the guard in function.rs
that only fires when the parameter list starts with `{` or `[` by adding a
destructured-parameter variant of the arrow function; specifically, in the
conditional_arrow_return_type_in_consequent.ts fixture where you currently have
the slotFn ternary with a typed simple parameter, replace or add a branch that
uses an object- or array-destructured parameter with the same return type
annotation (e.g., a parameter like an object destructuring rather than `i`) so
the parser path in crates/biome_js_parser/src/syntax/function.rs (the guard
around return type annotation parsing) is exercised.

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 19, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing dyc3/fix-js-as-ts-missparse (67ff502) with main (f4bf341)

Open in CodSpeed

Footnotes

  1. 156 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@dyc3 dyc3 force-pushed the dyc3/fix-js-as-ts-missparse branch from a0f4428 to 5caccc6 Compare March 19, 2026 18:48
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Ops, it seems we regressed something.

Maybe we should look at what regressed and feed it to the agent

@dyc3 dyc3 force-pushed the dyc3/fix-js-as-ts-missparse branch from 5caccc6 to 67ff502 Compare March 19, 2026 19:47
@dyc3 dyc3 merged commit d4e3d6e into main Mar 19, 2026
18 checks passed
@dyc3 dyc3 deleted the dyc3/fix-js-as-ts-missparse branch March 19, 2026 22:14
@github-actions github-actions bot mentioned this pull request Mar 19, 2026
@ematipico
Copy link
Member

We still regressed, we shouldn't have merged this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Valid JS parsed as TS

2 participants