Skip to content

Commit 78ac6f2

Browse files
Fix subtracting enums inside in_array
Co-authored-by: Ondrej Mirtes <[email protected]>
1 parent abe6f3c commit 78ac6f2

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
110110
$context->true()
111111
|| (
112112
$context->false()
113-
&& count($arrayValueType->getFiniteTypes()) === 1
113+
&& count($arrayValueType->getFiniteTypes()) > 0
114+
&& count($needleType->getFiniteTypes()) > 0
115+
&& $arrayType->isIterableAtLeastOnce()->yes()
114116
)
115117
) {
116118
$specifiedTypes = $this->typeSpecifier->create(
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
<?php // lint >= 8.1
2+
3+
use function PHPStan\Testing\assertType;
4+
5+
enum MyEnum: string
6+
{
7+
8+
case A = 'a';
9+
case B = 'b';
10+
case C = 'c';
11+
12+
const SET_AB = [self::A, self::B];
13+
const SET_C = [self::C];
14+
const SET_ABC = [self::A, self::B, self::C];
15+
16+
public function test1(): void
17+
{
18+
foreach (self::cases() as $enum) {
19+
if (in_array($enum, MyEnum::SET_AB, true)) {
20+
assertType('MyEnum::A|MyEnum::B', $enum);
21+
} elseif (in_array($enum, MyEnum::SET_C, true)) {
22+
assertType('MyEnum::C', $enum);
23+
} else {
24+
assertType('*NEVER*', $enum);
25+
}
26+
}
27+
}
28+
29+
public function test2(): void
30+
{
31+
foreach (self::cases() as $enum) {
32+
if (in_array($enum, MyEnum::SET_ABC, true)) {
33+
assertType('MyEnum::A|MyEnum::B|MyEnum::C', $enum);
34+
} else {
35+
assertType('*NEVER*', $enum);
36+
}
37+
}
38+
}
39+
40+
public function test3(): void
41+
{
42+
foreach (self::cases() as $enum) {
43+
if (in_array($enum, MyEnum::SET_C, true)) {
44+
assertType('MyEnum::C', $enum);
45+
} else {
46+
assertType('MyEnum::A|MyEnum::B', $enum);
47+
}
48+
}
49+
}
50+
51+
public function test4(): void
52+
{
53+
foreach ([MyEnum::C] as $enum) {
54+
if (in_array($enum, MyEnum::SET_C, true)) {
55+
assertType('MyEnum::C', $enum);
56+
} else {
57+
assertType('*NEVER*', $enum);
58+
}
59+
}
60+
}
61+
62+
public function testNegative1(): void
63+
{
64+
foreach (self::cases() as $enum) {
65+
if (!in_array($enum, MyEnum::SET_AB, true)) {
66+
assertType('MyEnum::C', $enum);
67+
} else {
68+
assertType('MyEnum::A|MyEnum::B', $enum);
69+
}
70+
}
71+
}
72+
73+
public function testNegative2(): void
74+
{
75+
foreach (self::cases() as $enum) {
76+
if (!in_array($enum, MyEnum::SET_AB, true)) {
77+
assertType('MyEnum::C', $enum);
78+
} elseif (!in_array($enum, MyEnum::SET_AB, true)) {
79+
assertType('*NEVER*', $enum);
80+
}
81+
}
82+
}
83+
84+
public function testNegative3(): void
85+
{
86+
foreach ([MyEnum::C] as $enum) {
87+
if (!in_array($enum, MyEnum::SET_C, true)) {
88+
assertType('*NEVER*', $enum);
89+
}
90+
}
91+
}
92+
93+
/**
94+
* @param array<MyEnum|null> $array
95+
*/
96+
public function testNegative4(MyEnum $enum, array $array): void
97+
{
98+
if (!in_array($enum, $array, true)) {
99+
assertType('MyEnum', $enum);
100+
assertType('array<MyEnum|null>', $array);
101+
} else {
102+
assertType('MyEnum', $enum);
103+
assertType('non-empty-array<MyEnum|null>', $array);
104+
}
105+
}
106+
107+
}
108+
109+
class InArrayEnum
110+
{
111+
112+
/** @param list<MyEnum> $list */
113+
public function testPositive(MyEnum $enum, array $list): void
114+
{
115+
if (in_array($enum, $list, true)) {
116+
return;
117+
}
118+
119+
assertType(MyEnum::class, $enum);
120+
assertType('list<MyEnum>', $list);
121+
}
122+
123+
/** @param list<MyEnum> $list */
124+
public function testNegative(MyEnum $enum, array $list): void
125+
{
126+
if (!in_array($enum, $list, true)) {
127+
return;
128+
}
129+
130+
assertType(MyEnum::class, $enum);
131+
assertType('non-empty-list<MyEnum>', $list);
132+
}
133+
134+
}
135+
136+
137+
class InArrayOtherFiniteType {
138+
139+
const SET_AB = ['a', 'b'];
140+
const SET_C = ['c'];
141+
const SET_ABC = ['a', 'b', 'c'];
142+
143+
public function test1(): void
144+
{
145+
foreach (['a', 'b', 'c'] as $item) {
146+
if (in_array($item, self::SET_AB, true)) {
147+
assertType("'a'|'b'", $item);
148+
} elseif (in_array($item, self::SET_C, true)) {
149+
assertType("'c'", $item);
150+
} else {
151+
assertType('*NEVER*', $item);
152+
}
153+
}
154+
}
155+
156+
public function test2(): void
157+
{
158+
foreach (['a', 'b', 'c'] as $item) {
159+
if (in_array($item, self::SET_ABC, true)) {
160+
assertType("'a'|'b'|'c'", $item);
161+
} else {
162+
assertType('*NEVER*', $item);
163+
}
164+
}
165+
}
166+
167+
public function test3(): void
168+
{
169+
foreach (['a', 'b', 'c'] as $item) {
170+
if (in_array($item, self::SET_C, true)) {
171+
assertType("'c'", $item);
172+
} else {
173+
assertType("'a'|'b'", $item);
174+
}
175+
}
176+
}
177+
public function test4(): void
178+
{
179+
foreach (['c'] as $item) {
180+
if (in_array($item, self::SET_C, true)) {
181+
assertType("'c'", $item);
182+
} else {
183+
assertType('*NEVER*', $item);
184+
}
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)