diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index df779e633e..b63fccc5de 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -991,13 +991,16 @@ private function produceArrayDimFetchAssignValueToWrite(array $dimFetchStack, ar $offsetType !== null && $arrayDimFetch !== null && $scope->hasExpressionType($arrayDimFetch)->yes() - && !$offsetValueType->hasOffsetValueType($offsetType)->no() ) { $hasOffsetType = null; - if ($offsetType instanceof ConstantStringType || $offsetType instanceof ConstantIntegerType) { - $hasOffsetType = new HasOffsetValueType($offsetType, $valueToWrite); + if ($offsetValueType->hasOffsetValueType($offsetType)->no()) { + $valueToWrite = $offsetValueType->setOffsetValueType($offsetType, $valueToWrite); + } else { + if ($offsetType instanceof ConstantStringType || $offsetType instanceof ConstantIntegerType) { + $hasOffsetType = new HasOffsetValueType($offsetType, $valueToWrite); + } + $valueToWrite = $offsetValueType->setExistingOffsetValueType($offsetType, $valueToWrite); } - $valueToWrite = $offsetValueType->setExistingOffsetValueType($offsetType, $valueToWrite); if ($valueToWrite->isArray()->yes()) { if ($hasOffsetType !== null) { diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 2e5514dc02..7c6f215085 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -981,7 +981,12 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni } } - if ($this->isList()->yes() && $this->getIterableValueType()->isArray()->yes()) { + if ( + $this->isList()->yes() + && $valueType->isArray()->yes() + && $offsetType !== null + && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($offsetType)->yes() + ) { $result = TypeCombinator::intersect($result, new AccessoryArrayListType()); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-10089.php b/tests/PHPStan/Analyser/nsrt/bug-10089.php index 21122cdfc3..ff8d55eeed 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-10089.php +++ b/tests/PHPStan/Analyser/nsrt/bug-10089.php @@ -22,7 +22,7 @@ protected function create_matrix(int $size): array $matrix[$size - 1][8] = 3; // non-empty-array&hasOffsetValue(8, 3)> - assertType('non-empty-list, 0|3>>', $matrix); + assertType('non-empty-array, 0|3>>', $matrix); for ($i = 0; $i <= $size; $i++) { if ($matrix[$i][8] === 0) { diff --git a/tests/PHPStan/Analyser/nsrt/bug-13629.php b/tests/PHPStan/Analyser/nsrt/bug-13629.php new file mode 100644 index 0000000000..ee2724f4a1 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13629.php @@ -0,0 +1,37 @@ +> $xsdFiles + * @param array> $groupedByNamespace + * @param array> $extraNamespaces + */ +function test(array $xsdFiles, array $groupedByNamespace, array $extraNamespaces): void { + foreach ($extraNamespaces as $mergedNamespace) { + if (count($mergedNamespace) < 2) { + continue; + } + + $targetNamespace = end($mergedNamespace); + if (!isset($groupedByNamespace[$targetNamespace])) { + continue; + } + $xmlNamespace = $groupedByNamespace[$targetNamespace][0]['xmlNamespace']; + + assertType('string', $xmlNamespace); + assertType('non-empty-list&hasOffsetValue(1, string)', $mergedNamespace); + + $xsdFiles[$xmlNamespace] = []; + foreach ($mergedNamespace as $namespace) { + foreach ($groupedByNamespace[$namespace] ?? [] as $viewHelper) { + $xsdFiles[$xmlNamespace][$viewHelper['name']] = $viewHelper; + } + } + // After assigning with string keys ($viewHelper['name']), $xsdFiles[$xmlNamespace] should NOT be a list + assertType('array|string, array{xmlNamespace: string, namespace: string, name: string}>', $xsdFiles[$xmlNamespace]); + $xsdFiles[$xmlNamespace] = array_values($xsdFiles[$xmlNamespace]); + } +}