diff --git a/src/Reflection/Php/PhpFunctionReflection.php b/src/Reflection/Php/PhpFunctionReflection.php index 5303d38b0f..a52f3829f8 100644 --- a/src/Reflection/Php/PhpFunctionReflection.php +++ b/src/Reflection/Php/PhpFunctionReflection.php @@ -3,10 +3,7 @@ namespace PHPStan\Reflection\Php; use PhpParser\Node; -use PhpParser\Node\Stmt\ClassLike; -use PhpParser\Node\Stmt\Declare_; use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Stmt\Namespace_; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionFunction; use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter; use PHPStan\Cache\Cache; @@ -27,6 +24,7 @@ use function array_key_exists; use function array_map; use function filemtime; +use function is_array; use function is_file; use function sprintf; use function time; @@ -149,12 +147,12 @@ private function isVariadic(): bool if ($modifiedTime === false) { $modifiedTime = time(); } - $variableCacheKey = sprintf('%d-v3', $modifiedTime); + $variableCacheKey = sprintf('%d-v4', $modifiedTime); $key = sprintf('variadic-function-%s-%s', $functionName, $fileName); $cachedResult = $this->cache->load($key, $variableCacheKey); if ($cachedResult === null) { $nodes = $this->parser->parseFile($fileName); - $result = $this->callsFuncGetArgs($nodes); + $result = !$this->containsVariadicFunction($nodes)->no(); $this->cache->save($key, $variableCacheKey, $result); return $result; } @@ -167,41 +165,40 @@ private function isVariadic(): bool } /** - * @param Node[] $nodes + * @param Node[]|scalar[]|Node $node */ - private function callsFuncGetArgs(array $nodes): bool + private function containsVariadicFunction(array|Node $node): TrinaryLogic { - foreach ($nodes as $node) { + $result = TrinaryLogic::createMaybe(); + + if ($node instanceof Node) { if ($node instanceof Function_) { $functionName = (string) $node->namespacedName; if ($functionName === $this->reflection->getName()) { - return $this->functionCallStatementFinder->findFunctionCallInStatements(ParametersAcceptor::VARIADIC_FUNCTIONS, $node->getStmts()) !== null; + return TrinaryLogic::createFromBoolean($this->isFunctionNodeVariadic($node)); } - - continue; } - if ($node instanceof ClassLike) { - continue; - } - - if ($node instanceof Namespace_) { - if ($this->callsFuncGetArgs($node->stmts)) { - return true; + foreach ($node->getSubNodeNames() as $subNodeName) { + $innerNode = $node->{$subNodeName}; + if (!$innerNode instanceof Node && !is_array($innerNode)) { + continue; } - } - if (!$node instanceof Declare_ || $node->stmts === null) { - continue; + $result = $result->and($this->containsVariadicFunction($innerNode)); } + } elseif (is_array($node)) { + foreach ($node as $subNode) { + if (!$subNode instanceof Node) { + continue; + } - if ($this->callsFuncGetArgs($node->stmts)) { - return true; + $result = $result->and($this->containsVariadicFunction($subNode)); } } - return false; + return $result; } private function getReturnType(): Type @@ -303,4 +300,19 @@ public function acceptsNamedArguments(): bool return $this->acceptsNamedArguments; } + private function isFunctionNodeVariadic(Function_ $node): bool + { + foreach ($node->params as $parameter) { + if ($parameter->variadic) { + return true; + } + } + + if ($this->functionCallStatementFinder->findFunctionCallInStatements(ParametersAcceptor::VARIADIC_FUNCTIONS, $node->getStmts()) !== null) { + return true; + } + + return false; + } + } diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 49b56548cc..d49987e3c5 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1742,4 +1742,23 @@ public function testBug11506(): void $this->analyse([__DIR__ . '/data/bug-11506.php'], []); } + public function testBug11559(): void + { + $this->analyse([__DIR__ . '/data/bug-11559.php'], []); + } + + public function testBug11559b(): void + { + $this->analyse([__DIR__ . '/data/bug-11559b.php'], [ + [ + 'Function Bug11559b\maybe_variadic_fn invoked with 5 parameters, 0 required.', + 14, + ], + [ + 'Function Bug11559b\maybe_variadic_fn4 invoked with 2 parameters, 0 required.', + 65, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-11559.php b/tests/PHPStan/Rules/Functions/data/bug-11559.php new file mode 100644 index 0000000000..05e4d85fff --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-11559.php @@ -0,0 +1,41 @@ +