Skip to content

Commit 4382314

Browse files
committed
array_key_exist inference is lost when adding a false condition
1 parent 20f2f27 commit 4382314

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed

src/Analyser/TypeSpecifier.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,25 @@ 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)->isFalse()->yes()
670+
|| $scope->getType($expr->right)->isTrue()->yes()
671+
) {
672+
$types = $rightTypes->normalize($rightScope);
673+
} elseif (
674+
$scope->getType($expr->left)->isTrue()->yes()
675+
|| $scope->getType($expr->right)->isFalse()->yes()
676+
) {
677+
$types = $leftTypes->normalize($scope);
678+
} else {
679+
$types = $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($rightScope));
680+
}
681+
} else {
682+
$types = $leftTypes->unionWith($rightTypes);
683+
}
684+
667685
if ($context->true()) {
668686
return (new SpecifiedTypes(
669687
$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: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
use function PHPStan\Testing\assertType;
4+
5+
function sayEqualArrayShape(int $i, $arr): void
6+
{
7+
if (false || array_key_exists($i, $arr)) {
8+
assertType('non-empty-array', $arr);
9+
}
10+
11+
if (array_key_exists($i, $arr) || false) {
12+
assertType('non-empty-array', $arr);
13+
}
14+
15+
if (true || array_key_exists($i, $arr)) {
16+
assertType('mixed', $arr);
17+
}
18+
19+
if (array_key_exists($i, $arr) || true) {
20+
assertType('mixed', $arr);
21+
}
22+
23+
if (array_key_exists($i, $arr)) {
24+
assertType('non-empty-array', $arr);
25+
}
26+
}

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+
}

0 commit comments

Comments
 (0)