oxc-project/oxc · warning · OxcDiagnostic

Do not spread accumulators in loops

Error message

Do not spread accumulators in loops

What it means

Oxlint performance rule `oxc/no_accumulating_spread`, loop variant: an object accumulator declared outside a loop is reassigned with an object spread (`{ ...acc, ... }`) inside the loop body. Each iteration copies all previously accumulated properties, so total work is O(n^2) in the number of iterations. The rule suggests `Object.assign()` mutation instead, and labels the accumulator declaration, the spread, and the loop (primary).

Source

Thrown at crates/oxc_linter/src/rules/oxc/no_accumulating_spread.rs:55

        ])
}

fn reduce_unknown(spread_span: Span, reduce_span: Span) -> OxcDiagnostic {
    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.")
        .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 loop_spread_likely_object_diagnostic(
    accumulator_decl_span: Span,
    spread_span: Span,
    loop_span: Span,
) -> OxcDiagnostic {
    OxcDiagnostic::warn("Do not spread accumulators in loops")
        .with_help("Consider using `Object.assign()` to mutate the accumulator instead.")
        .with_note("Using spreads within accumulators leads to `O(n^2)` time complexity.")
        .with_labels([
            accumulator_decl_span.label("From this accumulator"),
            spread_span.label("From this spread"),
            loop_span.primary_label("For this loop"),
        ])
}
fn loop_spread_likely_array_diagnostic(
    accumulator_decl_span: Span,
    spread_span: Span,
    loop_span: Span,
) -> OxcDiagnostic {
    OxcDiagnostic::warn("Do not spread accumulators in loops")
        .with_help("Consider using `Array.prototype.push()` to mutate the accumulator instead.")
        .with_note("Using spreads within accumulators leads to `O(n^2)` time complexity.")
        .with_labels([
            accumulator_decl_span.label("From this accumulator"),

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Mutate in place: `Object.assign(acc, { [x.key]: x.value })` or plain `acc[x.key] = x.value` inside the loop
  2. Use a `Map` during the loop and convert with `Object.fromEntries(map)` once after it
  3. If immutability per iteration is required (e.g. snapshots), disable the rule locally and document why the quadratic cost is acceptable

Example fix

// before
let acc = {};
for (const x of xs) {
  acc = { ...acc, [x.key]: x.value };
}

// after
const acc = {};
for (const x of xs) {
  acc[x.key] = x.value;
}
Defensive patterns

Strategy: fallback

Prevention

When it happens

Trigger: Code shaped like `let acc = {}; for (const x of xs) { acc = { ...acc, [x.key]: x.value }; }` (or while/do-while), where the accumulator is declared before the loop, the analysis concludes it is likely an object, and it is spread inside the loop.

Common situations: Merging per-item results into a single object inside for-of loops; incremental config/props building in React render loops; enabling the `oxc` plugin's performance rules on existing code for the first time.

Related errors


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