feat(linter) eslint plugin unicorn: prefer array flat (#1650)

This commit is contained in:
Cameron 2023-12-10 15:19:30 +00:00 committed by GitHub
parent ef740c3754
commit 295822d7d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 898 additions and 9 deletions

View file

@ -187,6 +187,7 @@ mod unicorn {
pub mod number_literal_case;
pub mod numeric_separators_style;
pub mod prefer_add_event_listener;
pub mod prefer_array_flat;
pub mod prefer_array_flat_map;
pub mod prefer_array_some;
pub mod prefer_blob_reading_methods;
@ -384,6 +385,7 @@ oxc_macros::declare_all_lint_rules! {
unicorn::numeric_separators_style,
unicorn::prefer_add_event_listener,
unicorn::prefer_array_flat_map,
unicorn::prefer_array_flat,
unicorn::prefer_array_some,
unicorn::prefer_blob_reading_methods,
unicorn::prefer_code_point,

View file

@ -64,7 +64,7 @@ impl Rule for NoArrayReduce {
return;
};
let Some(member_expr) = (call_expr).callee.get_member_expr() else {
let Some(member_expr) = call_expr.callee.get_member_expr() else {
return;
};

View file

@ -0,0 +1,416 @@
use oxc_ast::{
ast::{
Argument, ArrayExpressionElement, BindingPatternKind, CallExpression, Expression, Statement,
},
AstKind,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{
ast_util::is_method_call,
context::LintContext,
rule::Rule,
utils::get_first_parameter_name,
utils::{get_return_identifier_name, is_empty_array_expression, is_prototype_property},
AstNode,
};
#[derive(Debug, Error, Diagnostic)]
#[error("eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.")]
#[diagnostic(severity(warning), help(r"Call `.flat()` on the array instead."))]
struct PreferArrayFlatDiagnostic(#[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct PreferArrayFlat;
declare_oxc_lint!(
/// ### What it does
///
/// Prefers `Array#flat()` over legacy techniques to flatten arrays. ///
///
/// ### Why is this bad?
///
/// ES2019 introduced a new method [`Array#flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) that flatten arrays.
///
/// This rule aims to standardize the use of `Array#flat()` over legacy techniques to flatten arrays.
///
/// ### Example
/// ```javascript
/// // Bad
/// const foo = array.flatMap(x => x);
/// const foo = array.reduce((a, b) => a.concat(b), []);
/// const foo = array.reduce((a, b) => [...a, ...b], []);
/// const foo = [].concat(maybeArray);
/// const foo = [].concat(...array);
/// const foo = [].concat.apply([], array);
/// const foo = Array.prototype.concat.apply([], array);
/// const foo = Array.prototype.concat.call([], maybeArray);
/// const foo = Array.prototype.concat.call([], ...array);
///
/// // Good
/// const foo = array.flat();
/// const foo = [maybeArray].flat();
/// ```
PreferArrayFlat,
correctness
);
impl Rule for PreferArrayFlat {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::CallExpression(call_expr) = node.kind() else {
return;
};
check_array_flat_map_case(call_expr, ctx);
check_array_reduce_case(call_expr, ctx);
check_array_concat_case(call_expr, ctx);
check_array_prototype_concat_case(call_expr, ctx);
}
}
// `array.flatMap(x => x)`
fn check_array_flat_map_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) {
if !is_method_call(call_expr, None, Some(&["flatMap"]), Some(1), Some(1)) {
return;
}
let Argument::Expression(first_argument) = call_expr.arguments.get(0).unwrap() else {
return;
};
let Expression::ArrowExpression(first_argument) = first_argument else {
return;
};
if first_argument.r#async || first_argument.params.parameters_count() != 1 {
return;
}
let Some(first_param_name) = get_first_parameter_name(&first_argument.params) else {
return;
};
let Some(return_param_name) = get_return_identifier_name(&first_argument.body) else {
return;
};
if first_param_name != return_param_name {
return;
}
ctx.diagnostic(PreferArrayFlatDiagnostic(call_expr.span));
}
// `array.reduce((a, b) => a.concat(b), [])`
// `array.reduce((a, b) => [...a, ...b], [])`
fn check_array_reduce_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) {
if !is_method_call(call_expr, None, Some(&["reduce"]), Some(2), Some(2)) {
return;
}
let Argument::Expression(Expression::ArrowExpression(first_argument)) =
call_expr.arguments.get(0).unwrap()
else {
return;
};
let Argument::Expression(second_argument) = call_expr.arguments.get(1).unwrap() else {
return;
};
if first_argument.r#async
|| first_argument.params.parameters_count() != 2
|| !is_empty_array_expression(second_argument)
{
return;
}
let Some((first_parameter, second_parameter)) = ({
match (
&first_argument.params.items[0].pattern.kind,
&first_argument.params.items[1].pattern.kind,
) {
(
BindingPatternKind::BindingIdentifier(ref first_param),
BindingPatternKind::BindingIdentifier(second_param),
) => Some((&first_param.name, &second_param.name)),
_ => None,
}
}) else {
return;
};
let Some(Statement::ExpressionStatement(expr_stmt)) = first_argument.body.statements.get(0)
else {
return;
};
// `array.reduce((a, b) => a.concat(b), [])`
if let Expression::CallExpression(concat_call_expr) = &expr_stmt.expression {
if is_method_call(concat_call_expr, None, Some(&["concat"]), Some(1), Some(1)) {
if let Argument::Expression(Expression::Identifier(first_argument_ident)) =
&concat_call_expr.arguments[0]
{
if first_argument_ident.name != second_parameter {
return;
}
let Expression::Identifier(second_argument_ident) =
concat_call_expr.callee.get_member_expr().unwrap().object()
else {
return;
};
if second_argument_ident.name != first_parameter {
return;
}
ctx.diagnostic(PreferArrayFlatDiagnostic(call_expr.span));
}
}
}
if let Expression::ArrayExpression(array_expr) = &expr_stmt.expression {
if array_expr.elements.len() != 2 {
return;
}
let Some((first_element, second_element)) = ({
match (&array_expr.elements[0], &array_expr.elements[1]) {
(
ArrayExpressionElement::SpreadElement(first_element),
ArrayExpressionElement::SpreadElement(second_element),
) => match (&first_element.argument, &second_element.argument) {
(
Expression::Identifier(first_element),
Expression::Identifier(second_element),
) => Some((first_element, second_element)),
_ => None,
},
_ => None,
}
}) else {
return;
};
if first_element.name != first_parameter || second_element.name != second_parameter {
return;
}
ctx.diagnostic(PreferArrayFlatDiagnostic(call_expr.span));
};
}
// `[].concat(maybeArray)`
// `[].concat(...array)`
fn check_array_concat_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) {
if is_method_call(call_expr, None, Some(&["concat"]), Some(1), Some(1)) {
// `array.concat(maybeArray)`
if let Expression::ArrayExpression(array_expr) =
call_expr.callee.get_member_expr().unwrap().object()
{
if !array_expr.elements.is_empty() {
return;
}
ctx.diagnostic(PreferArrayFlatDiagnostic(call_expr.span));
}
}
}
// - `[].concat.apply([], array)` and `Array.prototype.concat.apply([], array)`
// - `[].concat.call([], maybeArray)` and `Array.prototype.concat.call([], maybeArray)`
// - `[].concat.call([], ...array)` and `Array.prototype.concat.call([], ...array)`
fn check_array_prototype_concat_case<'a>(call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) {
let Some(member_expr) = call_expr.callee.get_member_expr() else {
return;
};
if let Expression::MemberExpression(member_expr_obj) = member_expr.object() {
let is_call_call = is_method_call(call_expr, None, Some(&["call"]), Some(2), Some(2));
if (is_call_call || is_method_call(call_expr, None, Some(&["apply"]), Some(2), Some(2)))
&& is_prototype_property(member_expr_obj, "concat", Some("Array"))
{
if let Argument::Expression(first_argument) = &call_expr.arguments[0] {
if is_empty_array_expression(first_argument)
&& (is_call_call
|| !matches!(call_expr.arguments.get(1), Some(Argument::SpreadElement(_))))
{
ctx.diagnostic(PreferArrayFlatDiagnostic(call_expr.span));
}
}
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
r"array.flatMap",
r"new array.flatMap(x => x)",
r"flatMap(x => x)",
r"array.notFlatMap(x => x)",
r"array[flatMap](x => x)",
r"array.flatMap(x => x, thisArgument)",
r"array.flatMap(...[x => x])",
r"array.flatMap(function (x) { return x; })",
r"array.flatMap(async x => x)",
r"array.flatMap(function * (x) { return x;})",
r"array.flatMap(() => x)",
r"array.flatMap((x, y) => x)",
r"array.flatMap(x => y)",
r"new array.reduce((a, b) => a.concat(b), [])",
r"array.reduce",
r"reduce((a, b) => a.concat(b), [])",
r"array[reduce]((a, b) => a.concat(b), [])",
r"array.notReduce((a, b) => a.concat(b), [])",
r"array.reduce((a, b) => a.concat(b), [], EXTRA_ARGUMENT)",
r"array.reduce((a, b) => a.concat(b), NOT_EMPTY_ARRAY)",
r"array.reduce((a, b, extraParameter) => a.concat(b), [])",
r"array.reduce((a,) => a.concat(b), [])",
r"array.reduce(() => a.concat(b), [])",
r"array.reduce((a, b) => {return a.concat(b); }, [])",
r"array.reduce(function (a, b) { return a.concat(b); }, [])",
r"array.reduce((a, b) => b.concat(b), [])",
r"array.reduce((a, b) => a.concat(a), [])",
r"array.reduce((a, b) => b.concat(a), [])",
r"array.reduce((a, b) => a.notConcat(b), [])",
r"array.reduce((a, b) => a.concat, [])",
r"new array.reduce((a, b) => [...a, ...b], [])",
r"array[reduce]((a, b) => [...a, ...b], [])",
r"reduce((a, b) => [...a, ...b], [])",
r"array.notReduce((a, b) => [...a, ...b], [])",
r"array.reduce((a, b) => [...a, ...b], [], EXTRA_ARGUMENT)",
r"array.reduce((a, b) => [...a, ...b], NOT_EMPTY_ARRAY)",
r"array.reduce((a, b, extraParameter) => [...a, ...b], [])",
r"array.reduce((a,) => [...a, ...b], [])",
r"array.reduce(() => [...a, ...b], [])",
r"array.reduce((a, b) => {return [...a, ...b]; }, [])",
r"array.reduce(function (a, b) { return [...a, ...b]; }, [])",
r"array.reduce((a, b) => [...b, ...b], [])",
r"array.reduce((a, b) => [...a, ...a], [])",
r"array.reduce((a, b) => [...b, ...a], [])",
r"array.reduce((a, b) => [a, ...b], [])",
r"array.reduce((a, b) => [...a, b], [])",
r"array.reduce((a, b) => [a, b], [])",
r"array.reduce((a, b) => [...a, ...b, c], [])",
r"array.reduce((a, b) => [...a, ...b,,], [])",
r"array.reduce((a, b) => [,...a, ...b], [])",
r"array.reduce((a, b) => [, ], [])",
r"array.reduce((a, b) => [, ,], [])",
r"[].concat",
r"new [].concat(array)",
r"[][concat](array)",
r"[].notConcat(array)",
r"[,].concat(array)",
r"({}).concat(array)",
r"[].concat()",
r"[].concat(array, EXTRA_ARGUMENT)",
r"new [].concat(...array)",
r"[][concat](...array)",
r"[].notConcat(...array)",
r"[,].concat(...array)",
r"({}).concat(...array)",
r"[].concat()",
r"[].concat(...array, EXTRA_ARGUMENT)",
r"new [].concat.apply([], array)",
r"[].concat.apply",
r"[].concat.apply([], ...array)",
r"[].concat.apply([], array, EXTRA_ARGUMENT)",
r"[].concat.apply([])",
r"[].concat.apply(NOT_EMPTY_ARRAY, array)",
r"[].concat.apply([,], array)",
r"[,].concat.apply([], array)",
r"[].concat[apply]([], array)",
r"[][concat].apply([], array)",
r"[].concat.notApply([], array)",
r"[].notConcat.apply([], array)",
r"new Array.prototype.concat.apply([], array)",
r"Array.prototype.concat.apply",
r"Array.prototype.concat.apply([], ...array)",
r"Array.prototype.concat.apply([], array, EXTRA_ARGUMENT)",
r"Array.prototype.concat.apply([])",
r"Array.prototype.concat.apply(NOT_EMPTY_ARRAY, array)",
r"Array.prototype.concat.apply([,], array)",
r"Array.prototype.concat[apply]([], array)",
r"Array.prototype[concat].apply([], array)",
r"Array[prototype].concat.apply([], array)",
r"Array.prototype.concat.notApply([], array)",
r"Array.prototype.notConcat.apply([], array)",
r"Array.notPrototype.concat.apply([], array)",
r"NotArray.prototype.concat.apply([], array)",
r"Array.prototype?.concat.apply([], array)",
r"object.Array.prototype.concat.apply([], array)",
];
let fail = vec![
r"array.flatMap(x => x)",
r"function foo(){return[].flatMap(x => x)}",
r"foo.flatMap(x => x) instanceof Array",
r"array.reduce((a, b) => a.concat(b), [])",
r"function foo(){return[].reduce((a, b) => a.concat(b), [])}",
r"array.reduce((a, b) => [...a, ...b], [])",
r"array.reduce((a, b) => [...a, ...b,], [])",
r"function foo(){return[].reduce((a, b) => [...a, ...b,], [])}",
r"[].concat(maybeArray)",
r"[].concat( ((0, maybeArray)) )",
r"[].concat( ((maybeArray)) )",
r"[].concat( [foo] )",
r"[].concat( [[foo]] )",
r"function foo(){return[].concat(maybeArray)}",
r"[].concat(...array)",
r"[].concat(...(( array )))",
r"[].concat(...(( [foo] )))",
r"[].concat(...(( [[foo]] )))",
r"function foo(){return[].concat(...array)}",
r"class A extends[].concat(...array){}",
r"const A = class extends[].concat(...array){}",
r"[].concat.apply([], array)",
r"[].concat.apply([], ((0, array)))",
r"[].concat.apply([], ((array)))",
r"[].concat.apply([], [foo])",
r"[].concat.apply([], [[foo]])",
r"[].concat.call([], maybeArray)",
r"[].concat.call([], ((0, maybeArray)))",
r"[].concat.call([], ((maybeArray)))",
r"[].concat.call([], [foo])",
r"[].concat.call([], [[foo]])",
r"[].concat.call([], ...array)",
r"[].concat.call([], ...((0, array)))",
r"[].concat.call([], ...((array)))",
r"[].concat.call([], ...[foo])",
r"[].concat.call([], ...[[foo]])",
r"function foo(){return[].concat.call([], ...array)}",
r"Array.prototype.concat.apply([], array)",
r"Array.prototype.concat.apply([], ((0, array)))",
r"Array.prototype.concat.apply([], ((array)))",
r"Array.prototype.concat.apply([], [foo])",
r"Array.prototype.concat.apply([], [[foo]])",
r"Array.prototype.concat.call([], maybeArray)",
r"Array.prototype.concat.call([], ((0, maybeArray)))",
r"Array.prototype.concat.call([], ((maybeArray)))",
r"Array.prototype.concat.call([], [foo])",
r"Array.prototype.concat.call([], [[foo]])",
r"Array.prototype.concat.call([], ...array)",
r"Array.prototype.concat.call([], ...((0, array)))",
r"Array.prototype.concat.call([], ...((array)))",
r"Array.prototype.concat.call([], ...[foo])",
r"Array.prototype.concat.call([], ...[[foo]])",
r"[].concat.apply([], array)",
r"Array.prototype.concat.apply([], array)",
r"Array.prototype.concat.apply([], (0, array))",
r"Array.prototype.concat.call([], (0, array))",
r"async function a() { return [].concat(await getArray()); }",
r"[].concat(some./**/array)",
r"[/**/].concat(some./**/array)",
r"[/**/].concat(some.array)",
];
Tester::new_without_config(PreferArrayFlat::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,425 @@
---
source: crates/oxc_linter/src/tester.rs
expression: prefer_array_flat
---
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ array.flatMap(x => x)
· ─────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].flatMap(x => x)}
· ──────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ foo.flatMap(x => x) instanceof Array
· ───────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ array.reduce((a, b) => a.concat(b), [])
· ───────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].reduce((a, b) => a.concat(b), [])}
· ────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ array.reduce((a, b) => [...a, ...b], [])
· ────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ array.reduce((a, b) => [...a, ...b,], [])
· ─────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].reduce((a, b) => [...a, ...b,], [])}
· ──────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(maybeArray)
· ─────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat( ((0, maybeArray)) )
· ──────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat( ((maybeArray)) )
· ───────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat( [foo] )
· ──────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat( [[foo]] )
· ────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].concat(maybeArray)}
· ─────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(...array)
· ───────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(...(( array )))
· ─────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(...(( [foo] )))
· ─────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(...(( [[foo]] )))
· ───────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].concat(...array)}
· ───────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ class A extends[].concat(...array){}
· ───────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ const A = class extends[].concat(...array){}
· ───────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], array)
· ──────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], ((0, array)))
· ─────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], ((array)))
· ──────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], [foo])
· ──────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], [[foo]])
· ────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], maybeArray)
· ──────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ((0, maybeArray)))
· ─────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ((maybeArray)))
· ──────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], [foo])
· ─────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], [[foo]])
· ───────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ...array)
· ────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ...((0, array)))
· ───────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ...((array)))
· ────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ...[foo])
· ────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.call([], ...[[foo]])
· ──────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ function foo(){return[].concat.call([], ...array)}
· ────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], array)
· ───────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], ((0, array)))
· ──────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], ((array)))
· ───────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], [foo])
· ───────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], [[foo]])
· ─────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], maybeArray)
· ───────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ((0, maybeArray)))
· ──────────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ((maybeArray)))
· ───────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], [foo])
· ──────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], [[foo]])
· ────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ...array)
· ─────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ...((0, array)))
· ────────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ...((array)))
· ─────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ...[foo])
· ─────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], ...[[foo]])
· ───────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat.apply([], array)
· ──────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], array)
· ───────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.apply([], (0, array))
· ────────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ Array.prototype.concat.call([], (0, array))
· ───────────────────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ async function a() { return [].concat(await getArray()); }
· ───────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [].concat(some./**/array)
· ─────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [/**/].concat(some./**/array)
· ─────────────────────────────
╰────
help: Call `.flat()` on the array instead.
⚠ eslint-plugin-unicorn(prefer-array-flat): Prefer Array#flat() over legacy techniques to flatten arrays.
╭─[prefer_array_flat.tsx:1:1]
1 │ [/**/].concat(some.array)
· ─────────────────────────
╰────
help: Call `.flat()` on the array instead.

View file

@ -1,7 +1,10 @@
mod boolean;
pub use self::boolean::*;
use oxc_ast::{
ast::{Expression, LogicalExpression, MemberExpression, Statement},
ast::{
BindingPatternKind, Expression, FormalParameters, FunctionBody, LogicalExpression,
MemberExpression, Statement,
},
AstKind,
};
use oxc_semantic::AstNode;
@ -61,13 +64,7 @@ pub fn is_prototype_property(
match object {
// `[].method`
Some("Array") => {
if let Expression::ArrayExpression(array_expr) = member_expr.object() {
array_expr.elements.len() == 0
} else {
false
}
}
Some("Array") => is_empty_array_expression(member_expr.object()),
// `{}.method`
Some("Object") => {
@ -81,6 +78,14 @@ pub fn is_prototype_property(
}
}
pub fn is_empty_array_expression(expr: &Expression) -> bool {
if let Expression::ArrayExpression(array_expr) = expr {
array_expr.elements.len() == 0
} else {
false
}
}
pub fn is_logical_expression(node: &AstNode) -> bool {
matches!(
node.kind(),
@ -90,3 +95,44 @@ pub fn is_logical_expression(node: &AstNode) -> bool {
})
)
}
// gets the name of the first parameter of a function
pub fn get_first_parameter_name<'a>(arg: &'a FormalParameters) -> Option<&'a str> {
let first_func_param = arg.items.get(0)?;
let BindingPatternKind::BindingIdentifier(first_func_param) = &first_func_param.pattern.kind
else {
return None;
};
Some(first_func_param.name.as_str())
}
pub fn get_return_identifier_name<'a>(body: &'a FunctionBody<'_>) -> Option<&'a str> {
match body.statements.get(0)? {
Statement::BlockStatement(block_stmt) => {
let Statement::ReturnStatement(return_stmt) = block_stmt.body.get(0)? else {
return None;
};
let Some(Expression::Identifier(ident)) = return_stmt.argument.as_ref() else {
return None;
};
Some(ident.name.as_str())
}
Statement::ReturnStatement(return_stmt) => {
let return_expr = return_stmt.argument.as_ref()?;
match return_expr {
Expression::Identifier(ident) => Some(ident.name.as_str()),
_ => None,
}
}
Statement::ExpressionStatement(expr_stmt) => {
let Expression::Identifier(ident) = &expr_stmt.expression else {
return None;
};
Some(ident.name.as_str())
}
_ => None,
}
}