diff --git a/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php b/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php index ffbdc4069f..c48fee653f 100644 --- a/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php +++ b/src/Type/Php/ArrayKeyExistsFunctionTypeSpecifyingExtension.php @@ -77,17 +77,15 @@ public function specifyTypes( if ($context->true()) { $specifiedTypes = new SpecifiedTypes(); - if (count($keyType->getConstantScalarTypes()) <= 1) { - $nonEmptyType = $arrayType->isArray()->yes() - ? new NonEmptyArrayType() - : TypeCombinator::intersect(new ArrayType(new MixedType(), new MixedType()), new NonEmptyArrayType()); - $specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create( - $array, - $nonEmptyType, - $context, - $scope, - )); - } + $nonEmptyType = $arrayType->isArray()->yes() + ? new NonEmptyArrayType() + : TypeCombinator::intersect(new ArrayType(new MixedType(), new MixedType()), new NonEmptyArrayType()); + $specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create( + $array, + $nonEmptyType, + $context, + $scope, + )); if ($arrayType->isIterableAtLeastOnce()->no()) { return $specifiedTypes; diff --git a/tests/PHPStan/Analyser/nsrt/bug-14489.php b/tests/PHPStan/Analyser/nsrt/bug-14489.php new file mode 100644 index 0000000000..58f8d17b71 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-14489.php @@ -0,0 +1,46 @@ + [1], 'c2' => [4]]]; + + $cData = []; + foreach ($data as $cMap) { + foreach ($cMap as $c => $ids) { + if (array_key_exists($c, $cData)) { + $cData[$c] = array_unique(array_merge($cData[$c], $ids)); + } else { + $cData[$c] = $ids; + } + } + } + + $values = array_values($cData); + assertType('non-empty-list>', $values); +}; + +function () { + /** @var 'c1'|'c2' $c */ + $c = 'c1'; + /** @var array{1}|array{4} $ids */ + $ids = [1]; + + $cData = []; + while (rand(0, 1)) { + if (array_key_exists($c, $cData)) { + assertType('non-empty-array<\'c1\'|\'c2\', array{1}|array{4}>', $cData); + assertType('array{1}|array{4}', $cData[$c]); + $cData[$c] = $cData[$c]; + } else { + $cData[$c] = $ids; + } + } + assertType('array<\'c1\'|\'c2\', array{1}|array{4}>', $cData); +}; diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index a3a31d3948..7cd71f4123 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -419,7 +419,7 @@ public function testBug7000b(): void { $this->analyse([__DIR__ . '/data/bug-7000b.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, ], ]);