Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 269ed62

Browse files
Cover more situations and add tests
1 parent 9a91543 commit 269ed62

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

‎src/PhpDoc/TypeNodeResolver.php‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
use PHPStan\Type\FloatType;
6464
use PHPStan\Type\Generic\GenericClassStringType;
6565
use PHPStan\Type\Generic\GenericObjectType;
66+
use PHPStan\Type\Generic\TemplateType;
6667
use PHPStan\Type\IntegerRangeType;
6768
use PHPStan\Type\IntegerType;
6869
use PHPStan\Type\IntersectionType;
@@ -92,6 +93,7 @@
9293
use Traversable;
9394
use function array_key_exists;
9495
use function array_map;
96+
use function array_values;
9597
use function count;
9698
use function explode;
9799
use function get_class;
@@ -683,6 +685,15 @@ private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $na
683685

684686
$classReflection = $this->getReflectionProvider()->getClass($mainType->getClassName());
685687
if ($classReflection->isGeneric()) {
688+
$templateTypes = array_values($classReflection->getTemplateTypeMap()->getTypes());
689+
for ($i = count($genericTypes), $templateTypesCount = count($templateTypes); $i < $templateTypesCount; $i++) {
690+
$templateType = $templateTypes[$i];
691+
if (!$templateType instanceof TemplateType || $templateType->getDefault() === null) {
692+
continue;
693+
}
694+
$genericTypes[] = $templateType->getDefault();
695+
}
696+
686697
if (in_array($mainType->getClassName(), [
687698
Traversable::class,
688699
IteratorAggregate::class,

‎src/Type/Generic/TemplateTypeMap.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PHPStan\Type\NeverType;
66
use PHPStan\Type\Type;
77
use PHPStan\Type\TypeCombinator;
8+
use PHPStan\Type\TypeTraverser;
89
use PHPStan\Type\TypeUtils;
910
use function array_key_exists;
1011
use function count;
@@ -208,7 +209,10 @@ public function resolveToBounds(): self
208209
if ($this->resolvedToBounds !== null) {
209210
return $this->resolvedToBounds;
210211
}
211-
return $this->resolvedToBounds = $this->map(static fn (string $name, Type $type): Type => TemplateTypeHelper::resolveToBounds($type));
212+
return $this->resolvedToBounds = $this->map(static fn (string $name, Type $type): Type => TypeTraverser::map(
213+
$type,
214+
static fn (Type $type, callable $traverse): Type => $type instanceof TemplateType ? $traverse($type->getDefault() ?? $type->getBound()) : $traverse($type),
215+
));
212216
}
213217

214218
/**

‎tests/PHPStan/Analyser/NodeScopeResolverTest.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,7 @@ public function dataFileAsserts(): iterable
10731073
yield from $this->gatherAssertTypes(__DIR__ . '/data/native-expressions.php');
10741074
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Classes/data/bug-5333.php');
10751075
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4801.php');
1076+
yield from $this->gatherAssertTypes(__DIR__ . '/data/template-default.php');
10761077
}
10771078

10781079
/**
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace TemplateDefault;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template T1 = true
9+
* @template T2 = true
10+
*/
11+
class Test
12+
{
13+
}
14+
15+
/**
16+
* @param Test<false> $one
17+
* @param Test<false, false> $two
18+
* @param Test<false, false, false> $three
19+
*/
20+
function foo(Test $one, Test $two, Test $three)
21+
{
22+
assertType('TemplateDefault\\Test<false, true>', $one);
23+
assertType('TemplateDefault\\Test<false, false>', $two);
24+
assertType('TemplateDefault\\Test<false, false, false>', $three);
25+
}
26+
27+
28+
/**
29+
* @template S = false
30+
* @template T = false
31+
*/
32+
class Builder
33+
{
34+
/**
35+
* @phpstan-self-out self<true, T>
36+
*/
37+
public function one(): void
38+
{
39+
}
40+
41+
/**
42+
* @phpstan-self-out self<S, true>
43+
*/
44+
public function two(): void
45+
{
46+
}
47+
48+
/**
49+
* @return ($this is self<true, true> ? void : never)
50+
*/
51+
public function execute(): void
52+
{
53+
}
54+
}
55+
56+
function () {
57+
$qb = new Builder();
58+
assertType('TemplateDefault\\Builder<false, false>', $qb);
59+
$qb->one();
60+
assertType('TemplateDefault\\Builder<true, false>', $qb);
61+
$qb->two();
62+
assertType('TemplateDefault\\Builder<true, true>', $qb);
63+
assertType('void', $qb->execute());
64+
};
65+
66+
function () {
67+
$qb = new Builder();
68+
assertType('TemplateDefault\\Builder<false, false>', $qb);
69+
$qb->two();
70+
assertType('TemplateDefault\\Builder<false, true>', $qb);
71+
$qb->one();
72+
assertType('TemplateDefault\\Builder<true, true>', $qb);
73+
assertType('void', $qb->execute());
74+
};
75+
76+
function () {
77+
$qb = new Builder();
78+
assertType('TemplateDefault\\Builder<false, false>', $qb);
79+
$qb->one();
80+
assertType('TemplateDefault\\Builder<true, false>', $qb);
81+
assertType('*NEVER*', $qb->execute());
82+
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /