fix(parser): report comma operator in JSX expression in the parser (#23030)

## What

Moves the "JSX expressions may not use the comma operator" check (TS18007) — e.g. `<div>{a, b}</div>` — out of the semantic checker (`check_jsx_expression_container`) into the parser, right where the JSX expression is parsed.

## Why it's a clean move

- **Zero state:** the check reads only whether the parsed expression is a `SequenceExpression`.
- **Node-final:** a `JSXExpressionContainer` is never reinterpreted by cover grammar, so there's no value-vs-pattern ambiguity.
- **Negative-only:** confirmed by `cargo coverage` — no positive-suite regressions, and zero snapshot changes in any suite.

## Verification

- `<div>{a, b}</div>` and `<div a={b, c} />` → error; `<div>{a}</div>` and `<div>{[a, b]}</div>` → clean.
- Parser/semantic/linter tests pass; clippy clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Boshen committed 2026-06-06 18:22:57 +00:00
1 parent 325c94fefb
commit c54315405d
5 files changed
+13 -21

No files matched your search

+7
View File
@@ -1209,6 +1209,13 @@ pub fn abstract_with_private_identifier(span: Span) -> OxcDiagnostic {
.with_label(span)
}
#[cold]
pub fn jsx_expressions_may_not_use_the_comma_operator(span: Span) -> OxcDiagnostic {
ts_error("18007", "JSX expressions may not use the comma operator")
.with_help("Did you mean to write an array?")
.with_label(span)
}
#[cold]
pub fn import_alias_cannot_use_import_type(span: Span) -> OxcDiagnostic {
ts_error("1392", "An import alias cannot use 'import type'").with_label(span)
+6
View File
@@ -352,6 +352,12 @@ impl<'a, C: Config> ParserImpl<'a, C> {
self.ast.jsx_expression_empty_expression(Span::new(span.start + 1, span.end - 1))
} else {
let expr = JSXExpression::from(self.parse_expr());
// JSX expressions may not use the comma operator.
if matches!(expr, JSXExpression::SequenceExpression(_)) {
self.error(diagnostics::jsx_expressions_may_not_use_the_comma_operator(
expr.span(),
));
}
if in_jsx_child {
self.expect_jsx_child(Kind::RCurly);
} else {
-3
View File
@@ -114,9 +114,6 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) {
AstKind::TSEnumDeclaration(decl) => ts::check_ts_enum_declaration(decl, ctx),
AstKind::TSTypeAliasDeclaration(decl) => ts::check_ts_type_alias_declaration(decl, ctx),
AstKind::TSInferType(infer_type) => ts::check_ts_infer_type(infer_type, ctx),
AstKind::JSXExpressionContainer(container) => {
ts::check_jsx_expression_container(container, ctx);
}
_ => {}
}
}
@@ -332,17 +332,6 @@ pub fn check_for_statement_left(left: &ForStatementLeft, is_for_in: bool, ctx: &
}
}
pub fn check_jsx_expression_container(
container: &JSXExpressionContainer,
ctx: &SemanticBuilder<'_>,
) {
if matches!(container.expression, JSXExpression::SequenceExpression(_)) {
ctx.error(diagnostics::jsx_expressions_may_not_use_the_comma_operator(
container.expression.span(),
));
}
}
pub fn check_ts_export_assignment_in_program<'a>(program: &Program<'a>, ctx: &SemanticBuilder<'a>) {
if !ctx.source_type.is_typescript() {
return;
-7
View File
@@ -427,13 +427,6 @@ pub fn type_annotation_in_for_left(span: Span, is_for_in: bool) -> OxcDiagnostic
).with_label(span).with_help("This iterator's type will be inferred from the iterable. You can safely remove the type annotation.")
}
#[cold]
pub fn jsx_expressions_may_not_use_the_comma_operator(span: Span) -> OxcDiagnostic {
ts_error("18007", "JSX expressions may not use the comma operator")
.with_help("Did you mean to write an array?")
.with_label(span)
}
#[cold]
pub fn ts_export_assignment_cannot_be_used_with_other_exports(span: Span) -> OxcDiagnostic {
ts_error("2309", "An export assignment cannot be used in a module with other exported elements")