Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/Rules/FunctionCallParametersCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
final class FunctionCallParametersCheck
{

/**
* Marks a call node that was synthesized from a different node (e.g. the callback call of call_user_func()),
* its arguments do not correspond to the arguments of the node in the file, so it cannot be fixed.
*/
public const NOT_FIXABLE_CALL_ATTRIBUTE = 'phpstanNotFixableCall';

public function __construct(
private RuleLevelHelper $ruleLevelHelper,
private NullsafeCheck $nullsafeCheck,
Expand Down Expand Up @@ -911,8 +917,11 @@ private function createPositionalArgumentsFix(
Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall|Node\Expr\New_ $funcCall,
): Closure|false
{
if ((bool) $funcCall->getAttribute('isAttribute', false)) {
if (
// the call was created from an attribute, it is not in the file
(bool) $funcCall->getAttribute('isAttribute', false)
|| (bool) $funcCall->getAttribute(self::NOT_FIXABLE_CALL_ATTRIBUTE, false)
) {
return false;
}

Expand Down
1 change: 1 addition & 0 deletions src/Rules/Functions/CallUserFuncRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataE
return [];
}
[$parametersAcceptor, $funcCall, $acceptsNamedArguments] = $result;
$funcCall->setAttribute(FunctionCallParametersCheck::NOT_FIXABLE_CALL_ATTRIBUTE, true);

$callableDescription = sprintf('callable passed to %s()', $functionName);

Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Rules/Functions/CallUserFuncRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,10 @@ public function testConstantParameterCheckCallUserFunc(): void
]);
}

#[RequiresPhp('>= 8.0.0')]
public function testBug15664(): void
{
$this->fix(__DIR__ . '/data/bug-15664.php', __DIR__ . '/data/bug-15664.php.fixed');
}

}
38 changes: 38 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-15664.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php // lint >= 8.0

namespace Bug15664;

/**
* @no-named-arguments
*/
final class ParallelConfigFactory
{

public static function detect(
?int $filesPerProcess = null,
?int $processTimeout = null,
?int $maxProcesses = null
): void
{
}

}

function doFoo(): void
{
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['processTimeout' => 300]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], [
'maxProcesses' => 1,
'processTimeout' => 300,
'filesPerProcess' => 5,
]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['filesPerProcess' => 7]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['maxProcesses' => 1]);

\call_user_func([ParallelConfigFactory::class, 'detect'], processTimeout: 300);

\call_user_func([ParallelConfigFactory::class, 'detect'], filesPerProcess: 7);
}
38 changes: 38 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-15664.php.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php // lint >= 8.0

namespace Bug15664;

/**
* @no-named-arguments
*/
final class ParallelConfigFactory
{

public static function detect(
?int $filesPerProcess = null,
?int $processTimeout = null,
?int $maxProcesses = null
): void
{
}

}

function doFoo(): void
{
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['processTimeout' => 300]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], [
'maxProcesses' => 1,
'processTimeout' => 300,
'filesPerProcess' => 5,
]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['filesPerProcess' => 7]);

\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['maxProcesses' => 1]);

\call_user_func([ParallelConfigFactory::class, 'detect'], processTimeout: 300);

\call_user_func([ParallelConfigFactory::class, 'detect'], filesPerProcess: 7);
}
Loading