From 2548653263b12b9b9cea99683e823d992ed955db Mon Sep 17 00:00:00 2001 From: Brian Liu Date: Sun, 20 Oct 2024 12:40:32 +1100 Subject: [PATCH 1/5] Added linter unicorn/consistent-empty-array-spread --- crates/oxc_linter/src/rules.rs | 2 + .../unicorn/consistent_empty_array_spread.rs | 158 ++++++++++++++++++ .../consistent_empty_array_spread.snap | 37 ++++ 3 files changed, 197 insertions(+) create mode 100644 crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs create mode 100644 crates/oxc_linter/src/snapshots/consistent_empty_array_spread.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 4680894cb2a6c..37af5bf5824e9 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -272,6 +272,7 @@ mod react_perf { mod unicorn { pub mod catch_error_name; + pub mod consistent_empty_array_spread; pub mod consistent_function_scoping; pub mod empty_brace_spaces; pub mod error_message; @@ -846,6 +847,7 @@ oxc_macros::declare_all_lint_rules! { typescript::prefer_ts_expect_error, typescript::triple_slash_reference, unicorn::catch_error_name, + unicorn::consistent_empty_array_spread, unicorn::consistent_function_scoping, unicorn::empty_brace_spaces, unicorn::error_message, diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs new file mode 100644 index 0000000000000..5ee1df7a37993 --- /dev/null +++ b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs @@ -0,0 +1,158 @@ +use std::borrow::Cow; + +use oxc_ast::{ast::Expression, AstKind}; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{ast_util::outermost_paren_parent, context::LintContext, rule::Rule, AstNode}; + +fn consistent_empty_array_spread_diagnostic(span: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("Prefer consistent types when spreading a ternary in an array literal.") + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct ConsistentEmptyArraySpread; + +declare_oxc_lint!( + /// ### What it does + /// + /// When spreading a ternary in an array, we can use both [] and '' as fallbacks, + /// but it's better to have consistent types in both branches. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```javascript + /// const array = [ + /// a, + /// ...(foo ? [b, c] : ''), + /// ]; + /// + /// const array = [ + /// a, + /// ...(foo ? 'bc' : []), + /// ]; + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```javascript + /// const array = [ + /// a, + /// ...(foo ? [b, c] : []), + /// ]; + /// + /// const array = [ + /// a, + /// ...(foo ? 'bc' : ''), + /// ]; + /// ``` + ConsistentEmptyArraySpread, + pedantic, + fix +); + +impl Rule for ConsistentEmptyArraySpread { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::ConditionalExpression(conditional_expr) = node.kind() else { + return; + }; + + let Some(parent) = outermost_paren_parent(node, ctx) else { + return; + }; + + let AstKind::SpreadElement(_) = parent.kind() else { + return; + }; + + let Some(parent) = outermost_paren_parent(parent, ctx) else { + return; + }; + + let AstKind::ArrayExpressionElement(_) = parent.kind() else { + return; + }; + + match ( + conditional_expr.consequent.get_inner_expression(), + conditional_expr.alternate.get_inner_expression(), + ) { + (Expression::ArrayExpression(_), Expression::StringLiteral(right_str_lit)) => { + if right_str_lit.value.is_empty() { + ctx.diagnostic_with_fix( + consistent_empty_array_spread_diagnostic(conditional_expr.span), + |fixer| fixer.replace(right_str_lit.span, Cow::Owned(format!("[]"))), + ); + } + } + (Expression::StringLiteral(_), Expression::ArrayExpression(right_array_expr)) => { + if right_array_expr.elements.is_empty() { + ctx.diagnostic_with_fix( + consistent_empty_array_spread_diagnostic(conditional_expr.span), + |fixer| fixer.replace(right_array_expr.span, Cow::Owned(format!("''"))), + ); + } + } + _ => {} + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + r"const array = foo ? [b, c] : []", + r"const array = foo ? 'bc' : ''", + r"const obj = { a, ...(foo ? {d: [b, c]} : {}) }", + r"const obj = { a, ...(foo ? {d: 'bc'} : {}) }", + r"const array = [ a, ...(foo ? [b, c] : []) ]", + r"const array = [ a, ...(foo ? 'bc' : '') ]", + ]; + + let fail: Vec<&str> = vec![ + r"const array = [ a, ...(foo ? [b, c] : '') ]", + r"const array = [ a, ...(foo ? 'bc' : []) ]", + r"const array = [ a, ...(foo ? ['str', 'str'] : '') ]", + r"const array = [ a, ...(foo ? [1, 2, 3] : '') ]", + r"const array = [ {}, ...(foo ? [{}, {}] : '') ]", + ]; + + let fix = vec![ + ( + r"const array = [ a, ...(foo ? [b, c] : '') ]", + r"const array = [ a, ...(foo ? [b, c] : []) ]", + None, + ), + ( + r"const array = [ a, ...(foo ? 'bc' : []) ]", + r"const array = [ a, ...(foo ? 'bc' : '') ]", + None, + ), + ( + r"const array = [ a, ...(foo ? ['str', 'str', 'str'] : '') ]", + r"const array = [ a, ...(foo ? ['str', 'str', 'str'] : []) ]", + None, + ), + ( + r"const array = [ a, ...(foo ? [1, 2, 3] : '') ]", + r"const array = [ a, ...(foo ? [1, 2, 3] : []) ]", + None, + ), + ( + r"const array = [ {}, ...(foo ? [{}, {}, {}] : '') ]", + r"const array = [ {}, ...(foo ? [{}, {}, {}] : []) ]", + None, + ), + ( + r"const array = [ a, ...(foo ? [b, c] : ''), b, ...(foo ? 'bc' : []), c, ...(foo ? [1, 2, 3] : '') ]", + r"const array = [ a, ...(foo ? [b, c] : []), b, ...(foo ? 'bc' : ''), c, ...(foo ? [1, 2, 3] : []) ]", + None, + ), + ]; + + Tester::new(ConsistentEmptyArraySpread::NAME, pass, fail).expect_fix(fix).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/consistent_empty_array_spread.snap b/crates/oxc_linter/src/snapshots/consistent_empty_array_spread.snap new file mode 100644 index 0000000000000..c9895f9863cc4 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/consistent_empty_array_spread.snap @@ -0,0 +1,37 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint-plugin-unicorn(consistent-empty-array-spread): Prefer consistent types when spreading a ternary in an array literal. + ╭─[consistent_empty_array_spread.tsx:1:24] + 1 │ const array = [ a, ...(foo ? [b, c] : '') ] + · ───────────────── + ╰──── + help: Replace `''` with `[]`. + + ⚠ eslint-plugin-unicorn(consistent-empty-array-spread): Prefer consistent types when spreading a ternary in an array literal. + ╭─[consistent_empty_array_spread.tsx:1:24] + 1 │ const array = [ a, ...(foo ? 'bc' : []) ] + · ─────────────── + ╰──── + help: Replace `[]` with `''`. + + ⚠ eslint-plugin-unicorn(consistent-empty-array-spread): Prefer consistent types when spreading a ternary in an array literal. + ╭─[consistent_empty_array_spread.tsx:1:24] + 1 │ const array = [ a, ...(foo ? ['str', 'str'] : '') ] + · ───────────────────────── + ╰──── + help: Replace `''` with `[]`. + + ⚠ eslint-plugin-unicorn(consistent-empty-array-spread): Prefer consistent types when spreading a ternary in an array literal. + ╭─[consistent_empty_array_spread.tsx:1:24] + 1 │ const array = [ a, ...(foo ? [1, 2, 3] : '') ] + · ──────────────────── + ╰──── + help: Replace `''` with `[]`. + + ⚠ eslint-plugin-unicorn(consistent-empty-array-spread): Prefer consistent types when spreading a ternary in an array literal. + ╭─[consistent_empty_array_spread.tsx:1:25] + 1 │ const array = [ {}, ...(foo ? [{}, {}] : '') ] + · ─────────────────── + ╰──── + help: Replace `''` with `[]`. From a6a75e7e0b9f0172fd5002a900b28e5650d44de2 Mon Sep 17 00:00:00 2001 From: Brian Liu Date: Sun, 20 Oct 2024 12:48:54 +1100 Subject: [PATCH 2/5] Fixed a linting issue --- .../src/rules/unicorn/consistent_empty_array_spread.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs index 5ee1df7a37993..3a8031e89d722 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use oxc_ast::{ast::Expression, AstKind}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -83,7 +81,7 @@ impl Rule for ConsistentEmptyArraySpread { if right_str_lit.value.is_empty() { ctx.diagnostic_with_fix( consistent_empty_array_spread_diagnostic(conditional_expr.span), - |fixer| fixer.replace(right_str_lit.span, Cow::Owned(format!("[]"))), + |fixer| fixer.replace(right_str_lit.span, "[]"), ); } } @@ -91,7 +89,7 @@ impl Rule for ConsistentEmptyArraySpread { if right_array_expr.elements.is_empty() { ctx.diagnostic_with_fix( consistent_empty_array_spread_diagnostic(conditional_expr.span), - |fixer| fixer.replace(right_array_expr.span, Cow::Owned(format!("''"))), + |fixer| fixer.replace(right_array_expr.span, "''"), ); } } From 38e2472bbf7c390d800a4623e09382b0159d8461 Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Sat, 19 Oct 2024 23:31:36 -0400 Subject: [PATCH 3/5] Update crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs --- .../src/rules/unicorn/consistent_empty_array_spread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs index 3a8031e89d722..7f0cb98a84ece 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs @@ -48,7 +48,7 @@ declare_oxc_lint!( /// ``` ConsistentEmptyArraySpread, pedantic, - fix + suggestion ); impl Rule for ConsistentEmptyArraySpread { From 3137adc1b6e2fa1493c443170aa73fface1d571b Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Sat, 19 Oct 2024 23:32:42 -0400 Subject: [PATCH 4/5] Update crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs --- .../src/rules/unicorn/consistent_empty_array_spread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs index 7f0cb98a84ece..64e250106afb7 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs @@ -79,7 +79,7 @@ impl Rule for ConsistentEmptyArraySpread { ) { (Expression::ArrayExpression(_), Expression::StringLiteral(right_str_lit)) => { if right_str_lit.value.is_empty() { - ctx.diagnostic_with_fix( + ctx.diagnostic_with_suggestion( consistent_empty_array_spread_diagnostic(conditional_expr.span), |fixer| fixer.replace(right_str_lit.span, "[]"), ); From 33657b8941c377bd8dac5c46cb4b9276e2c9e5bb Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Sat, 19 Oct 2024 23:32:48 -0400 Subject: [PATCH 5/5] Update crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs --- .../src/rules/unicorn/consistent_empty_array_spread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs index 64e250106afb7..288a128dcfc83 100644 --- a/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs +++ b/crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs @@ -87,7 +87,7 @@ impl Rule for ConsistentEmptyArraySpread { } (Expression::StringLiteral(_), Expression::ArrayExpression(right_array_expr)) => { if right_array_expr.elements.is_empty() { - ctx.diagnostic_with_fix( + ctx.diagnostic_with_suggestion( consistent_empty_array_spread_diagnostic(conditional_expr.span), |fixer| fixer.replace(right_array_expr.span, "''"), );