Skip to content

Commit

Permalink
feat(linter): unicorn/switch-cases-braces support options (#8704)
Browse files Browse the repository at this point in the history
close #8492
  • Loading branch information
1zumii authored Jan 25, 2025
1 parent 6589c3b commit e8e6917
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 30 deletions.
88 changes: 80 additions & 8 deletions crates/oxc_linter/src/rules/unicorn/switch_case_braces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
};
Expand All @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
44 changes: 22 additions & 22 deletions crates/oxc_linter/src/snapshots/unicorn_switch_case_braces.snap
Original file line number Diff line number Diff line change
@@ -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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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]
1switch(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.

0 comments on commit e8e6917

Please sign in to comment.