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 8e6eaf1

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Fix resolving nested conditional types with union subjects
1 parent 3334735 commit 8e6eaf1

File tree

3 files changed

+56
-7
lines changed

3 files changed

+56
-7
lines changed

‎src/Type/ConditionalType.php‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,29 @@ protected function getResult(): Type
113113
{
114114
$isSuperType = $this->target->isSuperTypeOf($this->subject);
115115

116+
$yesType = fn () => TypeTraverser::map(!$this->negated ? $this->if : $this->else, function (Type $type, callable $traverse) {
117+
if ($type->equals($this->subject)) {
118+
return !$this->negated ? TypeCombinator::intersect($type, $this->target) : TypeCombinator::remove($type, $this->target);
119+
}
120+
return $traverse($type);
121+
});
122+
123+
$noType = fn () => TypeTraverser::map(!$this->negated ? $this->else : $this->if, function (Type $type, callable $traverse) {
124+
if ($type->equals($this->subject)) {
125+
return !$this->negated ? TypeCombinator::remove($type, $this->target) : TypeCombinator::intersect($type, $this->target);
126+
}
127+
return $traverse($type);
128+
});
129+
116130
if ($isSuperType->yes()) {
117-
return !$this->negated ? $this->if : $this->else;
131+
return $yesType();
118132
}
119133

120134
if ($isSuperType->no()) {
121-
return !$this->negated ? $this->else : $this->if;
135+
return $noType();
122136
}
123137

124-
return TypeCombinator::union($this->if, $this->else);
138+
return TypeCombinator::union($yesType(), $noType());
125139
}
126140

127141
public function traverse(callable $cb): Type

‎src/Type/TypeUtils.php‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -409,13 +409,11 @@ public static function containsTemplateType(Type $type): bool
409409
public static function resolveLateResolvableTypes(Type $type, bool $resolveUnresolvableTypes = true): Type
410410
{
411411
return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($resolveUnresolvableTypes): Type {
412-
$type = $traverse($type);
413-
414-
if ($type instanceof LateResolvableType && ($resolveUnresolvableTypes || $type->isResolvable())) {
412+
while ($type instanceof LateResolvableType && ($resolveUnresolvableTypes || $type->isResolvable())) {
415413
$type = $type->resolve();
416414
}
417415

418-
return $type;
416+
return $traverse($type);
419417
});
420418
}
421419

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9860;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class A {}
8+
class B {}
9+
class C {}
10+
11+
class ANode {}
12+
class BNode {}
13+
class CNode {}
14+
15+
class HelloWorld
16+
{
17+
public function a(): A|B {
18+
return new A();
19+
}
20+
21+
/**
22+
* @return ($b is A ? ANode : ($b is B ? BNode : CNode))
23+
*/
24+
public function b(A|B|C $b): ANode|BNode|CNode {
25+
return match(true) {
26+
$b instanceof A => new ANode(),
27+
$b instanceof B => new BNode(),
28+
default => new CNode(),
29+
};
30+
}
31+
32+
public function test(): void {
33+
assertType('Bug9860\\ANode', $this->b(new A()));
34+
assertType('Bug9860\\BNode', $this->b(new B()));
35+
assertType('Bug9860\\ANode|Bug9860\\BNode', $this->b($this->a()));
36+
}
37+
}

0 commit comments

Comments
(0)

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