#9907·rector

AddNameToBooleanArgumentRector and AddNameToNullArgumentRector add named arguments to @no-named-arguments APIs

Author: ShoxcieCreated Sep 18, 2026Updated Sep 18, 2026

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:

php
return RectorConfig::configure()
    ->withRules([AddNameToBooleanArgumentRector::class, AddNameToNullArgumentRector::class]);
php
<?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:

diff
-(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.