From c545e1021c1419fcc6cac60bf06787e36a59f68b Mon Sep 17 00:00:00 2001 From: 1zumii <524123601@qq.com> Date: Thu, 23 Jan 2025 17:38:00 +0800 Subject: [PATCH] feat(linter): unicorn/switch-cases-braces support options --- .../src/rules/unicorn/switch_case_braces.rs | 88 +++++++++++++++++-- .../snapshots/unicorn_switch_case_braces.snap | 44 +++++----- 2 files changed, 102 insertions(+), 30 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 fd74760321c1d..bd2cb783643c1 100644 --- a/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs +++ b/crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs @@ -5,16 +5,33 @@ use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; -fn switch_case_braces_diagnostic(span: Span) -> OxcDiagnostic { - OxcDiagnostic::warn( - " Empty switch case shouldn't have braces and not-empty case should have braces around it.", - ) - .with_help("There is less visual clutter for empty cases and proper scope for non-empty cases.") +#[derive(Clone, Copy)] +enum Diagnostic { + EmptyClause, + MissingBraces, + UnnecessaryBraces, +} + +fn switch_case_braces_diagnostic(span: Span, diagnostic_type: Diagnostic) -> OxcDiagnostic { + (match diagnostic_type { + Diagnostic::EmptyClause => OxcDiagnostic::warn("Unexpected braces in empty case clause.") + .with_help("Remove braces in empty case clause."), + Diagnostic::MissingBraces => OxcDiagnostic::warn("Missing braces in case clause.") + .with_help("Add Braces for case clause."), + Diagnostic::UnnecessaryBraces => OxcDiagnostic::warn("Unnecessary braces in case clause.") + .with_help("Remove Braces for case clause."), + }) .with_label(span) } #[derive(Debug, Default, Clone)] -pub struct SwitchCaseBraces; +pub struct SwitchCaseBraces { + // true - "always" (default) + // - Always report when clause is not a BlockStatement + // false - "avoid" + // - Only allow braces when there are variable declaration or function declaration which requires a scope. + always_braces: bool, +} declare_oxc_lint!( /// ### What it does @@ -41,6 +58,12 @@ declare_oxc_lint!( ); impl Rule for SwitchCaseBraces { + fn from_configuration(value: serde_json::Value) -> Self { + let always = value.get(0).map_or(true, |v| v.as_str() != Some("avoid")); + + Self { always_braces: always } + } + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { let AstKind::SwitchStatement(switch) = node.kind() else { return; @@ -56,13 +79,54 @@ impl Rule for SwitchCaseBraces { Statement::BlockStatement(case_block) => { if case_block.body.is_empty() { ctx.diagnostic_with_fix( - switch_case_braces_diagnostic(case_block.span), + switch_case_braces_diagnostic( + case_block.span, + Diagnostic::EmptyClause, + ), |fixer| fixer.delete_range(case_block.span), ); } + + if !self.always_braces + && !case_block.body.iter().any(|stmt| { + matches!( + stmt, + Statement::VariableDeclaration(_) + | Statement::FunctionDeclaration(_) + ) + }) + { + ctx.diagnostic_with_fix( + switch_case_braces_diagnostic( + case_block.span(), + Diagnostic::UnnecessaryBraces, + ), + |fixer| { + fixer.replace( + case_block.span, + fixer.source_range(Span::new( + case_block.span.start + 1, + case_block.span.end - 1, + )), + ) + }, + ); + } } Statement::EmptyStatement(_) => {} _ => { + if !self.always_braces + && !&case.consequent.iter().any(|stmt| { + matches!( + stmt, + Statement::VariableDeclaration(_) + | Statement::FunctionDeclaration(_) + ) + }) + { + return; + } + let Some(first_statement) = &case.consequent.first() else { return; }; @@ -74,7 +138,10 @@ impl Rule for SwitchCaseBraces { Span::new(first_statement.span().start, last_statement.span().end); ctx.diagnostic_with_fix( - switch_case_braces_diagnostic(case_body_span), + switch_case_braces_diagnostic( + case_body_span, + Diagnostic::MissingBraces, + ), |fixer| { let modified_code = { let mut formatter = fixer.codegen(); @@ -155,6 +222,11 @@ fn test() { None, ), ("switch(s){case'':/]/}", "switch(s){case '': {/]/}}", None), + ( + "switch(foo) { default: {doSomething();} }", + "switch(foo) { default: doSomething(); }", + Some(serde_json::json!(["avoid"])), + ), ]; Tester::new(SwitchCaseBraces::NAME, SwitchCaseBraces::PLUGIN, pass, fail) diff --git a/crates/oxc_linter/src/snapshots/unicorn_switch_case_braces.snap b/crates/oxc_linter/src/snapshots/unicorn_switch_case_braces.snap index d8a9a17a96d17..d0f21f184bcd8 100644 --- a/crates/oxc_linter/src/snapshots/unicorn_switch_case_braces.snap +++ b/crates/oxc_linter/src/snapshots/unicorn_switch_case_braces.snap @@ -1,79 +1,79 @@ --- source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:18] 1 │ switch(s){case'':/]/} · ─── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Unexpected braces in empty case clause. ╭─[switch_case_braces.tsx:1:29] 1 │ switch(something) { case 1: {} case 2: {console.log('something'); break;}} · ── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Remove braces in empty case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:37] 1 │ switch(something) { case 1: case 2: console.log('something'); break;} · ──────────────────────────────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Unexpected braces in empty case clause. ╭─[switch_case_braces.tsx:1:23] 1 │ switch(foo) { case 1: {} case 2: {} default: { doSomething(); } } · ── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Remove braces in empty case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Unexpected braces in empty case clause. ╭─[switch_case_braces.tsx:1:34] 1 │ switch(foo) { case 1: {} case 2: {} default: { doSomething(); } } · ── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Remove braces in empty case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Unexpected braces in empty case clause. ╭─[switch_case_braces.tsx:1:23] 1 │ switch(foo) { case 1: { /* fallthrough */ } default: {}/* fallthrough */ case 3: { doSomething(); break; } } · ───────────────────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Remove braces in empty case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Unexpected braces in empty case clause. ╭─[switch_case_braces.tsx:1:54] 1 │ switch(foo) { case 1: { /* fallthrough */ } default: {}/* fallthrough */ case 3: { doSomething(); break; } } · ── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Remove braces in empty case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:24] 1 │ switch(foo) { default: doSomething(); } · ────────────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:23] 1 │ switch(foo) { case 1: { doSomething(); } break; /* <-- This should be between braces */ } · ───────────────────────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:24] 1 │ switch(foo) { default: label: {} } · ───────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause. - ⚠ eslint-plugin-unicorn(switch-case-braces): Empty switch case shouldn't have braces and not-empty case should have braces around it. + ⚠ eslint-plugin-unicorn(switch-case-braces): Missing braces in case clause. ╭─[switch_case_braces.tsx:1:82] 1 │ switch(something) { case 1: case 2: { console.log('something'); break; } case 3: console.log('something else'); } · ────────────────────────────── ╰──── - help: There is less visual clutter for empty cases and proper scope for non-empty cases. + help: Add Braces for case clause.