From 77efb7698db27c4f9b3f624d96b2fd3beeb7d462 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Sun, 16 Nov 2025 02:57:35 +0000 Subject: [PATCH] feat(parser): improve error message for invalid switch clauses (#15728) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improved error message for invalid switch clauses. **Before** ``` x Unexpected token ,-[:2:3] 1 | switch (foo) { 2 | caze 1: : ^^^^ 3 | } `---- ``` **After** ``` × Expected switch clause ╭─[misc/fail/switch-invalid-clause.js:2:3] 1 │ switch (foo) { 2 │ caze 1: · ──┬─ · ╰── `case` or `default` clause expected here 3 │ } ╰──── help: If this is intended to be the condition for the switch statement, add `case` before it. ``` --- crates/oxc_parser/src/diagnostics.rs | 7 +++++++ crates/oxc_parser/src/js/statement.rs | 5 ++++- tasks/coverage/misc/fail/switch-invalid-clause.js | 3 +++ tasks/coverage/snapshots/parser_babel.snap | 6 ++++-- tasks/coverage/snapshots/parser_misc.snap | 12 +++++++++++- tasks/coverage/snapshots/parser_test262.snap | 6 ++++-- tasks/coverage/snapshots/parser_typescript.snap | 6 ++++-- 7 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tasks/coverage/misc/fail/switch-invalid-clause.js diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 21a9cae8cd5a6..8f5749e6a0828 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -1089,3 +1089,10 @@ pub fn expect_function_body(span: Span) -> OxcDiagnostic { .with_label(span) .with_help("Add a function body (`{}`).") } + +#[cold] +pub fn expect_switch_clause(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Expected switch clause") + .with_label(span.label("`case` or `default` clause expected here")) + .with_help("If this is intended to be the condition for the switch statement, add `case` before it.") +} diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index c2e6774c6060c..a87b0d4482ca3 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -644,7 +644,10 @@ impl<'a> ParserImpl<'a> { let expression = self.parse_expr(); Some(expression) } - _ => return self.unexpected(), + _ => { + return self + .fatal_error(diagnostics::expect_switch_clause(self.cur_token().span())); + } }; self.expect(Kind::Colon); let mut consequent = self.ast.vec(); diff --git a/tasks/coverage/misc/fail/switch-invalid-clause.js b/tasks/coverage/misc/fail/switch-invalid-clause.js new file mode 100644 index 0000000000000..5a3d4e699f4b4 --- /dev/null +++ b/tasks/coverage/misc/fail/switch-invalid-clause.js @@ -0,0 +1,3 @@ +switch (foo) { + caze 1: +} diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index d4f704d42a8a3..7245fc19d3567 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -918,13 +918,15 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ─── ╰──── - × Unexpected token + × Expected switch clause ╭─[babel/packages/babel-parser/test/fixtures/core/categorized/malformed-switch/input.js:2:3] 1 │ switch (x) { 2 │ var y = 5; - · ─── + · ─┬─ + · ╰── `case` or `default` clause expected here 3 │ } ╰──── + help: If this is intended to be the condition for the switch statement, add `case` before it. × Cannot assign to this expression ╭─[babel/packages/babel-parser/test/fixtures/core/create-parenthesized-expressions/invalid-parenthesized-assignment-pattern/input.js:1:1] diff --git a/tasks/coverage/snapshots/parser_misc.snap b/tasks/coverage/snapshots/parser_misc.snap index 12e867d590169..f62eaa207b68b 100644 --- a/tasks/coverage/snapshots/parser_misc.snap +++ b/tasks/coverage/snapshots/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 49/49 (100.00%) Positive Passed: 49/49 (100.00%) -Negative Passed: 111/111 (100.00%) +Negative Passed: 112/112 (100.00%) × Cannot assign to 'arguments' in strict mode ╭─[misc/fail/arguments-eval.ts:1:10] @@ -3431,3 +3431,13 @@ Negative Passed: 111/111 (100.00%) 4 │ let()[a] = 1; · ─── ╰──── + + × Expected switch clause + ╭─[misc/fail/switch-invalid-clause.js:2:3] + 1 │ switch (foo) { + 2 │ caze 1: + · ──┬─ + · ╰── `case` or `default` clause expected here + 3 │ } + ╰──── + help: If this is intended to be the condition for the switch statement, add `case` before it. diff --git a/tasks/coverage/snapshots/parser_test262.snap b/tasks/coverage/snapshots/parser_test262.snap index cff0044cf9689..13de17900fefc 100644 --- a/tasks/coverage/snapshots/parser_test262.snap +++ b/tasks/coverage/snapshots/parser_test262.snap @@ -38140,13 +38140,15 @@ Expect to Parse: tasks/coverage/test262/test/language/statements/using/syntax/us 23 │ result += 2; ╰──── - × Unexpected token + × Expected switch clause ╭─[test262/test/language/statements/switch/S12.11_A3_T5.js:20:4] 19 │ switch(value) { 20 │ result =2; - · ────── + · ───┬── + · ╰── `case` or `default` clause expected here 21 │ case 0: ╰──── + help: If this is intended to be the condition for the switch statement, add `case` before it. × Identifier `f` has already been declared ╭─[test262/test/language/statements/switch/syntax/redeclaration/async-function-name-redeclaration-attempt-with-async-function.js:24:37] diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index a6f42f8612bef..d216efdfef322 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -21568,13 +21568,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/statements/Va 5 │ 1 + ╰──── - × Unexpected token + × Expected switch clause ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/SwitchStatements/parserErrorRecovery_SwitchStatement2.ts:5:1] 4 │ 5 │ class D { - · ───── + · ──┬── + · ╰── `case` or `default` clause expected here 6 │ } ╰──── + help: If this is intended to be the condition for the switch statement, add `case` before it. × Invalid Unicode escape sequence ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/TypeArgumentLists/TypeArgumentList1.ts:1:10]