Skip to content

Commit 7f3ad70

Browse files
committed
Fixed infinite recursion with late-resolvable types
1 parent 19bb299 commit 7f3ad70

File tree

3 files changed

+103
-1
lines changed

3 files changed

+103
-1
lines changed

src/Type/UnionType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ public function isSuperTypeOf(Type $otherType): IsSuperTypeOfResult
240240
($otherType instanceof self && !$otherType instanceof TemplateUnionType)
241241
|| ($otherType instanceof IterableType && !$otherType instanceof TemplateIterableType)
242242
|| $otherType instanceof NeverType
243-
|| ($otherType instanceof LateResolvableType && $otherType instanceof CompoundType)
243+
|| ($otherType instanceof LateResolvableType && $otherType instanceof CompoundType && !$otherType instanceof TemplateType)
244244
|| $otherType instanceof IntegerRangeType
245245
) {
246246
return $otherType->isSubTypeOf($this);

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ public function testBug3379(): void
287287
$this->assertSame('Constant SOME_UNKNOWN_CONST not found.', $errors[0]->getMessage());
288288
}
289289

290+
public function testBug13685(): void
291+
{
292+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-13685.php');
293+
$this->assertNoErrors($errors);
294+
}
295+
290296
public function testBug3798(): void
291297
{
292298
$errors = $this->runAnalyse(__DIR__ . '/data/bug-3798.php');
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace Bug13685;
4+
5+
/**
6+
* @template TAttr of array<string, mixed>
7+
*/
8+
trait AttributeTrait
9+
{
10+
/**
11+
* @param key-of<TAttr> $key
12+
*/
13+
public function hasAttribute(string $key): bool
14+
{
15+
$attr = $this->getAttributes();
16+
17+
return isset($attr[$key]);
18+
}
19+
20+
/**
21+
* @template TKey of key-of<TAttr>
22+
* @param TKey&key-of<TAttr> $key
23+
* @param TAttr[TKey] $val
24+
*/
25+
public function setAttribute(string $key, $val): self
26+
{
27+
$attr = $this->getAttributes();
28+
$attr[$key] = $val;
29+
/** @phpstan-ignore-next-line */
30+
$this->setAttributes($attr);
31+
32+
return $this;
33+
}
34+
35+
/**
36+
* @template TKey of key-of<TAttr>
37+
* @param TKey $key
38+
* @return TAttr[TKey]|null
39+
*/
40+
public function getAttribute(string $key)
41+
{
42+
return $this->getAttributes()[$key] ?? null;
43+
}
44+
45+
/**
46+
* @param key-of<TAttr> $key
47+
*/
48+
public function unsetAttribute(string $key): self
49+
{
50+
$attr = $this->getAttributes();
51+
unset($attr[$key]);
52+
$this->setAttributes($attr);
53+
54+
return $this;
55+
}
56+
57+
/**
58+
* @param TAttr $attributes
59+
* @return static
60+
*/
61+
abstract public function setAttributes(array $attributes): self;
62+
63+
/**
64+
* @return TAttr
65+
*/
66+
abstract public function getAttributes(): array;
67+
}
68+
69+
/**
70+
* @phpstan-type FooAttributes array<self::ATTR_*, mixed>
71+
*/
72+
class FooWithAttribute
73+
{
74+
public const ATTR_ONE = 'attr_one';
75+
public const ATTR_TWO = 'attr_two';
76+
public const ATTR_THREE = 'attr_three';
77+
78+
/** @var FooAttributes */
79+
private array $attributes = [];
80+
81+
/** @use AttributeTrait<FooAttributes> */
82+
use AttributeTrait;
83+
84+
public function setAttributes(array $attributes): self
85+
{
86+
return $this;
87+
}
88+
89+
/**
90+
* @phpstan-return FooAttributes
91+
*/
92+
public function getAttributes(): array
93+
{
94+
return $this->attributes ?? [];
95+
}
96+
}

0 commit comments

Comments
 (0)