Skip to content

Commit 4321374

Browse files
committed
Fix in_array() for arrays with union value type
1 parent b7b6655 commit 4321374

File tree

4 files changed

+123
-16
lines changed

4 files changed

+123
-16
lines changed

phpstan-baseline.neon

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,6 @@ parameters:
288288
count: 1
289289
path: src/Testing/PHPStanTestCase.php
290290

291-
-
292-
message: "#^Call to function in_array\\(\\) with arguments 'array', array\\<int, 'array'\\|'string'\\> and true will always evaluate to true\\.$#"
293-
count: 1
294-
path: src/Type/IntersectionType.php
295-
296291
-
297292
message: """
298293
#^Call to deprecated method getInstance\\(\\) of class PHPStan\\\\Broker\\\\Broker\\:

src/Rules/Comparison/ImpossibleCheckTypeHelper.php

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,32 @@ public function findSpecifiedType(
9191
return null;
9292
}
9393

94-
if (!$haystackType instanceof ConstantArrayType || count($haystackType->getValueTypes()) > 0) {
95-
$needleType = $scope->getType($node->getArgs()[0]->value);
94+
$constantArrays = TypeUtils::getConstantArrays($haystackType);
95+
$needleType = $scope->getType($node->getArgs()[0]->value);
96+
$valueType = $haystackType->getIterableValueType();
97+
$constantNeedleTypesCount = count(TypeUtils::getConstantScalars($needleType));
98+
$constantHaystackTypesCount = count(TypeUtils::getConstantScalars($valueType));
99+
$isNeedleSupertype = $needleType->isSuperTypeOf($valueType);
100+
if (count($constantArrays) === 0) {
101+
if ($haystackType->isIterableAtLeastOnce()->yes()) {
102+
if ($constantNeedleTypesCount === 1 && $constantHaystackTypesCount === 1) {
103+
if ($isNeedleSupertype->yes()) {
104+
return true;
105+
}
106+
if ($isNeedleSupertype->no()) {
107+
return false;
108+
}
109+
}
110+
}
111+
return null;
112+
}
96113

114+
if (!$haystackType instanceof ConstantArrayType || count($haystackType->getValueTypes()) > 0) {
97115
$haystackArrayTypes = TypeUtils::getArrays($haystackType);
98116
if (count($haystackArrayTypes) === 1 && $haystackArrayTypes[0]->getIterableValueType() instanceof NeverType) {
99117
return null;
100118
}
101119

102-
$valueType = $haystackType->getIterableValueType();
103-
$isNeedleSupertype = $needleType->isSuperTypeOf($valueType);
104-
105120
if ($isNeedleSupertype->maybe() || $isNeedleSupertype->yes()) {
106121
foreach ($haystackArrayTypes as $haystackArrayType) {
107122
foreach (TypeUtils::getConstantScalars($haystackArrayType->getIterableValueType()) as $constantScalarType) {
@@ -115,13 +130,10 @@ public function findSpecifiedType(
115130
}
116131

117132
if ($isNeedleSupertype->yes()) {
118-
$hasConstantNeedleTypes = count(TypeUtils::getConstantScalars($needleType)) > 0;
119-
$hasConstantHaystackTypes = count(TypeUtils::getConstantScalars($valueType)) > 0;
133+
$hasConstantNeedleTypes = $constantNeedleTypesCount > 0;
134+
$hasConstantHaystackTypes = $constantHaystackTypesCount > 0;
120135
if (
121-
(
122-
!$hasConstantNeedleTypes
123-
&& !$hasConstantHaystackTypes
124-
)
136+
(!$hasConstantNeedleTypes && !$hasConstantHaystackTypes)
125137
|| $hasConstantNeedleTypes !== $hasConstantHaystackTypes
126138
) {
127139
return null;

tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,4 +462,24 @@ public function testBug5369(): void
462462
$this->analyse([__DIR__ . '/data/bug-5369.php'], []);
463463
}
464464

465+
public function testBugInArrayDateFormat(): void
466+
{
467+
$this->checkAlwaysTrueCheckTypeFunctionCall = true;
468+
$this->treatPhpDocTypesAsCertain = true;
469+
$this->analyse([__DIR__ . '/data/in-array-date-format.php'], [
470+
[
471+
'Call to function in_array() with arguments \'a\', non-empty-array<int, \'a\'> and true will always evaluate to true.',
472+
39,
473+
],
474+
[
475+
'Call to function in_array() with arguments \'b\', non-empty-array<int, \'a\'> and true will always evaluate to false.',
476+
43,
477+
],
478+
[
479+
'Call to function in_array() with arguments int, array{} and true will always evaluate to false.',
480+
47,
481+
],
482+
]);
483+
}
484+
465485
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace InArrayDateFormat;
4+
5+
class Foo
6+
{
7+
8+
public function doFoo(array $a, \DateTimeImmutable $dt)
9+
{
10+
$result = [];
11+
foreach ($a as $k => $v) {
12+
$result[$k] = $dt->format('d');
13+
}
14+
15+
$d = new \DateTimeImmutable();
16+
if (in_array($d->format('d'), $result, true)) {
17+
18+
}
19+
20+
if (in_array('01', $result, true)) {
21+
22+
}
23+
24+
$day = $d->format('d');
25+
if (rand(0, 1)) {
26+
$day = '32';
27+
}
28+
29+
if (in_array($day, $result, true)) {
30+
31+
}
32+
}
33+
34+
/**
35+
* @param non-empty-array<int, 'a'> $a
36+
*/
37+
public function doBar(array $a, int $i)
38+
{
39+
if (in_array('a', $a, true)) {
40+
41+
}
42+
43+
if (in_array('b', $a, true)) {
44+
45+
}
46+
47+
if (in_array($i, [], true)) {
48+
49+
}
50+
}
51+
52+
/**
53+
* @param array<int, string> $a
54+
*/
55+
public function doBaz(array $a, int $i, string $s)
56+
{
57+
if (in_array($s, $a, true)) {
58+
59+
}
60+
61+
if (in_array($i, $a, true)) {
62+
63+
}
64+
}
65+
66+
/**
67+
* @param non-empty-array<int, 'a'|'b'> $a
68+
*/
69+
public function doLorem(array $a, int $i)
70+
{
71+
if (in_array('a', $a, true)) {
72+
73+
}
74+
75+
if (in_array('b', $a, true)) {
76+
77+
}
78+
}
79+
80+
}

0 commit comments

Comments
 (0)