Repository navigation
Description
Bug Report
| Subject | Details |
|---|---|
| Rector version | Rector 2.8.3 (also 2.5.7) |
ResponseStatusCodeRector crashes when it visits a first-class callable whose method name starts with assert, for example $validator->assert(...). This is a common pattern with validation libraries such as PSL (Psl\Type\string()->assert(...)).
processMethodCall() sends every method call whose name starts with assert to processAssertMethodCall():
processAssertMethodCall() then calls getArgs() without a first-class callable check, and the php-parser assertion fails:
[ERROR] Could not process "src/Example.php" file, due to:
"System error: "assert(!$this->isPartialFunctionApplication())"
Stack trace:
#0 vendor/rector/rector/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php(50): assert()
#1 vendor/rector/rector/vendor/rector/rector-symfony/rules/CodeQuality/Rector/BinaryOp/ResponseStatusCodeRector.php(155): PhpParser\Node\Expr\CallLike->getArgs()
#2 vendor/rector/rector/vendor/rector/rector-symfony/rules/CodeQuality/Rector/BinaryOp/ResponseStatusCodeRector.php(105): Rector\Symfony\CodeQuality\Rector\BinaryOp\ResponseStatusCodeRector->processAssertMethodCall()
#3 vendor/rector/rector/vendor/rector/rector-symfony/rules/CodeQuality/Rector/BinaryOp/ResponseStatusCodeRector.php(94): Rector\Symfony\CodeQuality\Rector\BinaryOp\ResponseStatusCodeRector->processMethodCall()
In Rector 2.5.7 the message is assert(!$this->isFirstClassCallable()). The crash needs zend.assertions = 1, which is the PHP CLI default in development setups.
Minimal PHP Code Causing Issue
<?php
final class Validator
{
public function assert(mixed $value): string
{
return (string) $value;
}
}
$validator = new Validator();
$values = array_map($validator->assert(...), ['a', 'b']);<?php
use Rector\Config\RectorConfig;
use Rector\Symfony\CodeQuality\Rector\BinaryOp\ResponseStatusCodeRector;
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withRules([ResponseStatusCodeRector::class]);Run: php -d zend.assertions=1 vendor/bin/rector process --dry-run
Expected Behaviour
Rector should skip the node. A first-class callable has no arguments to inspect. A guard at the start of processMethodCall() would fix it. isPartialFunctionApplication() also covers first-class callables in the php-parser version that Rector 2.8.3 bundles:
if ($methodCall->isPartialFunctionApplication()) {
return null;
}
Activity
TomasVotruba commentedon Nov 3, 2026
Thanks for reporting, fixed in rectorphp/rector-symfony#1103
devnix commentedon Nov 3, 2026
Thank you @TomasVotruba!
devnix commentedon Nov 4, 2026
Some context on how I hit this, in case it helps others: I switched the default PHP CLI on my machine from 8.3 (
zend.assertions = -1) to 8.4 (zend.assertions = 1). The Rector cache hid the crash. The affected files were already cached from runs under 8.3, and the cache stays valid after the PHP switch, so only a run without cache (a fresh worktree, or--clear-cache) showed it.This also points to a gap in CI. rector-src runs its tests with assertions enabled on purpose, for this exact case:
https://github.com/rectorphp/rector-src/blob/0532a8977bfbf91008d5e20fdeb568b710a7ca83/.github/workflows/tests.yaml#L29-L35
The workflows that run the rector-symfony tests don't set it: neither
code_analysis.yamlhere norpackages_tests.yamlin rector-src. setup-php uses the productionphp.iniby default, soassert()never runs there. With assertions disabled,getArgs()returns[VariadicPlaceholder], the old code returnsnull, and the newskip_first_class_callable_assert.php.incfixture passes even without the fix.Would you accept a PR that adds
ini-values: zend.assertions=1to the setup-php step of both workflows? The other rule packages are probably in the same situation.TomasVotruba commentedon Nov 4, 2026
We do not run assert exactly for this reason. It runs/doesn't run, depends.
Instead we prefer hard failure, exception and fixed bugs.
devnix commentedon Nov 5, 2026
Fair enough, and for Rector's own code I agree: an exception beats
assert().The catch is that this
assert()isn't Rector's but php-parser's, so there's no exception to switch to. With assertions off, the rule didn't fail at all. It returnednullby luck, and the new fixture passes even without the fix. Running the tests withzend.assertions=1is the only way to get the hard failure you're after.Plenty of people run with assertions on, too: it's the PHP default when there's no
php.ini(the official Docker images don't ship one), andphp.ini-developmentsets it as well. That's how I ran into it.rector-src already enables it in
tests.yamland three other workflows, but not inpackages_tests.yamlor the e2e ones, and none of the rule packages do. If you're open to it, I'm happy to send PRs addingini-values: zend.assertions=1to those setup-php steps, maybe with a check in the test bootstrap so a new workflow can't miss it.samsonasik commentedon Nov 5, 2026
It's there :)
https://github.com/rectorphp/rector-src/blob/0532a8977bfbf91008d5e20fdeb568b710a7ca83/.github/workflows/tests.yaml#L35
The catched bug usually due to missing test :)
samsonasik commentedon Nov 5, 2026
Oh i see, in package tests seems not exists now