rectorphp/rector · error · ShouldNotHappenException

"%s" rule is deprecated, as inverting nested ifs to continue

Error message

"%s" rule is deprecated, as inverting nested ifs to continue makes the code harder to read and understand

What it means

ChangeNestedForeachIfsToEarlyContinueRector used to invert nested if blocks inside foreach loops into guard continue statements (early-continue style). It is deprecated because inverting nested ifs makes the loop body harder to read and understand — the guard-chain style trades nesting for order-dependence. refactor() now throws ShouldNotHappenException on every Foreach_ node; the rule performs no rewrite and exists only as a deprecation marker.

Source

Thrown at rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php:71

        }
    }
}
CODE_SAMPLE
)]);
    }
    /**
     * @return array<class-string<Node>>
     */
    public function getNodeTypes(): array
    {
        return [Foreach_::class];
    }
    /**
     * @param Foreach_ $node
     */
    public function refactor(Node $node): ?Node
    {
        throw new ShouldNotHappenException(sprintf('"%s" rule is deprecated, as inverting nested ifs to continue makes the code harder to read and understand', self::class));
    }
}

View on GitHub (pinned to 408fcb0ff1)

Solutions

  1. Remove ChangeNestedForeachIfsToEarlyContinueRector::class from rector.php and every imported set, then re-run vendor/bin/rector dry-run
  2. Convert specific loops to early-continue manually only where the guard style genuinely reads better
  3. Prefer extracting the loop body into a named method that uses early returns — clearer than either inline form
  4. grep -r ChangeNestedForeachIfsToEarlyContinueRector across the project to catch shared set files

Example fix

// before (rector.php)
->withRules([
    \Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector::class,
])
// code:
foreach ($items as $item) {
    if ($item->isValid()) {
        if ($item->isReady()) {
            $handler->process($item);
        }
    }
}

// after (rector.php)
// rule removed; loop left as is, or hand-converted where clearer:
foreach ($items as $item) {
    if (! $item->isValid()) {
        continue;
    }
    if (! $item->isReady()) {
        continue;
    }
    $handler->process($item);
}
Defensive patterns

Strategy: validation

Validate before calling

$deprecated = ['ChangeNestedForeachIfsToEarlyContinueRector'];
foreach (glob(__DIR__ . '/rector*.php') as $config) {
    $src = file_get_contents($config);
    foreach ($deprecated as $rule) {
        if (str_contains($src, $rule)) {
            fwrite(STDERR, "Remove deprecated rule {$rule} from {$config}\n");
            exit(1);
        }
    }
}

Prevention

When it happens

Trigger: Registering rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php in config and running rector over any file containing a foreach loop. The throw comes from rules/EarlyReturn/Rector/Foreach_/ChangeNestedForeachIfsToEarlyContinueRector.php:71 on the first Foreach_ node visited.

Common situations: Configs importing the EarlyReturn set or listing this rule individually, carried forward into a Rector version where it was deprecated. The first dry-run over any loop-heavy file (importers, iterators) crashes, which looks like a regression in Rector rather than a config issue.

Related errors


AI-assisted analysis of rectorphp/rector@408fcb0ff1 (2026-08-21). Data as JSON: /api/errors/2aa6c08c2b12bae1. Report an issue: GitHub.