AddNameToBooleanArgumentRector and AddNameToNullArgumentRector add named arguments to @no-named-arguments APIs
Bug Report
| Subject | Details |
|---|---|
| Rector version | 2.6.7 |
AddNameToBooleanArgumentRector and AddNameToNullArgumentRector (from the namedArgs prepared set) name arguments even when the callee is marked @no-named-arguments.
PHPStan then reports argument.named ("invoked with named argument $isRiskyAllowed, but it's not allowed because of @no-named-arguments"). Removing the name by hand doesn't stick, because Rector adds it back, so the two tools loop. The resulting code also depends on a parameter name that the library says isn't covered by its backward-compatibility promise.
Real-world case: setRiskyAllowed(true) in a .php-cs-fixer.dist.php, because PhpCsFixer\Config is annotated @no-named-arguments Parameter names are not covered by the backward compatibility promise.
Minimal PHP Code Causing Issue
The snippet declares its own class, so it reproduces without any dependency. I checked it locally against 2.6.7 with this config:
return RectorConfig::configure()
->withRules([AddNameToBooleanArgumentRector::class, AddNameToNullArgumentRector::class]);<?php
/**
* @no-named-arguments
*/
final class Config
{
public function setRiskyAllowed(bool $isRiskyAllowed): self
{
return $this;
}
public function setCacheFile(?string $cacheFile): self
{
return $this;
}
}
(new Config())->setRiskyAllowed(true);
(new Config())->setCacheFile(null);Rector's output:
-(new Config())->setRiskyAllowed(true);
-(new Config())->setCacheFile(null);
+(new Config())->setRiskyAllowed(isRiskyAllowed: true);
+(new Config())->setCacheFile(cacheFile: null);Expected Behaviour
Rector should skip both calls, because the callee doesn't accept named arguments.
Both rules go through CallLikeArgumentNameAdder::addNamesToArgs(), which never asks whether the callee accepts named arguments. PHPStan's reflection exposes that as acceptsNamedArguments() (returning TrinaryLogic on FunctionReflection and ExtendedMethodReflection), so returning early unless it's yes() would fix both rules.
Source: rectorphp/rector