Skip to content

Commit

Permalink
feat(linter): add unicorn/consistent-empty-array-spread (#6695)
Browse files Browse the repository at this point in the history
[unicorn/consistent-empty-array-spread](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v56.0.0/docs/rules/consistent-empty-array-spread.md)
of #684

---------

Co-authored-by: Brian Liu <[email protected]>
Co-authored-by: Don Isaac <[email protected]>
  • Loading branch information
3 people authored Oct 20, 2024
1 parent a5de230 commit 45f02d5
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
156 changes: 156 additions & 0 deletions crates/oxc_linter/src/rules/unicorn/consistent_empty_array_spread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
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,
suggestion
);

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_suggestion(
consistent_empty_array_spread_diagnostic(conditional_expr.span),
|fixer| fixer.replace(right_str_lit.span, "[]"),
);
}
}
(Expression::StringLiteral(_), Expression::ArrayExpression(right_array_expr)) => {
if right_array_expr.elements.is_empty() {
ctx.diagnostic_with_suggestion(
consistent_empty_array_spread_diagnostic(conditional_expr.span),
|fixer| fixer.replace(right_array_expr.span, "''"),
);
}
}
_ => {}
}
}
}

#[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();
}
37 changes: 37 additions & 0 deletions crates/oxc_linter/src/snapshots/consistent_empty_array_spread.snap
Original file line number Diff line number Diff line change
@@ -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]
1const 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]
1const 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]
1const 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]
1const 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]
1const array = [ {}, ...(foo ? [{}, {}] : '') ]
· ───────────────────
╰────
help: Replace `''` with `[]`.

0 comments on commit 45f02d5

Please sign in to comment.