Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 300b7b2

Browse files
authored
infer non-empty-list/array after array_key_exists($i, $arr) (#4440)
1 parent 27ccf57 commit 300b7b2

File tree

5 files changed

+71
-6
lines changed

5 files changed

+71
-6
lines changed

‎src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,19 @@ public function specifyTypes(
6666
&& !$keyType instanceof ConstantStringType
6767
) {
6868
if ($context->true()) {
69-
if ($arrayType->isIterableAtLeastOnce()->no()) {
70-
return $this->typeSpecifier->create(
69+
$specifiedTypes = new SpecifiedTypes();
70+
71+
if (count($keyType->getConstantScalarTypes()) <= 1) {
72+
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
7173
$array,
7274
new NonEmptyArrayType(),
7375
$context,
7476
$scope,
75-
);
77+
));
78+
}
79+
80+
if ($arrayType->isIterableAtLeastOnce()->no()) {
81+
return $specifiedTypes;
7682
}
7783

7884
$arrayKeyType = $arrayType->getIterableKeyType();
@@ -82,12 +88,12 @@ public function specifyTypes(
8288
$arrayKeyType = TypeCombinator::union($arrayKeyType, $arrayKeyType->toString());
8389
}
8490

85-
$specifiedTypes = $this->typeSpecifier->create(
91+
$specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create(
8692
$key,
8793
$arrayKeyType,
8894
$context,
8995
$scope,
90-
);
96+
));
9197

9298
$arrayDimFetch = new ArrayDimFetch(
9399
$array,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Bug13674a;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param array<int> $arrayA
11+
* @param list<int> $listA
12+
*/
13+
public function sayHello($arrayA, $listA, int $i): void
14+
{
15+
if (array_key_exists($i, $arrayA)) {
16+
assertType('non-empty-array<int>', $arrayA);
17+
} else {
18+
assertType('array<int>', $arrayA);
19+
}
20+
assertType('array<int>', $arrayA);
21+
22+
if (array_key_exists($i, $listA)) {
23+
assertType('non-empty-list<int>', $listA);
24+
} else {
25+
assertType('list<int>', $listA);
26+
}
27+
assertType('list<int>', $listA);
28+
}
29+
}

‎tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,16 @@ public function testBug7000(): void
406406
]);
407407
}
408408

409+
public function testBug7000b(): void
410+
{
411+
$this->analyse([__DIR__ . '/data/bug-7000b.php'], [
412+
[
413+
"Offset 'require'|'require-dev' might not exist on array{require?: array<string, string>, require-dev?: array<string, string>}.",
414+
16,
415+
],
416+
]);
417+
}
418+
409419
public function testBug6508(): void
410420
{
411421
$this->analyse([__DIR__ . '/data/bug-6508.php'], []);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7000b;
4+
5+
class Foo
6+
{
7+
public function doBar(): void
8+
{
9+
/** @var array{require?: array<string, string>, require-dev?: array<string, string>} $composer */
10+
$composer = array();
11+
/** @var 'require'|'require-dev' $foo */
12+
$foo = '';
13+
foreach (array('require', 'require-dev') as $linkType) {
14+
if (array_key_exists($linkType, $composer)) {
15+
foreach ($composer[$linkType] as $x) {} // should not report error
16+
foreach ($composer[$foo] as $x) {} // should report error. It can be $linkType = 'require', $foo = 'require-dev'
17+
}
18+
}
19+
}
20+
}

‎tests/PHPStan/Rules/Arrays/data/slevomat-foreach-array-key-exists-bug.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public function doFoo(array $percentageIntervals, array $changes): void
1515
if ($percentageInterval->isInInterval((float) $changeInPercents)) {
1616
$key = $percentageInterval->getFormatted();
1717
if (array_key_exists($key, $intervalResults)) {
18-
assertType('array<array{itemsCount: mixed, interval: mixed}>', $intervalResults);
18+
assertType('non-empty-array<array{itemsCount: mixed, interval: mixed}>', $intervalResults);
1919
assertType('array{itemsCount: mixed, interval: mixed}', $intervalResults[$key]);
2020
$intervalResults[$key]['itemsCount'] += $itemsCount;
2121
assertType('non-empty-array<array{itemsCount: (array|float|int), interval: mixed}>', $intervalResults);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /