diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index f6550a5845..13e7fafc52 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -410,6 +410,24 @@ public function setExistingOffsetValueType(Type $offsetType, Type $valueType): T } } + if ( + $this->itemType->isArray()->yes() + && $valueType->isArray()->yes() + && $this->itemType->getIterableValueType()->isConstantArray()->yes() + && $valueType->getIterableValueType()->isConstantArray()->yes() + ) { + $newItemType = $this->itemType->setExistingOffsetValueType( + $valueType->getIterableKeyType(), + $valueType->getIterableValueType(), + ); + if ($newItemType !== $this->itemType) { + return new self( + $this->keyType, + $newItemType, + ); + } + } + return new self( $this->keyType, TypeCombinator::union($this->itemType, $valueType), diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index cf56a34adc..4a8a3d14a6 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -991,7 +991,7 @@ public function testBug7581(): void public function testBug7903(): void { $errors = $this->runAnalyse(__DIR__ . '/data/bug-7903.php'); - $this->assertCount(24, $errors); + $this->assertCount(23, $errors); } public function testBug7901(): void diff --git a/tests/PHPStan/Analyser/nsrt/bug-13637.php b/tests/PHPStan/Analyser/nsrt/bug-13637.php new file mode 100644 index 0000000000..4b9eed835d --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13637.php @@ -0,0 +1,43 @@ +>> + */ +function doesNotWork() : array { + $final = []; + + for ($i = 0; $i < 5; $i++) { + $j = $i * 2; + $k = $j + 1; + $final[$i][$j][$k]['abc'] = $i; + $final[$i][$j][$k]['def'] = $i; + $final[$i][$j][$k]['ghi'] = $i; + } + + assertType("non-empty-array, non-empty-array, non-empty-array, array{abc: int<0, 4>, def: int<0, 4>, ghi: int<0, 4>}>>>", $final); + + return $final; +} + +/** + * @return array> + */ +function thisWorks() : array { + $final = []; + + for ($i = 0; $i < 5; $i++) { + $j = $i * 2; + $k = $j + 1; + $final[$i][$j]['abc'] = $i; + $final[$i][$j]['def'] = $i; + $final[$i][$j]['ghi'] = $i; + } + + assertType("non-empty-array, non-empty-array, array{abc: int<0, 4>, def: int<0, 4>, ghi: int<0, 4>}>>", $final); + + return $final; +}