diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 666b26e2b73..f4335c7dc0f 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -4545,11 +4545,14 @@ private function inferForLoopExpressions(For_ $stmt, Expr $lastCondExpr, Mutatin && $stmt->init[0]->var->name === $lastCondExpr->left->name ) { $arrayArg = $lastCondExpr->right->getArgs()[0]->value; - $bodyScope = $bodyScope->assignExpression( - new ArrayDimFetch($lastCondExpr->right->getArgs()[0]->value, $lastCondExpr->left), - $bodyScope->getType($arrayArg)->getIterableValueType(), - $bodyScope->getNativeType($arrayArg)->getIterableValueType(), - ); + $arrayType = $bodyScope->getType($arrayArg); + if ($arrayType->isList()->yes()) { + $bodyScope = $bodyScope->assignExpression( + new ArrayDimFetch($lastCondExpr->right->getArgs()[0]->value, $lastCondExpr->left), + $arrayType->getIterableValueType(), + $bodyScope->getNativeType($arrayArg)->getIterableValueType(), + ); + } } // count($items) > $i @@ -4566,11 +4569,14 @@ private function inferForLoopExpressions(For_ $stmt, Expr $lastCondExpr, Mutatin && $stmt->init[0]->var->name === $lastCondExpr->right->name ) { $arrayArg = $lastCondExpr->left->getArgs()[0]->value; - $bodyScope = $bodyScope->assignExpression( - new ArrayDimFetch($lastCondExpr->left->getArgs()[0]->value, $lastCondExpr->right), - $bodyScope->getType($arrayArg)->getIterableValueType(), - $bodyScope->getNativeType($arrayArg)->getIterableValueType(), - ); + $arrayType = $bodyScope->getType($arrayArg); + if ($arrayType->isList()->yes()) { + $bodyScope = $bodyScope->assignExpression( + new ArrayDimFetch($lastCondExpr->left->getArgs()[0]->value, $lastCondExpr->right), + $arrayType->getIterableValueType(), + $bodyScope->getNativeType($arrayArg)->getIterableValueType(), + ); + } } } diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 7649e1dffe6..23617d1cd41 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1240,4 +1240,20 @@ public function testBug14234(): void ]); } + public function testBug13773(): void + { + $this->reportPossiblyNonexistentGeneralArrayOffset = true; + + $this->analyse([__DIR__ . '/data/bug-13773.php'], [ + [ + 'Offset int<0, max> might not exist on non-empty-array.', + 14, + ], + [ + 'Offset int<0, max> might not exist on non-empty-array.', + 38, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Arrays/data/bug-13773.php b/tests/PHPStan/Rules/Arrays/data/bug-13773.php new file mode 100644 index 00000000000..f37ca9f8381 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-13773.php @@ -0,0 +1,40 @@ + */ +function getArray(): array { + return [100 => "hey"]; +} + +function testNonListArray(): void +{ + $array = getArray(); + for ($i = 0; $i < count($array); $i++) { + $a = $array[$i]; + } +} + +/** @param list $list */ +function testList(array $list): void +{ + for ($i = 0; $i < count($list); $i++) { + $a = $list[$i]; + } +} + +/** @param list $list */ +function testListReversed(array $list): void +{ + for ($i = 0; count($list) > $i; ++$i) { + $a = $list[$i]; + } +} + +/** @param array $array */ +function testNonListReversed(array $array): void +{ + for ($i = 0; count($array) > $i; ++$i) { + $a = $array[$i]; + } +}