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

  1. Mutate the accumulator and return it: `arr.reduce((acc, x) => { acc.push(fn(x)); return acc; }, [])`
  2. Or replace the reduce with a plain `for...of` loop pushing into an array
  3. 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

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


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/a585fef3b39a33eb. Report an issue: GitHub.