diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 0d312f865a..cceef0e7c0 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -1077,6 +1077,7 @@ public function specifyTypesInCondition( ); } else { $varType = $scope->getType($var->var); + $narrowedKey = AllowedArrayKeysTypes::narrowOffsetKeyType($varType, $dimType); if ($narrowedKey !== null) { $types = $types->unionWith( @@ -1088,6 +1089,17 @@ public function specifyTypesInCondition( )->setRootExpr($expr), ); } + + if ($varType->isArray()->yes()) { + $types = $types->unionWith( + $this->create( + $var->var, + new NonEmptyArrayType(), + $context, + $scope, + )->setRootExpr($expr), + ); + } } } diff --git a/tests/PHPStan/Analyser/nsrt/bug-12274.php b/tests/PHPStan/Analyser/nsrt/bug-12274.php index d4ad2e302d..7e899600be 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-12274.php +++ b/tests/PHPStan/Analyser/nsrt/bug-12274.php @@ -40,7 +40,7 @@ function getItemsByModifiedIndex(array $items): array function testKeepListAfterIssetIndex(array $list, int $i): void { if (isset($list[$i])) { - assertType('list', $list); + assertType('non-empty-list', $list); $list[$i] = 21; assertType('non-empty-list', $list); $list[$i+1] = 21; @@ -53,8 +53,8 @@ function testKeepListAfterIssetIndex(array $list, int $i): void function testKeepNestedListAfterIssetIndex(array $nestedList, int $i, int $j): void { if (isset($nestedList[$i][$j])) { - assertType('list>', $nestedList); - assertType('list', $nestedList[$i]); + assertType('non-empty-list>', $nestedList); + assertType('non-empty-list', $nestedList[$i]); $nestedList[$i][$j] = 21; assertType('non-empty-list>', $nestedList); assertType('list', $nestedList[$i]); @@ -66,7 +66,7 @@ function testKeepNestedListAfterIssetIndex(array $nestedList, int $i, int $j): v function testKeepListAfterIssetIndexPlusOne(array $list, int $i): void { if (isset($list[$i])) { - assertType('list', $list); + assertType('non-empty-list', $list); $list[$i+1] = 21; assertType('non-empty-list', $list); } @@ -77,7 +77,7 @@ function testKeepListAfterIssetIndexPlusOne(array $list, int $i): void function testKeepListAfterIssetIndexOnePlus(array $list, int $i): void { if (isset($list[$i])) { - assertType('list', $list); + assertType('non-empty-list', $list); $list[1+$i] = 21; assertType('non-empty-list', $list); } @@ -90,7 +90,7 @@ function testShouldLooseListbyAst(array $list, int $i): void if (isset($list[$i])) { $i++; - assertType('list', $list); + assertType('non-empty-list', $list); $list[1+$i] = 21; assertType('non-empty-array, int>', $list); } @@ -101,7 +101,7 @@ function testShouldLooseListbyAst(array $list, int $i): void function testShouldLooseListbyAst2(array $list, int $i): void { if (isset($list[$i])) { - assertType('list', $list); + assertType('non-empty-list', $list); $list[2+$i] = 21; assertType('non-empty-array, int>', $list); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-13674b.php b/tests/PHPStan/Analyser/nsrt/bug-13674b.php new file mode 100644 index 0000000000..3baabc168b --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13674b.php @@ -0,0 +1,39 @@ + $arrayA + * @param list $listA + */ + public function sayHello($arrayA, $listA, int $i): void + { + if (isset($arrayA[$i])) { + assertType('non-empty-array', $arrayA); + } else { + assertType('array', $arrayA); + } + assertType('array', $arrayA); + + if (isset($listA[$i])) { + assertType('non-empty-list', $listA); + } else { + assertType('list', $listA); + } + assertType('list', $listA); + + if (!isset($listA[$i])) { + assertType('list', $listA); + return; + } + assertType('non-empty-list', $listA); + + $emptyArray = []; + assertType('false', isset($emptyArray[$i])); + } +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-13675.php b/tests/PHPStan/Analyser/nsrt/bug-13675.php new file mode 100644 index 0000000000..a16bbf1c2d --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13675.php @@ -0,0 +1,27 @@ + $listA + * @param list $listB + */ + public function sayHello(int $i, $listA, $listB): void + { + if (!isset($listA[$i])) { + return; + } + assertType('non-empty-list', $listA); + + if (count($listA) !== count($listB)) { + return; + } + assertType('non-empty-list', $listB); + } +} + diff --git a/tests/PHPStan/Analyser/nsrt/bug-7000.php b/tests/PHPStan/Analyser/nsrt/bug-7000.php index a2e536a6da..3ad9a1b2d8 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-7000.php +++ b/tests/PHPStan/Analyser/nsrt/bug-7000.php @@ -12,7 +12,7 @@ public function doBar(): void $composer = array(); foreach (array('require', 'require-dev') as $linkType) { if (isset($composer[$linkType])) { - assertType('array{require?: array, require-dev?: array}', $composer); + assertType('non-empty-array{require?: array, require-dev?: array}', $composer); foreach ($composer[$linkType] as $x) {} } } diff --git a/tests/PHPStan/Analyser/nsrt/has-offset-type-bug.php b/tests/PHPStan/Analyser/nsrt/has-offset-type-bug.php index eacfb06af6..09955bde2e 100644 --- a/tests/PHPStan/Analyser/nsrt/has-offset-type-bug.php +++ b/tests/PHPStan/Analyser/nsrt/has-offset-type-bug.php @@ -26,7 +26,7 @@ public function doFoo(array $errorMessages): void continue; } - assertType('array>', $fileErrorsCounts); + assertType('non-empty-array>', $fileErrorsCounts); assertType('int<1, max>', $fileErrorsCounts[$errorMessage]); $fileErrorsCounts[$errorMessage]++; diff --git a/tests/PHPStan/Analyser/nsrt/shopware-connection-profiler.php b/tests/PHPStan/Analyser/nsrt/shopware-connection-profiler.php index ced23ea733..431a78fcbf 100644 --- a/tests/PHPStan/Analyser/nsrt/shopware-connection-profiler.php +++ b/tests/PHPStan/Analyser/nsrt/shopware-connection-profiler.php @@ -29,7 +29,7 @@ public function getGroupedQueries(): void $connectionGroupedQueries[$key]['index'] = $i; // "Explain query" relies on query index in 'queries'. } - assertType("array, count: 0, index: int}|array{sql: string, executionMS: float, types: array, count: int<1, max>, index: int}>", $connectionGroupedQueries); + assertType("non-empty-array, count: 0, index: int}|array{sql: string, executionMS: float, types: array, count: int<1, max>, index: int}>", $connectionGroupedQueries); $connectionGroupedQueries[$key]['executionMS'] += $query['executionMS']; assertType("non-empty-array, count: int<0, max>, index: int}>", $connectionGroupedQueries); ++$connectionGroupedQueries[$key]['count']; diff --git a/tests/PHPStan/Analyser/nsrt/specified-types-closure-use.php b/tests/PHPStan/Analyser/nsrt/specified-types-closure-use.php index 9cd49e4522..f5f8189ded 100644 --- a/tests/PHPStan/Analyser/nsrt/specified-types-closure-use.php +++ b/tests/PHPStan/Analyser/nsrt/specified-types-closure-use.php @@ -48,10 +48,10 @@ function ($arr) use ($key): void { public function doBuzz(array $arr, string $key): void { if (isset($arr[$key])) { - assertType('array', $arr); + assertType('non-empty-array', $arr); assertType("mixed~null", $arr[$key]); function () use ($arr, $key): void { - assertType('array', $arr); + assertType('non-empty-array', $arr); assertType("mixed~null", $arr[$key]); }; } @@ -60,10 +60,10 @@ function () use ($arr, $key): void { public function doBuzz(array $arr, string $key): void { if (isset($arr[$key])) { - assertType('array', $arr); + assertType('non-empty-array', $arr); assertType("mixed~null", $arr[$key]); function ($key) use ($arr): void { - assertType('array', $arr); + assertType('non-empty-array', $arr); assertType("mixed", $arr[$key]); }; } diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index e239384855..00783f481b 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -395,7 +395,7 @@ public function testBug7000(): void { $this->analyse([__DIR__ . '/data/bug-7000.php'], [ [ - "Offset 'require'|'require-dev' might not exist on array{require?: array, require-dev?: array}.", + "Offset 'require'|'require-dev' might not exist on non-empty-array{require?: array, require-dev?: array}.", 16, ], ]); diff --git a/tests/PHPStan/Rules/Arrays/data/bug-11679.php b/tests/PHPStan/Rules/Arrays/data/bug-11679.php index 46374b8cb2..a5079cbbd6 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-11679.php +++ b/tests/PHPStan/Rules/Arrays/data/bug-11679.php @@ -34,7 +34,7 @@ public function sayHello(int $index): bool assertType('non-empty-array', $this->arr); assertType('true', $this->arr[$index]['foo']); } - assertType('array', $this->arr); + assertType('non-empty-array', $this->arr); return $this->arr[$index]['foo']; // PHPStan does not realize 'foo' is set } } diff --git a/tests/PHPStan/Rules/Variables/IssetRuleTest.php b/tests/PHPStan/Rules/Variables/IssetRuleTest.php index be2f2b3c68..11f2b39f1f 100644 --- a/tests/PHPStan/Rules/Variables/IssetRuleTest.php +++ b/tests/PHPStan/Rules/Variables/IssetRuleTest.php @@ -493,7 +493,7 @@ public function testPr4374(): void $this->analyse([__DIR__ . '/data/pr-4374.php'], [ [ - 'Offset string on array in isset() always exists and is not nullable.', + 'Offset string on non-empty-array in isset() always exists and is not nullable.', 23, ], ]); diff --git a/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php b/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php index 1833e4f681..32cfa69d97 100644 --- a/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php +++ b/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php @@ -280,7 +280,7 @@ public function testBug7190(): void { $this->analyse([__DIR__ . '/../Properties/data/bug-7190.php'], [ [ - 'Offset int on array on left side of ?? always exists and is not nullable.', + 'Offset int on non-empty-array on left side of ?? always exists and is not nullable.', 20, ], ]);