oxc-project/oxc · warning · OxcDiagnostic
Do not spread accumulators in Array.prototype.reduce()
Error message
Do not spread accumulators in Array.prototype.reduce()
What it means
The array variant of oxlint's `oxc/no-accumulating-spread` (perf category): a spread element copies the accumulator — the first parameter of a two-parameter callback — inside a `.reduce()`/`.reduceRight()` call (invoked with 1-2 arguments). Each iteration clones the whole array (`[...acc, x]`), degrading the reduce to O(n^2) time and memory; the help recommends `Array.push`/`Array.concat`, and the note states the quadratic complexity explicitly.
Source
Thrown at crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs:21
ast::{
Argument, AssignmentExpression, AssignmentTarget, BindingPattern, CallExpression,
Expression, ForInStatement, ForOfStatement, ForStatement, VariableDeclarationKind,
},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::{NodeId, SymbolId};
use oxc_span::{GetSpan, Span};
use crate::{
AstNode,
ast_util::{call_expr_method_callee_info, is_method_call},
context::LintContext,
rule::Rule,
};
fn reduce_likely_array_spread_diagnostic(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
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.")
.with_note("Using spreads within accumulators leads to `O(n^2)` time complexity.")
.with_labels([
spread_span.label("From this spread"),
reduce_span.label("For this reduce")
])
}
fn reduce_likely_object_spread_diagnostic(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
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.")
.with_note("Using spreads within accumulators leads to `O(n^2)` time complexity.")
.with_labels([
spread_span.label("From this spread"),
reduce_span.label("For this reduce")
])
}
View on GitHub (pinned to e1e7af627c)
Solutions
- Mutate the accumulator and return it: `arr.reduce((acc, x) => { acc.push(fn(x)); return acc; }, [])`
- Or replace the reduce with a plain `for...of` loop pushing into an array
- Benchmark with realistic large inputs to confirm the fix
Example fix
// before
const out = arr.reduce((acc, x) => [...acc, fn(x)], []);
// after
const out = arr.reduce((acc, x) => { acc.push(fn(x)); return acc; }, []); Defensive patterns
Strategy: validation
Validate before calling
// .oxlintrc.json — perf rule
{
"rules": { "oxc/no-accumulating-spread": "warn" }
}
// CLI: npx oxlint src/ Prevention
- Never spread the reduce accumulator; mutate it and return it
- Benchmark reduce pipelines with large arrays (100k+ items) before shipping
- Use push/concat for array accumulation in hot paths
When it happens
Trigger: `arr.reduce((acc, x) => [...acc, fn(x)], [])`; `items.reduceRight((acc, x) => [...acc, transform(x)], [])` — any array spread of the accumulator identifier inside a reduce callback.
Common situations: Functional-style accumulation written with spread because mutation feels wrong; React/Redux reducer idioms copied into hot loops; large datasets where the quadratic blowup causes timeouts or memory pressure.
Related errors
- Global flag (g) is missing in the regular expression supplie
- Unexpected object literal comparison.
- Global flag (g) is missing in the regular expression supplie
- All `if` blocks contain the same code at the start
- All `if` blocks contain the same code at the end
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/a585fef3b39a33eb.
Report an issue: GitHub.