diff --git a/src/Rules/Arrays/AllowedArrayKeysTypes.php b/src/Rules/Arrays/AllowedArrayKeysTypes.php index 2178b579fb..7d920e167c 100644 --- a/src/Rules/Arrays/AllowedArrayKeysTypes.php +++ b/src/Rules/Arrays/AllowedArrayKeysTypes.php @@ -71,6 +71,11 @@ public static function narrowOffsetKeyType(Type $varType, Type $keyType): ?Type return $narrowedKey; } elseif ($varIterableKeyType->isInteger()->yes() && $keyType->isString()->yes()) { return TypeCombinator::intersect($varIterableKeyType->toString(), $keyType); + } elseif ($varType->isList()->yes()) { + return TypeCombinator::intersect( + $varIterableKeyType, + $keyType, + ); } return new MixedType( diff --git a/tests/PHPStan/Analyser/nsrt/bug-12274.php b/tests/PHPStan/Analyser/nsrt/bug-12274.php index f0536a0c15..b17b11aed8 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-12274.php +++ b/tests/PHPStan/Analyser/nsrt/bug-12274.php @@ -92,9 +92,9 @@ function testShouldLooseListbyAst(array $list, int $i): void assertType('list', $list); $list[1+$i] = 21; - assertType('non-empty-array', $list); + assertType('non-empty-array, int>', $list); } - assertType('array', $list); + assertType('array, int>', $list); } /** @param list $list */ @@ -103,7 +103,7 @@ function testShouldLooseListbyAst2(array $list, int $i): void if (isset($list[$i])) { assertType('list', $list); $list[2+$i] = 21; - assertType('non-empty-array', $list); + assertType('non-empty-array, int>', $list); } - assertType('array', $list); + assertType('array, int>', $list); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-12933.php b/tests/PHPStan/Analyser/nsrt/bug-12933.php new file mode 100644 index 0000000000..bc57d3c57d --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-12933.php @@ -0,0 +1,48 @@ += 8.0 + +namespace Bug12933; + +use function PHPStan\Testing\assertType; + +/** + * @phpstan-type record array{id: positive-int, name: string} + */ +class Collection +{ + /** @param list $list */ + public function __construct( + public array $list + ) + { + } + + public function updateNameIsset(int $index, string $name): void + { + assert(isset($this->list[$index])); + assertType('int<0, max>', $index); + } + + public function updateNameArrayKeyExists(int $index, string $name): void + { + assert(array_key_exists($index, $this->list)); + assertType('int<0, max>', $index); + } + + /** + * @param int<-5, 5> $index + */ + public function issetNarrowsIntRange(int $index, string $name): void + { + assert(isset($this->list[$index])); + assertType('int<0, 5>', $index); + } + + /** + * @param int<5, 15> $index + */ + public function issetNotWidensIntRange(int $index, string $name): void + { + assert(isset($this->list[$index])); + assertType('int<5, 15>', $index); + } +}