Skip to content

Commit 3341394

Browse files
phpstan-botgithub-actions[bot]claude
authored
Fix phpstan/phpstan#2572: Optional templated args not provided cause unbound template errors (#5175)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d3a9a3d commit 3341394

File tree

5 files changed

+121
-0
lines changed

5 files changed

+121
-0
lines changed

src/Reflection/GenericParametersAcceptorResolver.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Type\Generic\TemplateTypeMap;
1111
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
1212
use PHPStan\Type\MixedType;
13+
use PHPStan\Type\NeverType;
1314
use PHPStan\Type\Type;
1415
use PHPStan\Type\TypeCombinator;
1516
use function array_key_exists;
@@ -61,6 +62,8 @@ public static function resolve(array $argTypes, ParametersAcceptor $parametersAc
6162
$argType = $namedArgTypes[$param->getName()];
6263
} elseif ($param->getDefaultValue() !== null) {
6364
$argType = $param->getDefaultValue();
65+
} elseif ($param->isVariadic()) {
66+
$argType = new NeverType(true);
6467
} else {
6568
continue;
6669
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug2572Nsrt;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template TE
9+
* @template TR
10+
*
11+
* @param TE $elt
12+
* @param TR ...$elts
13+
*
14+
* @return TE|TR
15+
*/
16+
function collect($elt, ...$elts) {
17+
$ret = $elt;
18+
foreach ($elts as $item) {
19+
if (rand(0, 1)) {
20+
$ret = $item;
21+
}
22+
}
23+
return $ret;
24+
}
25+
26+
assertType("'a'", collect("a"));
27+
assertType("'a'|'b'|'c'", collect("a", "b", "c"));
28+
29+
/**
30+
* @template TValue
31+
* @template TArgs
32+
*
33+
* @param TValue|\Closure(TArgs): TValue $value
34+
* @param TArgs ...$args
35+
* @return TValue
36+
*/
37+
function value($value, ...$args)
38+
{
39+
return $value instanceof \Closure ? $value(...$args) : $value;
40+
}
41+
42+
assertType("'foo'", value('foo'));
43+
assertType("'foo'", value('foo', 42));
44+
assertType('42', value(fn () => 42));
45+
assertType('42', value(function ($foo) {
46+
assertType('true', $foo);
47+
48+
return 42;
49+
}, true));
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7704;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template T
9+
* @template TRest
10+
*
11+
* @param T $first
12+
* @param TRest ...$rest
13+
*
14+
* @return T|TRest
15+
*/
16+
function intersection($first, ...$rest)
17+
{
18+
$ret = $first;
19+
foreach ($rest as $item) {
20+
if (rand(0, 1)) {
21+
$ret = $item;
22+
}
23+
}
24+
return $ret;
25+
}
26+
27+
assertType("'a'", intersection("a"));
28+
assertType("'a'|'b'|'c'", intersection("a", "b", "c"));
29+
assertType('1', intersection(1));
30+
assertType('1|2|3', intersection(1, 2, 3));
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9360;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template T of string|bool
9+
*
10+
* @param T ...$args
11+
*
12+
* @return (T is string ? string : bool)
13+
*/
14+
function environment(string|bool ...$args): string|bool
15+
{
16+
if (count($args) === 0) {
17+
return true;
18+
}
19+
return (string) $args[0];
20+
}
21+
22+
assertType('string', environment());
23+
assertType('string', environment('APP_ENV'));
24+
assertType('string', environment('APP_ENV', 'production'));

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2691,6 +2691,21 @@ public function testBug8936(): void
26912691
$this->analyse([__DIR__ . '/data/bug-8936.php'], []);
26922692
}
26932693

2694+
public function testBug2572(): void
2695+
{
2696+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-2572.php'], []);
2697+
}
2698+
2699+
public function testBug7704(): void
2700+
{
2701+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-7704.php'], []);
2702+
}
2703+
2704+
public function testBug9360(): void
2705+
{
2706+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-9360.php'], []);
2707+
}
2708+
26942709
public function testDumpFunctions(): void
26952710
{
26962711
$this->analyse([__DIR__ . '/data/dump-functions.php'], [

0 commit comments

Comments
 (0)