Skip to content

Commit c1aeb64

Browse files
RobertMeondrejmirtes
authored andcommitted
Do not generalize big array when combined with empty array
Fixes phpstan/phpstan#10834
1 parent e74cd7c commit c1aeb64

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

src/Type/TypeCombinator.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -664,12 +664,19 @@ private static function processArrayTypes(array $arrayTypes): array
664664
$valueTypesForGeneralArray = [];
665665
$generalArrayOccurred = false;
666666
$constantKeyTypesNumbered = [];
667+
$filledArrays = 0;
668+
$overflowed = false;
667669

668670
/** @var int|float $nextConstantKeyTypeIndex */
669671
$nextConstantKeyTypeIndex = 1;
670672

671673
foreach ($arrayTypes as $arrayType) {
672-
if ($generalArrayOccurred || !$arrayType->isConstantArray()->yes()) {
674+
$isConstantArray = $arrayType->isConstantArray()->yes();
675+
if (!$isConstantArray || !$arrayType->isIterableAtLeastOnce()->no()) {
676+
$filledArrays++;
677+
}
678+
679+
if ($generalArrayOccurred || !$isConstantArray) {
673680
foreach ($arrayType->getArrays() as $type) {
674681
$keyTypesForGeneralArray[] = $type->getIterableKeyType();
675682
$valueTypesForGeneralArray[] = $type->getItemType();
@@ -693,13 +700,14 @@ private static function processArrayTypes(array $arrayTypes): array
693700
$nextConstantKeyTypeIndex *= 2;
694701
if (!is_int($nextConstantKeyTypeIndex)) {
695702
$generalArrayOccurred = true;
703+
$overflowed = true;
696704
continue 2;
697705
}
698706
}
699707
}
700708
}
701709

702-
if ($generalArrayOccurred) {
710+
if ($generalArrayOccurred && (!$overflowed || $filledArrays > 1)) {
703711
$scopes = [];
704712
$useTemplateArray = true;
705713
foreach ($arrayTypes as $arrayType) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1461,6 +1461,7 @@ public function dataFileAsserts(): iterable
14611461
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10468.php');
14621462
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6613.php');
14631463
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10187.php');
1464+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10834.php');
14641465
}
14651466

14661467
/**
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @return array{1: string, 2: int, 3: bool, 4: string, 5: int, 6: bool, 7: string, 8: int, 9: bool, 10: string, 11: int, 12: bool, 13: string, 14: int, 15: bool, 16: string, 17: int, 18: bool, 19: string, 20: int, 21: bool, 22: string, 23: int, 24: bool, 25: string, 26: int, 27: bool, 28: string, 29: int, 30: bool, 31: string, 32: int, 33: bool, 34: string, 35: int, 36: bool, 37: string, 38: int, 39: bool, 40: string, 41: int, 42: bool, 43: string, 44: int, 45: bool, 46: string, 47: int, 48: bool, 49: string, 50: int, 51: bool, 52: string, 53: int, 54: bool, 55: string, 56: int, 57: bool, 58: string, 59: int, 60: bool, 61: string, 62: int, 63: bool, 64: float}|null
9+
*/
10+
function returnArrayOrNull()
11+
{
12+
return null;
13+
}
14+
15+
function test(): void {
16+
$arrayOrNull = returnArrayOrNull();
17+
18+
assertType('array{1: string, 2: int, 3: bool, 4: string, 5: int, 6: bool, 7: string, 8: int, 9: bool, 10: string, 11: int, 12: bool, 13: string, 14: int, 15: bool, 16: string, 17: int, 18: bool, 19: string, 20: int, 21: bool, 22: string, 23: int, 24: bool, 25: string, 26: int, 27: bool, 28: string, 29: int, 30: bool, 31: string, 32: int, 33: bool, 34: string, 35: int, 36: bool, 37: string, 38: int, 39: bool, 40: string, 41: int, 42: bool, 43: string, 44: int, 45: bool, 46: string, 47: int, 48: bool, 49: string, 50: int, 51: bool, 52: string, 53: int, 54: bool, 55: string, 56: int, 57: bool, 58: string, 59: int, 60: bool, 61: string, 62: int, 63: bool, 64: float}|null', $arrayOrNull);
19+
assertType('array{}|array{1: string, 2: int, 3: bool, 4: string, 5: int, 6: bool, 7: string, 8: int, 9: bool, 10: string, 11: int, 12: bool, 13: string, 14: int, 15: bool, 16: string, 17: int, 18: bool, 19: string, 20: int, 21: bool, 22: string, 23: int, 24: bool, 25: string, 26: int, 27: bool, 28: string, 29: int, 30: bool, 31: string, 32: int, 33: bool, 34: string, 35: int, 36: bool, 37: string, 38: int, 39: bool, 40: string, 41: int, 42: bool, 43: string, 44: int, 45: bool, 46: string, 47: int, 48: bool, 49: string, 50: int, 51: bool, 52: string, 53: int, 54: bool, 55: string, 56: int, 57: bool, 58: string, 59: int, 60: bool, 61: string, 62: int, 63: bool, 64: float}', $arrayOrNull ?? []);
20+
assertType('array{}|array{1: string, 2: int, 3: bool, 4: string, 5: int, 6: bool, 7: string, 8: int, 9: bool, 10: string, 11: int, 12: bool, 13: string, 14: int, 15: bool, 16: string, 17: int, 18: bool, 19: string, 20: int, 21: bool, 22: string, 23: int, 24: bool, 25: string, 26: int, 27: bool, 28: string, 29: int, 30: bool, 31: string, 32: int, 33: bool, 34: string, 35: int, 36: bool, 37: string, 38: int, 39: bool, 40: string, 41: int, 42: bool, 43: string, 44: int, 45: bool, 46: string, 47: int, 48: bool, 49: string, 50: int, 51: bool, 52: string, 53: int, 54: bool, 55: string, 56: int, 57: bool, 58: string, 59: int, 60: bool, 61: string, 62: int, 63: bool, 64: float}|null', rand() ? $arrayOrNull : []);
21+
}

0 commit comments

Comments
 (0)