Skip to content

Commit 368d4e8

Browse files
authored
Do not attach the positional-arguments fix to the callback call synthesized from call_user_func() / call_user_func_array() (#6966)
1 parent be1a850 commit 368d4e8

5 files changed

Lines changed: 93 additions & 1 deletion

File tree

‎src/Rules/FunctionCallParametersCheck.php‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@
5656
final class FunctionCallParametersCheck
5757
{
5858

59+
/**
60+
* Marks a call node that was synthesized from a different node (e.g. the callback call of call_user_func()),
61+
* its arguments do not correspond to the arguments of the node in the file, so it cannot be fixed.
62+
*/
63+
public const NOT_FIXABLE_CALL_ATTRIBUTE = 'phpstanNotFixableCall';
64+
5965
public function __construct(
6066
private RuleLevelHelper $ruleLevelHelper,
6167
private NullsafeCheck $nullsafeCheck,
@@ -912,8 +918,11 @@ private function createPositionalArgumentsFix(
912918
Node\Expr\FuncCall|Node\Expr\MethodCall|Node\Expr\StaticCall|Node\Expr\New_ $funcCall,
913919
): Closure|false
914920
{
915-
if ((bool) $funcCall->getAttribute('isAttribute', false)) {
921+
if (
916922
// the call was created from an attribute, it is not in the file
923+
(bool) $funcCall->getAttribute('isAttribute', false)
924+
|| (bool) $funcCall->getAttribute(self::NOT_FIXABLE_CALL_ATTRIBUTE, false)
925+
) {
917926
return false;
918927
}
919928

‎src/Rules/Functions/CallUserFuncRule.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataE
6969
return [];
7070
}
7171
[$parametersAcceptor, $funcCall, $acceptsNamedArguments] = $result;
72+
$funcCall->setAttribute(FunctionCallParametersCheck::NOT_FIXABLE_CALL_ATTRIBUTE, true);
7273

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

‎tests/PHPStan/Rules/Functions/CallUserFuncRuleTest.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,10 @@ public function testConstantParameterCheckCallUserFunc(): void
202202
]);
203203
}
204204

205+
#[RequiresPhp('>= 8.0.0')]
206+
public function testBug15664(): void
207+
{
208+
$this->fix(__DIR__ . '/data/bug-15664.php', __DIR__ . '/data/bug-15664.php.fixed');
209+
}
210+
205211
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug15664;
4+
5+
/**
6+
* @no-named-arguments
7+
*/
8+
final class ParallelConfigFactory
9+
{
10+
11+
public static function detect(
12+
?int $filesPerProcess = null,
13+
?int $processTimeout = null,
14+
?int $maxProcesses = null
15+
): void
16+
{
17+
}
18+
19+
}
20+
21+
function doFoo(): void
22+
{
23+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['processTimeout' => 300]);
24+
25+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], [
26+
'maxProcesses' => 1,
27+
'processTimeout' => 300,
28+
'filesPerProcess' => 5,
29+
]);
30+
31+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['filesPerProcess' => 7]);
32+
33+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['maxProcesses' => 1]);
34+
35+
\call_user_func([ParallelConfigFactory::class, 'detect'], processTimeout: 300);
36+
37+
\call_user_func([ParallelConfigFactory::class, 'detect'], filesPerProcess: 7);
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug15664;
4+
5+
/**
6+
* @no-named-arguments
7+
*/
8+
final class ParallelConfigFactory
9+
{
10+
11+
public static function detect(
12+
?int $filesPerProcess = null,
13+
?int $processTimeout = null,
14+
?int $maxProcesses = null
15+
): void
16+
{
17+
}
18+
19+
}
20+
21+
function doFoo(): void
22+
{
23+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['processTimeout' => 300]);
24+
25+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], [
26+
'maxProcesses' => 1,
27+
'processTimeout' => 300,
28+
'filesPerProcess' => 5,
29+
]);
30+
31+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['filesPerProcess' => 7]);
32+
33+
\call_user_func_array([ParallelConfigFactory::class, 'detect'], ['maxProcesses' => 1]);
34+
35+
\call_user_func([ParallelConfigFactory::class, 'detect'], processTimeout: 300);
36+
37+
\call_user_func([ParallelConfigFactory::class, 'detect'], filesPerProcess: 7);
38+
}

0 commit comments

Comments
 (0)