diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs
index 13fadeb0012cd..2e693c30e888b 100644
--- a/crates/oxc_semantic/src/checker/mod.rs
+++ b/crates/oxc_semantic/src/checker/mod.rs
@@ -117,6 +117,9 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {
AstKind::TSImportEqualsDeclaration(decl) => {
ts::check_ts_import_equals_declaration(decl, ctx);
}
+ AstKind::JSXExpressionContainer(container) => {
+ ts::check_jsx_expression_container(container, ctx);
+ }
_ => {}
}
}
diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs
index 2837dc5a0c199..36b653d0775af 100644
--- a/crates/oxc_semantic/src/checker/typescript.rs
+++ b/crates/oxc_semantic/src/checker/typescript.rs
@@ -542,3 +542,19 @@ pub fn check_for_statement_left(left: &ForStatementLeft, is_for_in: bool, ctx: &
}
}
}
+
+fn invalid_jsx_attribute_value(span: Span) -> OxcDiagnostic {
+ ts_error("17000", "JSX attributes must only be assigned a non-empty 'expression'.")
+ .with_label(span)
+}
+
+pub fn check_jsx_expression_container(
+ container: &JSXExpressionContainer,
+ ctx: &SemanticBuilder<'_>,
+) {
+ if matches!(container.expression, JSXExpression::EmptyExpression(_))
+ && matches!(ctx.nodes.parent_kind(ctx.current_node_id), Some(AstKind::JSXAttributeItem(_)))
+ {
+ ctx.error(invalid_jsx_attribute_value(container.span()));
+ }
+}
diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap
index 1587be1641251..140637e6ec16d 100644
--- a/tasks/coverage/snapshots/parser_typescript.snap
+++ b/tasks/coverage/snapshots/parser_typescript.snap
@@ -3,7 +3,7 @@ commit: d85767ab
parser_typescript Summary:
AST Parsed : 6494/6503 (99.86%)
Positive Passed: 6483/6503 (99.69%)
-Negative Passed: 1283/5747 (22.32%)
+Negative Passed: 1284/5747 (22.34%)
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration24.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment8.ts
@@ -1279,7 +1279,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsdocTypeCas
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsdocTypeNongenericInstantiationAttempt.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsdocTypedefMissingType.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsdocTypedefNoCrash2.ts
-Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsxAttributeWithoutExpressionReact.tsx
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsxCallElaborationCheckNoCrash1.tsx
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsxChildWrongType.tsx
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/jsxChildrenArrayWrongType.tsx
@@ -8920,6 +8919,30 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2 │ const y = 0;
╰────
+ × TS(17000): JSX attributes must only be assigned a non-empty 'expression'.
+ ╭─[typescript/tests/cases/compiler/jsxAttributeWithoutExpressionReact.tsx:4:35]
+ 3 │
+ · ──
+ 5 │ } dataSource={this.state.ds} renderRow={}>
+ ╰────
+
+ × TS(17000): JSX attributes must only be assigned a non-empty 'expression'.
+ ╭─[typescript/tests/cases/compiler/jsxAttributeWithoutExpressionReact.tsx:4:49]
+ 3 │
+ · ──
+ 5 │ } dataSource={this.state.ds} renderRow={}>
+ ╰────
+
+ × TS(17000): JSX attributes must only be assigned a non-empty 'expression'.
+ ╭─[typescript/tests/cases/compiler/jsxAttributeWithoutExpressionReact.tsx:5:44]
+ 4 │
+ 5 │ } dataSource={this.state.ds} renderRow={}>
+ · ──
+ 6 │
+ ╰────
+
× Unexpected token
╭─[typescript/tests/cases/compiler/jsxNamespacePrefixInName.tsx:7:32]
6 │