Skip to content

Commit f84df88

Browse files
authored
Fix conditional types
1 parent f57052d commit f84df88

File tree

5 files changed

+86
-13
lines changed

5 files changed

+86
-13
lines changed

src/Type/ConditionalType.php

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -113,19 +113,17 @@ 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-
});
116+
$intersectedType = TypeCombinator::intersect($this->subject, $this->target);
117+
$removedType = TypeCombinator::remove($this->subject, $this->target);
118+
119+
$yesType = fn () => TypeTraverser::map(
120+
!$this->negated ? $this->if : $this->else,
121+
fn (Type $type, callable $traverse) => $type === $this->subject ? (!$this->negated ? $intersectedType : $removedType) : $traverse($type),
122+
);
123+
$noType = fn () => TypeTraverser::map(
124+
!$this->negated ? $this->else : $this->if,
125+
fn (Type $type, callable $traverse) => $type === $this->subject ? (!$this->negated ? $removedType : $intersectedType) : $traverse($type),
126+
);
129127

130128
if ($isSuperType->yes()) {
131129
return $yesType();

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,6 +1351,7 @@ public function dataFileAsserts(): iterable
13511351
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8517.php');
13521352
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-9803.php');
13531353
yield from $this->gatherAssertTypes(__DIR__ . '/data/impure-connection-fns.php');
1354+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9963.php');
13541355
}
13551356

13561357
/**
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9963;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param int|int[] $id
11+
*
12+
* @return ($id is array ? static[] : static|false)
13+
*/
14+
final public function find($id)
15+
{
16+
return [];
17+
}
18+
}
19+
20+
function ($something) {
21+
$h = new HelloWorld();
22+
assertType('array<Bug9963\HelloWorld>|Bug9963\HelloWorld|false', $h->find($something));
23+
};

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3048,4 +3048,22 @@ public function testObjectShapes(): void
30483048
]);
30493049
}
30503050

3051+
public function testBug9951(): void
3052+
{
3053+
$this->checkThisOnly = false;
3054+
$this->checkNullables = true;
3055+
$this->checkUnionTypes = true;
3056+
$this->checkExplicitMixed = true;
3057+
$this->analyse([__DIR__ . '/data/bug-9951.php'], [
3058+
[
3059+
'Parameter #1 $field of method Bug9951\Cl::addCondition() expects array<int, Bug9951\AbstractScope|Bug9951\Expressionable>|Bug9951\AbstractScope|Bug9951\Expressionable|string, mixed given.',
3060+
26,
3061+
],
3062+
[
3063+
'Parameter #1 $field of method Bug9951\Cl::addCondition() expects array<int, Bug9951\AbstractScope|Bug9951\Expressionable>|Bug9951\AbstractScope|Bug9951\Expressionable|string, object|string|null given.',
3064+
31,
3065+
],
3066+
]);
3067+
}
3068+
30513069
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9951;
4+
5+
class AbstractScope {}
6+
class Expressionable {}
7+
8+
class Cl
9+
{
10+
/**
11+
* @param AbstractScope|array<int, AbstractScope|Expressionable>|string|Expressionable $field
12+
* @param ($field is string|Expressionable ? ($value is null ? mixed : string) : never) $operator
13+
* @param ($operator is string ? mixed : never) $value
14+
*/
15+
public function addCondition($field, $operator = null, $value = null): void
16+
{
17+
}
18+
19+
public function testStr(string $field, bool $value): void
20+
{
21+
$this->addCondition($field, $value);
22+
}
23+
24+
public function testMixed(mixed $field, bool $value): void
25+
{
26+
$this->addCondition($field, $value);
27+
}
28+
29+
public function testMixedAsUnion(string|object|null $field, bool $value): void
30+
{
31+
$this->addCondition($field, $value);
32+
}
33+
}

0 commit comments

Comments
 (0)