diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 4ea8a595f5..99a816e1b6 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -929,6 +929,18 @@ public function specifyTypesInCondition( ); } else { $varType = $scope->getType($var->var); + + if ($varType->isArray()->yes() && count($dimType->getConstantScalarTypes()) <= 1) { + $types = $types->unionWith( + $this->create( + $var->var, + new NonEmptyArrayType(), + $context, + $scope, + )->setRootExpr($expr), + ); + } + $narrowedKey = AllowedArrayKeysTypes::narrowOffsetKeyType($varType, $dimType); if ($narrowedKey !== null) { $types = $types->unionWith( diff --git a/tests/PHPStan/Analyser/nsrt/bug-12274.php b/tests/PHPStan/Analyser/nsrt/bug-12274.php index b17b11aed8..c22fcc2d72 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-13674.php b/tests/PHPStan/Analyser/nsrt/bug-13674.php new file mode 100644 index 0000000000..cabbb99632 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13674.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/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/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/data/bug-11679.php b/tests/PHPStan/Rules/Arrays/data/bug-11679.php index 463362516a..c2badb5b75 100644 --- a/tests/PHPStan/Rules/Arrays/data/bug-11679.php +++ b/tests/PHPStan/Rules/Arrays/data/bug-11679.php @@ -33,7 +33,7 @@ public function sayHello(int $index): bool $this->arr[$index]['foo'] = true; assertType('non-empty-array', $this->arr); } - 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 40b837c134..3cacd9c7f5 100644 --- a/tests/PHPStan/Rules/Variables/IssetRuleTest.php +++ b/tests/PHPStan/Rules/Variables/IssetRuleTest.php @@ -498,7 +498,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 318bd3b275..2f8c90da34 100644 --- a/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php +++ b/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php @@ -285,7 +285,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, ], ]);