diff --git a/crates/oxc_linter/src/rules/early_error/javascript.rs b/crates/oxc_linter/src/rules/early_error/javascript.rs index ae8c0a47b..809bcf32e 100644 --- a/crates/oxc_linter/src/rules/early_error/javascript.rs +++ b/crates/oxc_linter/src/rules/early_error/javascript.rs @@ -29,6 +29,7 @@ impl Rule for EarlyErrorJavaScript { AstKind::StringLiteral(lit) => check_string_literal(lit, node, ctx), AstKind::RegExpLiteral(lit) => check_regexp_literal(lit, ctx), AstKind::WithStatement(stmt) => check_with_statement(stmt, node, ctx), + AstKind::SwitchStatement(stmt) => check_switch_statement(stmt, ctx), AstKind::BreakStatement(stmt) => check_break_statement(stmt, node, ctx), AstKind::ContinueStatement(stmt) => check_continue_statement(stmt, node, ctx), AstKind::LabeledStatement(stmt) => check_labeled_statement(stmt, node, ctx), @@ -322,6 +323,19 @@ fn check_with_statement<'a>(stmt: &WithStatement, node: &AstNode<'a>, ctx: &Lint } } +fn check_switch_statement<'a>(stmt: &SwitchStatement<'a>, ctx: &LintContext<'a>) { + let mut previous_default: Option = None; + for case in &stmt.cases { + if case.test.is_none() { + if let Some(previous_span) = previous_default { + ctx.diagnostic(Redeclaration("default".into(), previous_span, case.span)); + break; + } + previous_default.replace(case.span); + } + } +} + #[derive(Debug, Error, Diagnostic)] #[error("Jump target cannot cross function boundary.")] #[diagnostic()] diff --git a/tasks/coverage/babel.snap b/tasks/coverage/babel.snap index 67a00cd6a..01db3d693 100644 --- a/tasks/coverage/babel.snap +++ b/tasks/coverage/babel.snap @@ -1,7 +1,7 @@ Babel Summary: AST Parsed : 2053/2069 (99.23%) Positive Passed: 2053/2069 (99.23%) -Negative Passed: 1040/1502 (69.24%) +Negative Passed: 1042/1502 (69.37%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-multiple-labels/input.js" @@ -31,7 +31,6 @@ Expect Syntax Error: "core/scope/undecl-export-builtin-as/input.js" Expect Syntax Error: "core/scope/undecl-export-builtin/input.js" Expect Syntax Error: "core/scope/undecl-export-if/input.js" Expect Syntax Error: "core/scope/undecl-export/input.js" -Expect Syntax Error: "core/uncategorised/427/input.js" Expect Syntax Error: "core/uncategorised/454/input.js" Expect Syntax Error: "core/uncategorised/466/input.js" Expect Syntax Error: "core/uncategorised/467/input.js" @@ -265,7 +264,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0093/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0094/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0100/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0101/input.js" -Expect Syntax Error: "esprima/invalid-syntax/migrated_0143/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0171/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0183/input.js" Expect Syntax Error: "esprima/invalid-syntax/migrated_0184/input.js" @@ -1691,6 +1689,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" · ╰── Invalid Character `‿` ╰──── + × Identifier `default` has already been declared + ╭─[core/uncategorised/427/input.js:1:1] + 1 │ switch (c) { default: default: } + · ────┬─── ────┬─── + · │ ╰── It can not be redeclared here + · ╰── `default` has already been declared here + ╰──── + × Unexpected token ╭─[core/uncategorised/428/input.js:1:1] 1 │ new X()."s" @@ -7542,6 +7548,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" ╰──── help: Wrap this declaration in a block statement + × Identifier `default` has already been declared + ╭─[esprima/invalid-syntax/migrated_0143/input.js:1:1] + 1 │ switch (c) { default: default: } + · ────┬─── ────┬─── + · │ ╰── It can not be redeclared here + · ╰── `default` has already been declared here + ╰──── + × Unexpected token ╭─[esprima/invalid-syntax/migrated_0144/input.js:1:1] 1 │ new X()."s" diff --git a/tasks/coverage/test262.snap b/tasks/coverage/test262.snap index 0ac90ec96..118177b65 100644 --- a/tasks/coverage/test262.snap +++ b/tasks/coverage/test262.snap @@ -1,7 +1,7 @@ Test262 Summary: AST Parsed : 44015/44034 (99.96%) Positive Passed: 44015/44034 (99.96%) -Negative Passed: 3071/3917 (78.40%) +Negative Passed: 3072/3917 (78.43%) Expect Syntax Error: "language/block-scope/syntax/function-declarations/in-statement-position-do-statement-while-expression.js" Expect Syntax Error: "language/block-scope/syntax/function-declarations/in-statement-position-for-statement.js" Expect Syntax Error: "language/block-scope/syntax/function-declarations/in-statement-position-if-expression-statement-else-statement.js" @@ -798,7 +798,6 @@ Expect Syntax Error: "language/statements/return/S12.9_A1_T6.js" Expect Syntax Error: "language/statements/return/S12.9_A1_T7.js" Expect Syntax Error: "language/statements/return/S12.9_A1_T8.js" Expect Syntax Error: "language/statements/return/S12.9_A1_T9.js" -Expect Syntax Error: "language/statements/switch/S12.11_A2_T1.js" Expect Syntax Error: "language/statements/try/catch-parameter-boundnames-restriction-arguments-negative-early.js" Expect Syntax Error: "language/statements/try/catch-parameter-boundnames-restriction-eval-negative-early.js" Expect Syntax Error: "language/statements/try/dstr/ary-ptrn-rest-init-ary.js" @@ -27744,6 +27743,20 @@ Expect to Parse: "language/statements/function/S14_A5_T2.js" ╰──── help: Try insert a semicolon here + × Identifier `default` has already been declared + ╭─[language/statements/switch/S12.11_A2_T1.js:20:1] + 20 │ result += 2; + 21 │ ╭─▶ default: + 22 │ │ result += 32; + 23 │ ├─▶ break; + · ╰──── `default` has already been declared here + 24 │ ╭─▶ default: + 25 │ │ result += 32; + 26 │ ├─▶ break; + · ╰──── It can not be redeclared here + 27 │ } + ╰──── + × Unexpected token ╭─[language/statements/switch/S12.11_A3_T1.js:17:1] 17 │