From 341aa89d38211d0e0fca13ecd8a289b4093d2837 Mon Sep 17 00:00:00 2001 From: Lukino Date: Thu, 11 Sep 2025 13:39:38 +0200 Subject: [PATCH 1/5] fix(linter/switch-case-braces): add support for string including colon on case expression The following case was wrongly formatted. // Input case "scope:type": // ... break; // Output case "scope: {type": // ... break; } // Expected case "scope:type": { // ... break; } --- .../src/rules/unicorn/switch_case_braces.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs index b0a6b04314f7b..4a2964c907a4c 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -1,3 +1,4 @@ +use lazy_regex::Regex; use oxc_ast::{AstKind, ast::Statement}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -142,8 +143,10 @@ impl Rule for SwitchCaseBraces { }; if self.always_braces && missing_braces { - let colon = u32::try_from(ctx.source_range(case.span).find(':').unwrap()).unwrap(); - let span = Span::sized(case.span.start, colon + 1); + let regex = Regex::new(r#"(case|default)\s*(`.+`|'.+'|".+"|[^:]*):"#).unwrap(); + let colon_end = + u32::try_from(regex.find(ctx.source_range(case.span)).unwrap().end()).unwrap(); + let span = Span::sized(case.span.start, colon_end); ctx.diagnostic_with_fix( switch_case_braces_diagnostic_missing_braces(span), |fixer| { @@ -251,6 +254,21 @@ fn test() { None, ), ("switch(foo){ case 1: {}; break; }", "switch(foo){ case 1: { {}; break;} }", None), + ( + "switch(something) { case `scope:type`: doSomething();}", + "switch(something) { case `scope:type`: { doSomething();}}", + None, + ), + ( + "switch(something) { case \"scope:type\": doSomething();}", + "switch(something) { case \"scope:type\": { doSomething();}}", + None, + ), + ( + "switch(something) { case 'scope:type': doSomething();}", + "switch(something) { case 'scope:type': { doSomething();}}", + None, + ), ]; Tester::new(SwitchCaseBraces::NAME, SwitchCaseBraces::PLUGIN, pass, fail) From 2830da905f41377849dae666cc0b24ae005dae56 Mon Sep 17 00:00:00 2001 From: Lukino Date: Fri, 12 Sep 2025 16:10:15 +0200 Subject: [PATCH 2/5] fix(linter/switch-case-braces): move regex out of the for loop --- .../oxc_linter/src/rules/unicorn/switch_case_braces.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs index 4a2964c907a4c..28ce367316576 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -96,6 +96,9 @@ impl Rule for SwitchCaseBraces { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let switch_clause_regex = + Regex::new(r#"(case|default)\s*(`.+`|'.+'|".+"|[^:]*):"#).unwrap(); + let AstKind::SwitchStatement(switch) = node.kind() else { return; }; @@ -143,9 +146,10 @@ impl Rule for SwitchCaseBraces { }; if self.always_braces && missing_braces { - let regex = Regex::new(r#"(case|default)\s*(`.+`|'.+'|".+"|[^:]*):"#).unwrap(); - let colon_end = - u32::try_from(regex.find(ctx.source_range(case.span)).unwrap().end()).unwrap(); + let colon_end = u32::try_from( + switch_clause_regex.find(ctx.source_range(case.span)).unwrap().end(), + ) + .unwrap(); let span = Span::sized(case.span.start, colon_end); ctx.diagnostic_with_fix( switch_case_braces_diagnostic_missing_braces(span), From dfa5825ccbaab8ee8cd0de28adbf23a3ef266a5a Mon Sep 17 00:00:00 2001 From: Lukino Date: Fri, 19 Sep 2025 09:30:07 +0200 Subject: [PATCH 3/5] fix(linter/switch-case-braces): move the regex to fix lintgen --- crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs index 28ce367316576..22622400d0bd7 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -96,9 +96,6 @@ impl Rule for SwitchCaseBraces { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let switch_clause_regex = - Regex::new(r#"(case|default)\s*(`.+`|'.+'|".+"|[^:]*):"#).unwrap(); - let AstKind::SwitchStatement(switch) = node.kind() else { return; }; @@ -107,6 +104,9 @@ impl Rule for SwitchCaseBraces { return; } + let switch_clause_regex = + Regex::new(r#"(case|default)\s*(`[^`]+`|'[^']+'|"[^"]+"|[^:]*):"#).unwrap(); + for case in &switch.cases { if case.consequent.is_empty() { continue; From f08261d51d45da2aa3de0047f14c4cd6f8cd691b Mon Sep 17 00:00:00 2001 From: Lukino Date: Fri, 19 Sep 2025 09:30:57 +0200 Subject: [PATCH 4/5] fix(linter/switch-case-braces): add non-capturing group to regex --- crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs index 22622400d0bd7..e13d923cfbdc9 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -105,7 +105,7 @@ impl Rule for SwitchCaseBraces { } let switch_clause_regex = - Regex::new(r#"(case|default)\s*(`[^`]+`|'[^']+'|"[^"]+"|[^:]*):"#).unwrap(); + Regex::new(r#"(?:case|default)\s*(?:`[^`]+`|'[^']+'|"[^"]+"|[^:]*):"#).unwrap(); for case in &switch.cases { if case.consequent.is_empty() { From 1288958ff038b123088784fd7796e3a228f19164 Mon Sep 17 00:00:00 2001 From: Lukino Date: Fri, 19 Sep 2025 20:42:15 +0200 Subject: [PATCH 5/5] refactor(linter/switch-case-braces): remove usage of regex to improve performance --- .../src/rules/unicorn/switch_case_braces.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs index e13d923cfbdc9..0b9f8e18ab9dc 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -1,4 +1,3 @@ -use lazy_regex::Regex; use oxc_ast::{AstKind, ast::Statement}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -104,9 +103,6 @@ impl Rule for SwitchCaseBraces { return; } - let switch_clause_regex = - Regex::new(r#"(?:case|default)\s*(?:`[^`]+`|'[^']+'|"[^"]+"|[^:]*):"#).unwrap(); - for case in &switch.cases { if case.consequent.is_empty() { continue; @@ -146,11 +142,13 @@ impl Rule for SwitchCaseBraces { }; if self.always_braces && missing_braces { - let colon_end = u32::try_from( - switch_clause_regex.find(ctx.source_range(case.span)).unwrap().end(), + let colon = u32::try_from( + ctx.source_range(Span::new(case.span.start, case.consequent[0].span().start)) + .rfind(':') + .unwrap(), ) .unwrap(); - let span = Span::sized(case.span.start, colon_end); + let span = Span::sized(case.span.start, colon + 1); ctx.diagnostic_with_fix( switch_case_braces_diagnostic_missing_braces(span), |fixer| {