diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 9f7a86eb4a..c78150ac26 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -2470,7 +2470,7 @@ private function createForExpr( ) { if ( ($context->true() && $type->isSuperTypeOf($scope->getType($expr->right))->no()) - || ($context->false() && $type->isSuperTypeOf($scope->getType($expr->right))->yes()) + || ($context->false() && ($type->isSuperTypeOf($scope->getType($expr->right))->yes() || $type->isNull()->no())) ) { $expr = $expr->left; } @@ -2586,6 +2586,42 @@ private function createForExpr( } } + if ( + $expr instanceof ArrayDimFetch + && $expr->dim !== null + && !$context->null() + ) { + $dimType = $scope->getType($expr->dim); + if ($dimType instanceof ConstantIntegerType || $dimType->getConstantStrings() !== []) { + $varType = $scope->getType($expr->var); + $constantArrays = $varType->getConstantArrays(); + if ($constantArrays !== []) { + $typesToRemove = []; + foreach ($constantArrays as $constantArray) { + if (!$constantArray->hasOffsetValueType($dimType)->yes()) { + continue; + } + $offsetValueType = $constantArray->getOffsetValueType($dimType); + if ($context->false()) { + if ($type->isSuperTypeOf($offsetValueType)->yes()) { + $typesToRemove[] = $constantArray; + } + } elseif ($context->true()) { + if ($type->isSuperTypeOf($offsetValueType)->no()) { + $typesToRemove[] = $constantArray; + } + } + } + + if ($typesToRemove !== [] && count($typesToRemove) < count($constantArrays)) { + $typeToRemove = TypeCombinator::union(...$typesToRemove); + $varExprString = $this->exprPrinter->printExpr($expr->var); + $sureNotTypes[$varExprString] = [$expr->var, $typeToRemove]; + } + } + } + } + $types = new SpecifiedTypes($sureTypes, $sureNotTypes); if (isset($containsNull) && !$containsNull) { return $this->createNullsafeTypes($originalExpr, $scope, $context, $type)->unionWith($types); diff --git a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php index 4ee0a096b6..430eecb245 100644 --- a/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/OffsetAccessAssignmentRuleTest.php @@ -222,4 +222,16 @@ public function testAppendToArrayWithPhpIntMaxKey(): void ]); } + public function testBug9004(): void + { + $this->checkUnionTypes = true; + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-9004.php'], []); + } + + public function testBug14566(): void + { + $this->checkUnionTypes = true; + $this->analyse([__DIR__ . '/data/bug-14566.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Arrays/data/bug-14566.php b/tests/PHPStan/Rules/Arrays/data/bug-14566.php new file mode 100644 index 0000000000..66bcb8c13e --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-14566.php @@ -0,0 +1,46 @@ +