feat(linter): unicorn/switch-cases-braces support options (#8704)

close #8492
This commit is contained in:
1zumii 2025-01-25 17:30:01 +08:00 committed by GitHub
parent 6589c3bbb3
commit e8e69179d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 102 additions and 30 deletions

View file

@ -5,16 +5,33 @@ use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode}; use crate::{context::LintContext, rule::Rule, AstNode};
fn switch_case_braces_diagnostic(span: Span) -> OxcDiagnostic { #[derive(Clone, Copy)]
OxcDiagnostic::warn( enum Diagnostic {
" Empty switch case shouldn't have braces and not-empty case should have braces around it.", EmptyClause,
) MissingBraces,
.with_help("There is less visual clutter for empty cases and proper scope for non-empty cases.") 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) .with_label(span)
} }
#[derive(Debug, Default, Clone)] #[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!( declare_oxc_lint!(
/// ### What it does /// ### What it does
@ -41,6 +58,12 @@ declare_oxc_lint!(
); );
impl Rule for SwitchCaseBraces { 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>) { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::SwitchStatement(switch) = node.kind() else { let AstKind::SwitchStatement(switch) = node.kind() else {
return; return;
@ -56,13 +79,54 @@ impl Rule for SwitchCaseBraces {
Statement::BlockStatement(case_block) => { Statement::BlockStatement(case_block) => {
if case_block.body.is_empty() { if case_block.body.is_empty() {
ctx.diagnostic_with_fix( 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), |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(_) => {} 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 { let Some(first_statement) = &case.consequent.first() else {
return; return;
}; };
@ -74,7 +138,10 @@ impl Rule for SwitchCaseBraces {
Span::new(first_statement.span().start, last_statement.span().end); Span::new(first_statement.span().start, last_statement.span().end);
ctx.diagnostic_with_fix( ctx.diagnostic_with_fix(
switch_case_braces_diagnostic(case_body_span), switch_case_braces_diagnostic(
case_body_span,
Diagnostic::MissingBraces,
),
|fixer| { |fixer| {
let modified_code = { let modified_code = {
let mut formatter = fixer.codegen(); let mut formatter = fixer.codegen();
@ -155,6 +222,11 @@ fn test() {
None, None,
), ),
("switch(s){case'':/]/}", "switch(s){case '': {/]/}}", 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) Tester::new(SwitchCaseBraces::NAME, SwitchCaseBraces::PLUGIN, pass, fail)

View file

@ -1,79 +1,79 @@
--- ---
source: crates/oxc_linter/src/tester.rs 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] ╭─[switch_case_braces.tsx:1:18]
1 │ switch(s){case'':/]/} 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] ╭─[switch_case_braces.tsx:1:29]
1 │ switch(something) { case 1: {} case 2: {console.log('something'); break;}} 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] ╭─[switch_case_braces.tsx:1:37]
1 │ switch(something) { case 1: case 2: console.log('something'); break;} 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] ╭─[switch_case_braces.tsx:1:23]
1 │ switch(foo) { case 1: {} case 2: {} default: { doSomething(); } } 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] ╭─[switch_case_braces.tsx:1:34]
1 │ switch(foo) { case 1: {} case 2: {} default: { doSomething(); } } 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] ╭─[switch_case_braces.tsx:1:23]
1 │ switch(foo) { case 1: { /* fallthrough */ } default: {}/* fallthrough */ case 3: { doSomething(); break; } } 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] ╭─[switch_case_braces.tsx:1:54]
1 │ switch(foo) { case 1: { /* fallthrough */ } default: {}/* fallthrough */ case 3: { doSomething(); break; } } 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] ╭─[switch_case_braces.tsx:1:24]
1 │ switch(foo) { default: doSomething(); } 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] ╭─[switch_case_braces.tsx:1:23]
1 │ switch(foo) { case 1: { doSomething(); } break; /* <-- This should be between braces */ } 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] ╭─[switch_case_braces.tsx:1:24]
1 │ switch(foo) { default: label: {} } 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] ╭─[switch_case_braces.tsx:1:82]
1 │ switch(something) { case 1: case 2: { console.log('something'); break; } case 3: console.log('something else'); } 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.