Skip to content

Commit 95d3e90

Browse files
authored
array_key_exist inference is lost when adding a false condition (#4473)
1 parent 3578a23 commit 95d3e90

File tree

7 files changed

+132
-5
lines changed

7 files changed

+132
-5
lines changed

src/Analyser/TypeSpecifier.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,24 @@ public function specifyTypesInCondition(
663663
$leftTypes = $this->specifyTypesInCondition($scope, $expr->left, $context)->setRootExpr($expr);
664664
$rightScope = $scope->filterByFalseyValue($expr->left);
665665
$rightTypes = $this->specifyTypesInCondition($rightScope, $expr->right, $context)->setRootExpr($expr);
666-
$types = $context->true() ? $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($rightScope)) : $leftTypes->unionWith($rightTypes);
666+
667+
if ($context->true()) {
668+
if (
669+
$scope->getType($expr->left)->toBoolean()->isFalse()->yes()
670+
) {
671+
$types = $rightTypes->normalize($rightScope);
672+
} elseif (
673+
$scope->getType($expr->left)->toBoolean()->isTrue()->yes()
674+
|| $scope->getType($expr->right)->toBoolean()->isFalse()->yes()
675+
) {
676+
$types = $leftTypes->normalize($scope);
677+
} else {
678+
$types = $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($rightScope));
679+
}
680+
} else {
681+
$types = $leftTypes->unionWith($rightTypes);
682+
}
683+
667684
if ($context->true()) {
668685
return (new SpecifiedTypes(
669686
$types->getSureTypes(),

tests/PHPStan/Analyser/TypeSpecifierTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ public static function dataCondition(): iterable
353353
self::createFunctionCall('is_int', 'foo'),
354354
self::createFunctionCall('is_string', 'bar'),
355355
),
356-
[],
356+
['$foo' => 'int'],
357357
['$foo' => '~int', '$bar' => '~string'],
358358
],
359359
[
@@ -652,7 +652,7 @@ public static function dataCondition(): iterable
652652
[
653653
new Expr\Empty_(new Variable('array')),
654654
[
655-
'$array' => 'array{}|null',
655+
'$array' => 'array{}',
656656
],
657657
[
658658
'$array' => '~0|0.0|\'\'|\'0\'|array{}|false|null',
@@ -664,7 +664,7 @@ public static function dataCondition(): iterable
664664
'$array' => '~0|0.0|\'\'|\'0\'|array{}|false|null',
665665
],
666666
[
667-
'$array' => 'array{}|null',
667+
'$array' => 'array{}',
668668
],
669669
],
670670
[
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace Bug11276;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doFoo(int $i, int $j, $arr): void
8+
{
9+
if (false || array_key_exists($i, $arr)) {
10+
assertType('non-empty-array', $arr);
11+
}
12+
13+
if (array_key_exists($i, $arr) || false) {
14+
assertType('non-empty-array', $arr);
15+
}
16+
17+
if ("0" || array_key_exists($i, $arr)) {
18+
assertType('non-empty-array', $arr);
19+
}
20+
21+
if (array_key_exists($i, $arr) || "0") {
22+
assertType('non-empty-array', $arr);
23+
}
24+
25+
if (true || array_key_exists($i, $arr)) {
26+
assertType('mixed', $arr);
27+
}
28+
29+
if (array_key_exists($i, $arr) || true) {
30+
assertType('mixed', $arr);
31+
}
32+
33+
if ("1" || array_key_exists($i, $arr)) {
34+
assertType('mixed', $arr);
35+
}
36+
37+
if (array_key_exists($i, $arr) || "1") {
38+
assertType('mixed', $arr);
39+
}
40+
41+
if (array_key_exists($i, $arr) || array_key_exists($j, $arr)) {
42+
assertType('non-empty-array', $arr);
43+
}
44+
45+
if (!array_key_exists($j, $arr)) {
46+
if (array_key_exists($i, $arr) || array_key_exists($j, $arr)) {
47+
assertType('non-empty-array', $arr);
48+
}
49+
}
50+
if (!array_key_exists($i, $arr)) {
51+
if (array_key_exists($i, $arr) || array_key_exists($j, $arr)) {
52+
assertType('non-empty-array', $arr);
53+
}
54+
}
55+
56+
if (array_key_exists($i, $arr)) {
57+
assertType('non-empty-array', $arr);
58+
}
59+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php // lint >= 8.0
2+
3+
namespace Bug11276b;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
function doBar($j, array $arr) {
8+
$i = 1;
9+
if (!array_key_exists($i, $arr)) {
10+
if (array_key_exists($i, $arr) || array_key_exists($j, $arr)) {
11+
assertType('non-empty-array<mixed~1, mixed>', $arr);
12+
}
13+
}
14+
15+
if (!array_key_exists($i, $arr)) {
16+
if (array_key_exists($j, $arr) || array_key_exists($i, $arr)) {
17+
assertType('non-empty-array<mixed~1, mixed>', $arr);
18+
}
19+
}
20+
}

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,4 +1132,10 @@ public function testBug6209(): void
11321132
$this->analyse([__DIR__ . '/data/bug-6209.php'], []);
11331133
}
11341134

1135+
public function testBug11276(): void
1136+
{
1137+
$this->reportPossiblyNonexistentConstantArrayOffset = true;
1138+
$this->analyse([__DIR__ . '/data/bug-11276.php'], []);
1139+
}
1140+
11351141
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11276;
4+
5+
class HelloWorld
6+
{
7+
/**
8+
* @param array{from: string, to: string} $expected
9+
*/
10+
public function testLanguagesMatchingRegex(string $url, ?array $expected): void
11+
{
12+
preg_match('#\/(?<from>[a-z]{2})-(?<to>[a-z]{1}[a-z0-9]{1})\/#', $url, $matches);
13+
14+
foreach ($expected as $key => $value) {
15+
if ($matches instanceof ArrayAccess || \array_key_exists($key, $matches)) {
16+
$matches[$key];
17+
}
18+
}
19+
20+
foreach ($expected as $key => $value) {
21+
if (\array_key_exists($key, $matches) || $matches instanceof ArrayAccess) {
22+
$matches[$key];
23+
}
24+
}
25+
}
26+
}

tests/PHPStan/Rules/Comparison/BooleanOrConstantConditionRuleTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@ public function testReportPhpDoc(): void
239239
[
240240
'Right side of || is always true.',
241241
33,
242-
$tipText,
243242
],
244243
]);
245244
}

0 commit comments

Comments
 (0)