From ae5b8bf668062acb19feb46b79bf37b2ab5d0350 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:23:30 +0000 Subject: [PATCH 1/3] Fix false positive unresolvable template type on optional variadic params - When a template type is only used on a variadic parameter and no arguments are passed, it was incorrectly reported as unresolvable - Fixed by inferring NeverType for variadic parameters with no arguments in GenericParametersAcceptorResolver, since an empty variadic means the element type is never - Added regression tests in both rule test and NSRT format Closes https://github.com/phpstan/phpstan/issues/2572 --- .../GenericParametersAcceptorResolver.php | 3 +++ tests/PHPStan/Analyser/nsrt/bug-2572.php | 27 +++++++++++++++++++ .../CallToFunctionParametersRuleTest.php | 5 ++++ .../PHPStan/Rules/Functions/data/bug-2572.php | 25 +++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-2572.php create mode 100644 tests/PHPStan/Rules/Functions/data/bug-2572.php diff --git a/src/Reflection/GenericParametersAcceptorResolver.php b/src/Reflection/GenericParametersAcceptorResolver.php index d9b75bf3e0e..5111b5d3fb7 100644 --- a/src/Reflection/GenericParametersAcceptorResolver.php +++ b/src/Reflection/GenericParametersAcceptorResolver.php @@ -10,6 +10,7 @@ use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Generic\TemplateTypeVarianceMap; use PHPStan\Type\MixedType; +use PHPStan\Type\NeverType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use function array_key_exists; @@ -61,6 +62,8 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc $argType = $namedArgTypes[$param->getName()]; } elseif ($param->getDefaultValue() !== null) { $argType = $param->getDefaultValue(); + } elseif ($param->isVariadic()) { + $argType = new NeverType(); } else { continue; } diff --git a/tests/PHPStan/Analyser/nsrt/bug-2572.php b/tests/PHPStan/Analyser/nsrt/bug-2572.php new file mode 100644 index 00000000000..a1f332e4921 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-2572.php @@ -0,0 +1,27 @@ +analyse([__DIR__ . '/data/bug-8936.php'], []); } + public function testBug2572(): void + { + $this->analyse([__DIR__ . '/data/bug-2572.php'], []); + } + public function testDumpFunctions(): void { $this->analyse([__DIR__ . '/data/dump-functions.php'], [ diff --git a/tests/PHPStan/Rules/Functions/data/bug-2572.php b/tests/PHPStan/Rules/Functions/data/bug-2572.php new file mode 100644 index 00000000000..e56c50f8372 --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-2572.php @@ -0,0 +1,25 @@ + Date: Thu, 26 Mar 2026 09:52:22 +0000 Subject: [PATCH 2/3] Address review: deduplicate test file and fix NeverType to be explicit - Reuse tests/PHPStan/Analyser/nsrt/bug-2572.php in rule test instead of duplicating the file in tests/PHPStan/Rules/Functions/data/ - Use explicit NeverType(true) for empty variadic params so the UnresolvableTypeHelper doesn't treat it as unresolvable - Add staabm's reproducer (value() with Closure|TValue union) as additional test coverage Co-Authored-By: Claude Opus 4.6 --- .../GenericParametersAcceptorResolver.php | 2 +- tests/PHPStan/Analyser/nsrt/bug-2572.php | 22 ++++++++++++++++ .../CallToFunctionParametersRuleTest.php | 2 +- .../PHPStan/Rules/Functions/data/bug-2572.php | 25 ------------------- 4 files changed, 24 insertions(+), 27 deletions(-) delete mode 100644 tests/PHPStan/Rules/Functions/data/bug-2572.php diff --git a/src/Reflection/GenericParametersAcceptorResolver.php b/src/Reflection/GenericParametersAcceptorResolver.php index 5111b5d3fb7..c72afeedbb5 100644 --- a/src/Reflection/GenericParametersAcceptorResolver.php +++ b/src/Reflection/GenericParametersAcceptorResolver.php @@ -63,7 +63,7 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc } elseif ($param->getDefaultValue() !== null) { $argType = $param->getDefaultValue(); } elseif ($param->isVariadic()) { - $argType = new NeverType(); + $argType = new NeverType(true); } else { continue; } diff --git a/tests/PHPStan/Analyser/nsrt/bug-2572.php b/tests/PHPStan/Analyser/nsrt/bug-2572.php index a1f332e4921..43720afb199 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-2572.php +++ b/tests/PHPStan/Analyser/nsrt/bug-2572.php @@ -25,3 +25,25 @@ function collect($elt, ...$elts) { assertType("'a'", collect("a")); assertType("'a'|'b'|'c'", collect("a", "b", "c")); + +/** + * @template TValue + * @template TArgs + * + * @param TValue|\Closure(TArgs): TValue $value + * @param TArgs ...$args + * @return TValue + */ +function value($value, ...$args) +{ + return $value instanceof \Closure ? $value(...$args) : $value; +} + +assertType("'foo'", value('foo')); +assertType("'foo'", value('foo', 42)); +assertType('42', value(fn () => 42)); +assertType('42', value(function ($foo) { + assertType('true', $foo); + + return 42; +}, true)); diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 666d80f5be8..373e6902918 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -2693,7 +2693,7 @@ public function testBug8936(): void public function testBug2572(): void { - $this->analyse([__DIR__ . '/data/bug-2572.php'], []); + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-2572.php'], []); } public function testDumpFunctions(): void diff --git a/tests/PHPStan/Rules/Functions/data/bug-2572.php b/tests/PHPStan/Rules/Functions/data/bug-2572.php deleted file mode 100644 index e56c50f8372..00000000000 --- a/tests/PHPStan/Rules/Functions/data/bug-2572.php +++ /dev/null @@ -1,25 +0,0 @@ - Date: Thu, 26 Mar 2026 11:03:05 +0000 Subject: [PATCH 3/3] Add regression tests for phpstan/phpstan#7704 and phpstan/phpstan#9360 Both issues report the same underlying bug: unresolvable template type errors when calling functions with variadic template parameters without providing any variadic arguments. Co-Authored-By: Claude Opus 4.6 --- tests/PHPStan/Analyser/nsrt/bug-7704.php | 30 +++++++++++++++++++ tests/PHPStan/Analyser/nsrt/bug-9360.php | 24 +++++++++++++++ .../CallToFunctionParametersRuleTest.php | 10 +++++++ 3 files changed, 64 insertions(+) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-7704.php create mode 100644 tests/PHPStan/Analyser/nsrt/bug-9360.php diff --git a/tests/PHPStan/Analyser/nsrt/bug-7704.php b/tests/PHPStan/Analyser/nsrt/bug-7704.php new file mode 100644 index 00000000000..45bb92fb969 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-7704.php @@ -0,0 +1,30 @@ +analyse([__DIR__ . '/../../Analyser/nsrt/bug-2572.php'], []); } + public function testBug7704(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-7704.php'], []); + } + + public function testBug9360(): void + { + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-9360.php'], []); + } + public function testDumpFunctions(): void { $this->analyse([__DIR__ . '/data/dump-functions.php'], [