Skip to content

Commit 1cc0471

Browse files
committed
Fix for other finite types
1 parent c333639 commit 1cc0471

File tree

3 files changed

+64
-29
lines changed

3 files changed

+64
-29
lines changed

src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,9 @@
1515
use PHPStan\Reflection\FunctionReflection;
1616
use PHPStan\Type\Accessory\NonEmptyArrayType;
1717
use PHPStan\Type\ArrayType;
18-
use PHPStan\Type\Enum\EnumCaseObjectType;
1918
use PHPStan\Type\FunctionTypeSpecifyingExtension;
20-
use PHPStan\Type\IntersectionType;
2119
use PHPStan\Type\MixedType;
22-
use PHPStan\Type\Type;
2320
use PHPStan\Type\TypeCombinator;
24-
use PHPStan\Type\TypeTraverser;
25-
use PHPStan\Type\UnionType;
2621
use function count;
2722
use function strtolower;
2823

@@ -116,7 +111,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
116111
|| (
117112
$context->false()
118113
&& count($arrayValueType->getFiniteTypes()) > 0
119-
&& $this->isEnumCasesArray($arrayValueType) // avoid eliminating all enum cases in edge case like in_array(Enum, list<Enum>, true)
114+
&& TypeCombinator::union(...$arrayValueType->getFiniteTypes())->equals($arrayValueType)
120115
)
121116
) {
122117
$specifiedTypes = $this->typeSpecifier->create(
@@ -168,23 +163,4 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
168163
return $specifiedTypes;
169164
}
170165

171-
private function isEnumCasesArray(Type $type): bool
172-
{
173-
$containsOnlyEnumCases = true;
174-
175-
TypeTraverser::map($type, static function (Type $type, callable $traverse) use (&$containsOnlyEnumCases): Type {
176-
if ($type instanceof UnionType || $type instanceof IntersectionType) {
177-
return $traverse($type);
178-
}
179-
180-
if (!$type instanceof EnumCaseObjectType) {
181-
$containsOnlyEnumCases = false;
182-
}
183-
184-
return $type;
185-
});
186-
187-
return $containsOnlyEnumCases;
188-
}
189-
190166
}

tests/PHPStan/Analyser/AnalyserIntegrationTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,13 @@ public function testBug11913(): void
14831483
public function testBug12083InArrayEnum(): void
14841484
{
14851485
$errors = $this->runAnalyse(__DIR__ . '/data/enum-in-array.php');
1486-
$this->assertNoErrors($errors);
1486+
1487+
$this->assertCount(2, $errors);
1488+
1489+
$this->assertSame('Call to function in_array() with arguments \'c\', array{\'c\'} and true will always evaluate to true.', $errors[0]->getMessage());
1490+
$this->assertSame(92, $errors[0]->getLine());
1491+
$this->assertSame('Call to function in_array() with arguments \'c\', array{\'c\'} and true will always evaluate to true.', $errors[1]->getMessage());
1492+
$this->assertSame(124, $errors[1]->getLine());
14871493
}
14881494

14891495
/**

tests/PHPStan/Analyser/data/enum-in-array.php

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum MyEnum: string
1515

1616
public function test1(): void
1717
{
18-
foreach (MyEnum::cases() as $enum) {
18+
foreach (self::cases() as $enum) {
1919
if (in_array($enum, MyEnum::SET_AB, true)) {
2020
assertType('MyEnum::A|MyEnum::B', $enum);
2121
} elseif (in_array($enum, MyEnum::SET_C, true)) {
@@ -28,7 +28,7 @@ public function test1(): void
2828

2929
public function test2(): void
3030
{
31-
foreach (MyEnum::cases() as $enum) {
31+
foreach (self::cases() as $enum) {
3232
if (in_array($enum, MyEnum::SET_ABC, true)) {
3333
assertType('MyEnum::A|MyEnum::B|MyEnum::C', $enum);
3434
} else {
@@ -39,7 +39,7 @@ public function test2(): void
3939

4040
public function test3(): void
4141
{
42-
foreach (MyEnum::cases() as $enum) {
42+
foreach (self::cases() as $enum) {
4343
if (in_array($enum, MyEnum::SET_C, true)) {
4444
assertType('MyEnum::C', $enum);
4545
} else {
@@ -76,3 +76,56 @@ public function doFoo(MyEnum $enum): void
7676
}
7777

7878
}
79+
80+
81+
class InArrayOtherFiniteType {
82+
83+
const SET_AB = ['a', 'b'];
84+
const SET_C = ['c'];
85+
const SET_ABC = ['a', 'b', 'c'];
86+
87+
public function test1(): void
88+
{
89+
foreach (['a', 'b', 'c'] as $item) {
90+
if (in_array($item, self::SET_AB, true)) {
91+
assertType("'a'|'b'", $item);
92+
} elseif (in_array($item, self::SET_C, true)) {
93+
assertType("'c'", $item);
94+
} else {
95+
assertType('*NEVER*', $item);
96+
}
97+
}
98+
}
99+
100+
public function test2(): void
101+
{
102+
foreach (['a', 'b', 'c'] as $item) {
103+
if (in_array($item, self::SET_ABC, true)) {
104+
assertType("'a'|'b'|'c'", $item);
105+
} else {
106+
assertType('*NEVER*', $item);
107+
}
108+
}
109+
}
110+
111+
public function test3(): void
112+
{
113+
foreach (['a', 'b', 'c'] as $item) {
114+
if (in_array($item, self::SET_C, true)) {
115+
assertType("'c'", $item);
116+
} else {
117+
assertType("'a'|'b'", $item);
118+
}
119+
}
120+
}
121+
public function test4(): void
122+
{
123+
foreach (['c'] as $item) {
124+
if (in_array($item, self::SET_C, true)) {
125+
assertType("'c'", $item);
126+
} else {
127+
assertType('*NEVER*', $item);
128+
}
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)