mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(linter): improve no-accumulating-spread (#5302)
VSCode has a couple violations. examples:
```
x oxc(no-accumulating-spread): Do not spread accumulators in loops
,-[src/vs/workbench/services/textMate/common/TMGrammarFactory.ts:65:5]
64 | let injections: string[] = [];
65 | for (let i = 1; i <= scopeParts.length; i++) {
: ^|^
: `-- For this loop
66 | const subScopeName = scopeParts.slice(0, i).join('.');
67 | injections = [...injections, ...(this._injections[subScopeName] || [])];
: ^^^^^^|^^^^^^
: `-- From this spread
68 | }
`----
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
x oxc(no-accumulating-spread): Do not spread accumulators in loops
,-[src/vs/base/common/actions.ts:205:3]
204 | let out: IAction[] = [];
205 | for (const list of actionLists) {
: ^|^
: `-- For this loop
206 | if (!list.length) {
207 | // skip
208 | } else if (out.length) {
209 | out = [...out, new Separator(), ...list];
: ^^^|^^
: `-- From this spread
210 | } else {
`----
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
help: It looks like you're spreading an `Array`. Consider using the `Array.push` or `Array.concat` methods to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
x oxc(no-accumulating-spread): Do not spread accumulators in loops
,-[src/vs/workbench/contrib/extensions/browser/extensionsActions.ts:302:3]
301 | let actions: IAction[] = [];
302 | for (const visibleActions of actionsGroups) {
: ^|^
: `-- For this loop
303 | if (visibleActions.length) {
304 | actions = [...actions, ...visibleActions, new Separator()];
: ^^^^^|^^^^
: `-- From this spread
305 | }
`----
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
x oxc(no-accumulating-spread): Do not spread accumulators in loops
,-[src/vs/workbench/contrib/extensions/browser/extensionsActions.ts:1141:3]
1140 | let actions: IAction[] = [];
1141 | for (const menuActions of menuActionGroups) {
: ^|^
: `-- For this loop
1142 | actions = [...actions, ...menuActions, new Separator()];
: ^^^^^|^^^^
: `-- From this spread
1143 | }
`----
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
x oxc(no-accumulating-spread): Do not spread accumulators in loops
,-[src/vs/workbench/contrib/extensions/browser/extensionsViews.ts:334:4]
333 | let actions: IAction[] = [];
334 | for (const menuActions of groups) {
: ^|^
: `-- For this loop
335 | actions = [...actions, ...menuActions, new Separator()];
: ^^^^^|^^^^
: `-- From this spread
336 | }
`----
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
Using spreads within accumulators leads to `O(n^2)` time complexity.
```
This commit is contained in:
parent
da8aa1873a
commit
b1037372b7
2 changed files with 317 additions and 52 deletions
|
|
@ -1,11 +1,14 @@
|
||||||
use oxc_ast::{
|
use oxc_ast::{
|
||||||
ast::{Argument, BindingPatternKind, CallExpression, Expression},
|
ast::{
|
||||||
|
Argument, BindingPatternKind, CallExpression, Expression, ForInStatement, ForOfStatement,
|
||||||
|
ForStatement, VariableDeclarationKind,
|
||||||
|
},
|
||||||
AstKind,
|
AstKind,
|
||||||
};
|
};
|
||||||
use oxc_diagnostics::OxcDiagnostic;
|
use oxc_diagnostics::OxcDiagnostic;
|
||||||
use oxc_macros::declare_oxc_lint;
|
use oxc_macros::declare_oxc_lint;
|
||||||
use oxc_semantic::SymbolId;
|
use oxc_semantic::{AstNodeId, SymbolId};
|
||||||
use oxc_span::Span;
|
use oxc_span::{GetSpan, Span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_util::{call_expr_method_callee_info, is_method_call},
|
ast_util::{call_expr_method_callee_info, is_method_call},
|
||||||
|
|
@ -14,30 +17,39 @@ use crate::{
|
||||||
AstNode,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn likely_array(span0: Span, span1: Span) -> OxcDiagnostic {
|
fn reduce_likely_array_spread_diagnostic(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
|
||||||
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
||||||
.with_help("It looks like you're spreading an `Array`. Consider using the `Array.push` or `Array.concat` methods to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
.with_help("It looks like you're spreading an `Array`. Consider using the `Array.push` or `Array.concat` methods to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
||||||
.with_labels([
|
.with_labels([
|
||||||
span0.label("From this spread"),
|
spread_span.label("From this spread"),
|
||||||
span1.label("For this reduce")
|
reduce_span.label("For this reduce")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn likely_object(span0: Span, span1: Span) -> OxcDiagnostic {
|
fn reduce_likely_object_spread_diagnostic(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
|
||||||
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
||||||
.with_help("It looks like you're spreading an `Object`. Consider using the `Object.assign` or assignment operators to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
.with_help("It looks like you're spreading an `Object`. Consider using the `Object.assign` or assignment operators to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
||||||
.with_labels([
|
.with_labels([
|
||||||
span0.label("From this spread"),
|
spread_span.label("From this spread"),
|
||||||
span1.label("For this reduce")
|
reduce_span.label("For this reduce")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unknown(span0: Span, span1: Span) -> OxcDiagnostic {
|
fn reduce_unknown(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
|
||||||
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
OxcDiagnostic::warn("Do not spread accumulators in Array.prototype.reduce()")
|
||||||
.with_help("Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
.with_help("Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
||||||
.with_labels([
|
.with_labels([
|
||||||
span0.label("From this spread"),
|
spread_span.label("From this spread"),
|
||||||
span1.label("For this reduce")
|
reduce_span.label("For this reduce")
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_spread_diagnostic(spread_span: Span, loop_span: Span) -> OxcDiagnostic {
|
||||||
|
OxcDiagnostic::warn("Do not spread accumulators in loops")
|
||||||
|
.with_help("Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.\nUsing spreads within accumulators leads to `O(n^2)` time complexity.")
|
||||||
|
.with_labels([
|
||||||
|
spread_span.label("From this spread"),
|
||||||
|
loop_span.label("For this loop")
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +58,7 @@ pub struct NoAccumulatingSpread;
|
||||||
|
|
||||||
declare_oxc_lint!(
|
declare_oxc_lint!(
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Prevents using object or array spreads on accumulators in `Array.prototype.reduce()`.
|
/// Prevents using object or array spreads on accumulators in `Array.prototype.reduce()` and in loops.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Object and array spreads create a new object or array on each iteration.
|
/// Object and array spreads create a new object or array on each iteration.
|
||||||
|
|
@ -75,12 +87,16 @@ declare_oxc_lint!(
|
||||||
/// acc[el] = { ...obj[el] }
|
/// acc[el] = { ...obj[el] }
|
||||||
/// return acc
|
/// return acc
|
||||||
/// }, {})
|
/// }, {})
|
||||||
|
///
|
||||||
|
/// let foo = []; for (let i = 0; i < 10; i++) { foo.push(i); }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Fail
|
/// Fail
|
||||||
/// ```javascript
|
/// ```javascript
|
||||||
/// arr.reduce((acc, x) => ({ ...acc, [x]: fn(x) }), {})
|
/// arr.reduce((acc, x) => ({ ...acc, [x]: fn(x) }), {})
|
||||||
/// Object.keys(obj).reduce((acc, el) => ({ ...acc, [el]: fn(el) }), {})
|
/// Object.keys(obj).reduce((acc, el) => ({ ...acc, [el]: fn(el) }), {})
|
||||||
|
///
|
||||||
|
/// let foo = []; for (let i = 0; i < 10; i++) { foo = [...foo, i]; }
|
||||||
/// ```
|
/// ```
|
||||||
NoAccumulatingSpread,
|
NoAccumulatingSpread,
|
||||||
perf,
|
perf,
|
||||||
|
|
@ -96,7 +112,6 @@ impl Rule for NoAccumulatingSpread {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let nodes = ctx.semantic().nodes();
|
|
||||||
let symbols = ctx.semantic().symbols();
|
let symbols = ctx.semantic().symbols();
|
||||||
|
|
||||||
// get the AST node + symbol id of the declaration of the identifier
|
// get the AST node + symbol id of the declaration of the identifier
|
||||||
|
|
@ -111,6 +126,25 @@ impl Rule for NoAccumulatingSpread {
|
||||||
let Some(declaration) = ctx.semantic().nodes().parent_node(declaration_id) else {
|
let Some(declaration) = ctx.semantic().nodes().parent_node(declaration_id) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
check_reduce_usage(declaration, referenced_symbol_id, spread.span, ctx);
|
||||||
|
check_loop_usage(
|
||||||
|
declaration,
|
||||||
|
ctx.semantic().nodes().get_node(declaration_id),
|
||||||
|
referenced_symbol_id,
|
||||||
|
node.id(),
|
||||||
|
spread.span,
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_reduce_usage<'a>(
|
||||||
|
declaration: &AstNode<'a>,
|
||||||
|
referenced_symbol_id: SymbolId,
|
||||||
|
spread_span: Span,
|
||||||
|
ctx: &LintContext<'a>,
|
||||||
|
) {
|
||||||
let AstKind::FormalParameters(params) = declaration.kind() else {
|
let AstKind::FormalParameters(params) = declaration.kind() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -130,44 +164,104 @@ impl Rule for NoAccumulatingSpread {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the declaration resides within a call to reduce()
|
// Check if the declaration resides within a call to reduce()
|
||||||
for parent in nodes.iter_parents(declaration.id()) {
|
for parent in ctx.nodes().iter_parents(declaration.id()) {
|
||||||
if let AstKind::CallExpression(call_expr) = parent.kind() {
|
if let AstKind::CallExpression(call_expr) = parent.kind() {
|
||||||
if is_method_call(
|
if is_method_call(call_expr, None, Some(&["reduce", "reduceRight"]), Some(1), Some(2)) {
|
||||||
call_expr,
|
ctx.diagnostic(get_reduce_diagnostic(call_expr, spread_span));
|
||||||
None,
|
|
||||||
Some(&["reduce", "reduceRight"]),
|
|
||||||
Some(1),
|
|
||||||
Some(2),
|
|
||||||
) {
|
|
||||||
ctx.diagnostic(get_diagnostic(call_expr, spread.span));
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_loop_usage<'a>(
|
||||||
|
declaration_node: &AstNode<'a>,
|
||||||
|
declarator: &AstNode<'a>,
|
||||||
|
referenced_symbol_id: SymbolId,
|
||||||
|
spread_node_id: AstNodeId,
|
||||||
|
spread_span: Span,
|
||||||
|
ctx: &LintContext<'a>,
|
||||||
|
) {
|
||||||
|
let AstKind::VariableDeclaration(declaration) = declaration_node.kind() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matches!(declaration.kind, VariableDeclarationKind::Let) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_diagnostic<'a>(call_expr: &'a CallExpression<'a>, spread_span: Span) -> OxcDiagnostic {
|
if !matches!(declarator.kind(), AstKind::VariableDeclarator(_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(write_reference) =
|
||||||
|
ctx.semantic().symbol_references(referenced_symbol_id).find(|r| r.is_write())
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(assignment_target) = ctx.nodes().parent_node(write_reference.node_id()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let AstKind::SimpleAssignmentTarget(_) = assignment_target.kind() else { return };
|
||||||
|
|
||||||
|
let Some(assignment_expr) = ctx.nodes().parent_node(assignment_target.id()) else { return };
|
||||||
|
if !matches!(assignment_expr.kind(), AstKind::AssignmentTarget(_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(assignment) = ctx.nodes().parent_node(assignment_expr.id()) else { return };
|
||||||
|
let AstKind::AssignmentExpression(assignment_expression) = assignment.kind() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match assignment_expression.right.get_inner_expression() {
|
||||||
|
Expression::ArrayExpression(array_expr)
|
||||||
|
if array_expr.span.contains_inclusive(spread_span) => {}
|
||||||
|
Expression::ObjectExpression(object_expr)
|
||||||
|
if object_expr.span.contains_inclusive(spread_span) => {}
|
||||||
|
_ => return,
|
||||||
|
}
|
||||||
|
|
||||||
|
for parent in ctx.nodes().iter_parents(spread_node_id) {
|
||||||
|
if let Some(loop_span) = get_loop_span(parent.kind()) {
|
||||||
|
if !parent.kind().span().contains_inclusive(declaration.span)
|
||||||
|
&& parent.kind().span().contains_inclusive(spread_span)
|
||||||
|
{
|
||||||
|
ctx.diagnostic(loop_spread_diagnostic(spread_span, loop_span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_loop_span(ast_kind: AstKind) -> Option<Span> {
|
||||||
|
match ast_kind {
|
||||||
|
AstKind::ForStatement(ForStatement { span, .. })
|
||||||
|
| AstKind::ForOfStatement(ForOfStatement { span, .. })
|
||||||
|
| AstKind::ForInStatement(ForInStatement { span, .. }) => Some(Span::sized(span.start, 3)),
|
||||||
|
AstKind::WhileStatement(while_stmt) => Some(Span::sized(while_stmt.span.start, 5)),
|
||||||
|
AstKind::DoWhileStatement(do_stmt) => Some(Span::sized(do_stmt.span.start, 2)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reduce_diagnostic<'a>(
|
||||||
|
call_expr: &'a CallExpression<'a>,
|
||||||
|
spread_span: Span,
|
||||||
|
) -> OxcDiagnostic {
|
||||||
// unwrap is safe because we already checked that this is a reduce call
|
// unwrap is safe because we already checked that this is a reduce call
|
||||||
let (reduce_call_span, _) = call_expr_method_callee_info(call_expr).unwrap();
|
let (reduce_call_span, _) = call_expr_method_callee_info(call_expr).unwrap();
|
||||||
|
|
||||||
if let Some(second_arg) = call_expr.arguments.get(1).and_then(Argument::as_expression) {
|
if let Some(second_arg) = call_expr.arguments.get(1).and_then(Argument::as_expression) {
|
||||||
let second_arg = second_arg.without_parenthesized();
|
let second_arg = second_arg.get_inner_expression();
|
||||||
let second_arg =
|
|
||||||
if let Expression::TSAsExpression(as_expr) = second_arg.without_parenthesized() {
|
|
||||||
as_expr.expression.without_parenthesized()
|
|
||||||
} else {
|
|
||||||
second_arg
|
|
||||||
};
|
|
||||||
|
|
||||||
if matches!(second_arg, Expression::ObjectExpression(_)) {
|
if matches!(second_arg, Expression::ObjectExpression(_)) {
|
||||||
return likely_object(spread_span, reduce_call_span);
|
return reduce_likely_object_spread_diagnostic(spread_span, reduce_call_span);
|
||||||
} else if matches!(second_arg, Expression::ArrayExpression(_)) {
|
} else if matches!(second_arg, Expression::ArrayExpression(_)) {
|
||||||
return likely_array(spread_span, reduce_call_span);
|
return reduce_likely_array_spread_diagnostic(spread_span, reduce_call_span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unknown(spread_span, reduce_call_span)
|
reduce_unknown(spread_span, reduce_call_span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_identifier_symbol_id(ident: &BindingPatternKind<'_>) -> Option<SymbolId> {
|
fn get_identifier_symbol_id(ident: &BindingPatternKind<'_>) -> Option<SymbolId> {
|
||||||
|
|
@ -249,6 +343,21 @@ fn test() {
|
||||||
"foo.reduce((acc) => [...acc], [])",
|
"foo.reduce((acc) => [...acc], [])",
|
||||||
// Wrong number of arguments to known method (reduce can have 1 or 2 args, but not more)
|
// Wrong number of arguments to known method (reduce can have 1 or 2 args, but not more)
|
||||||
"foo.reduce((acc, bar) => [...acc, bar], [], 123)",
|
"foo.reduce((acc, bar) => [...acc, bar], [], 123)",
|
||||||
|
// loops, array case
|
||||||
|
"let foo = []; for (let i = 0; i < 10; i++) { foo.push(i); }",
|
||||||
|
"let foo = []; for (const i = 0; i < 10; i++) { foo.push(i); }",
|
||||||
|
"let foo = []; for (let i in [1,2,3]) { foo.push(i); }",
|
||||||
|
"let foo = []; for (const i in [1,2,3]) { foo.push(i); }",
|
||||||
|
"let foo = []; for (let i of [1,2,3]) { foo.push(i); }",
|
||||||
|
"let foo = []; while (foo.length < 10) { foo.push(foo.length); }",
|
||||||
|
// loops, object case
|
||||||
|
"let foo = {}; for (let i = 0; i < 10; i++) { foo[i] = i; }",
|
||||||
|
"let foo = {}; for (const i = 0; i < 10; i++) { foo[i] = i; }",
|
||||||
|
"let foo = {}; for (let i in [1,2,3]) { foo[i] = i; }",
|
||||||
|
"let foo = {}; for (const i in [1,2,3]) { foo[i] = i; }",
|
||||||
|
"let foo = {}; for (let i of [1,2,3]) { foo[i] = i; }",
|
||||||
|
"let foo = {}; for (const i of [1,2,3]) { foo[i] = i; }",
|
||||||
|
"let foo = {}; while (Object.keys(foo).length < 10) { foo[Object.keys(foo).length] = Object.keys(foo).length; }",
|
||||||
];
|
];
|
||||||
|
|
||||||
let fail = vec![
|
let fail = vec![
|
||||||
|
|
@ -297,6 +406,22 @@ fn test() {
|
||||||
// Object - Body return with item spread
|
// Object - Body return with item spread
|
||||||
"foo.reduce((acc, bar) => {return {...acc, ...bar};}, {})",
|
"foo.reduce((acc, bar) => {return {...acc, ...bar};}, {})",
|
||||||
"foo.reduceRight((acc, bar) => {return {...acc, ...bar};}, {})",
|
"foo.reduceRight((acc, bar) => {return {...acc, ...bar};}, {})",
|
||||||
|
// loops, array case
|
||||||
|
"let foo = []; for (let i = 0; i < 10; i++) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; for (const i = 0; i < 10; i++) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; for (let i in [1,2,3]) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; for (const i in [1,2,3]) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; for (let i of [1,2,3]) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; for (const i of [1,2,3]) { foo = [...foo, i]; }",
|
||||||
|
"let foo = []; while (foo.length < 10) { foo = [...foo, foo.length]; }",
|
||||||
|
// loops, object case
|
||||||
|
"let foo = {}; for (let i = 0; i < 10; i++) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; for (const i = 0; i < 10; i++) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; for (let i in [1,2,3]) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; for (const i in [1,2,3]) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; for (let i of [1,2,3]) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; for (const i of [1,2,3]) { foo = { ...foo, [i]: i }; }",
|
||||||
|
"let foo = {}; while (Object.keys(foo).length < 10) { foo = { ...foo, [Object.keys(foo).length]: Object.keys(foo).length }; }",
|
||||||
];
|
];
|
||||||
|
|
||||||
Tester::new(NoAccumulatingSpread::NAME, pass, fail).test_and_snapshot();
|
Tester::new(NoAccumulatingSpread::NAME, pass, fail).test_and_snapshot();
|
||||||
|
|
|
||||||
|
|
@ -313,3 +313,143 @@ source: crates/oxc_linter/src/tester.rs
|
||||||
╰────
|
╰────
|
||||||
help: It looks like you're spreading an `Object`. Consider using the `Object.assign` or assignment operators to mutate the accumulator instead.
|
help: It looks like you're spreading an `Object`. Consider using the `Object.assign` or assignment operators to mutate the accumulator instead.
|
||||||
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (let i = 0; i < 10; i++) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (const i = 0; i < 10; i++) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (let i in [1,2,3]) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (const i in [1,2,3]) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (let i of [1,2,3]) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; for (const i of [1,2,3]) { foo = [...foo, i]; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = []; while (foo.length < 10) { foo = [...foo, foo.length]; }
|
||||||
|
· ──┬── ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (let i = 0; i < 10; i++) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (const i = 0; i < 10; i++) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (let i in [1,2,3]) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (const i in [1,2,3]) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (let i of [1,2,3]) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; for (const i of [1,2,3]) { foo = { ...foo, [i]: i }; }
|
||||||
|
· ─┬─ ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
||||||
|
⚠ oxc(no-accumulating-spread): Do not spread accumulators in loops
|
||||||
|
╭─[no_accumulating_spread.tsx:1:15]
|
||||||
|
1 │ let foo = {}; while (Object.keys(foo).length < 10) { foo = { ...foo, [Object.keys(foo).length]: Object.keys(foo).length }; }
|
||||||
|
· ──┬── ───┬──
|
||||||
|
· │ ╰── From this spread
|
||||||
|
· ╰── For this loop
|
||||||
|
╰────
|
||||||
|
help: Consider using `Object.assign()` or `Array.prototype.push()` to mutate the accumulator instead.
|
||||||
|
Using spreads within accumulators leads to `O(n^2)` time complexity.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue