Skip to content

ForeachToArrayAllRector breaks code that increments an outer variable inside the loop #9961

Closed
rectorphp/rector-src
#8925

Description

@devnix

Bug Report

Subject Details
Rector version 2.8.4 (local), 2.8.1 (demo)

ForeachToArrayAllRector moves a $i++ from the loop body into an arrow function. Arrow functions capture outer variables by value, so the increment no longer persists between iterations and the result changes.

Minimal PHP Code Causing Issue

https://getrector.com/demo/8f948a23-ee3f-48f9-9e64-bcd2544124a5

<?php

final class DemoFile
{
    /**
     * @param list<int> $items
     * @param list<int> $others
     */
    public function allEqual(array $items, array $others): bool
    {
        $i = 0;
        $equals = true;

        foreach ($items as $item) {
            if ($item !== $others[$i++]) {
                $equals = false;
                break;
            }
        }

        return $equals;
    }
}

Rector output:

$i = 0;
$equals = array_all($items, fn($item) => !($item !== $others[$i++]));

With $items = [1, 2] and $others = [1, 2], the original code returns true and the refactored code returns false: every call reads $others[0].

Expected Behaviour

Skip the refactoring when the loop body modifies an outer variable (++, --, assignment, compound assignment), because the arrow function cannot propagate that change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions