diff --git a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php index 04b47475610..94feb24cac0 100644 --- a/src/Rules/Comparison/ImpossibleCheckTypeHelper.php +++ b/src/Rules/Comparison/ImpossibleCheckTypeHelper.php @@ -22,7 +22,6 @@ use PHPStan\Type\TypeUtils; use PHPStan\Type\TypeWithClassName; use PHPStan\Type\VerbosityLevel; -use function array_column; use function array_map; use function array_pop; use function count; @@ -182,6 +181,21 @@ public function findSpecifiedType( $sureTypes = $specifiedTypes->getSureTypes(); $sureNotTypes = $specifiedTypes->getSureNotTypes(); + return $this->findSpecifiedSureAndSureNotTypes($scope, $node, $sureTypes, $sureNotTypes); + } + + /** + * @param array $sureTypes + * @param array $sureNotTypes + * @return void + */ + private function findSpecifiedSureAndSureNotTypes( + Scope $scope, + Expr $node, + array $sureTypes, + array $sureNotTypes, + ) + { $isSpecified = static function (Expr $expr) use ($scope, $node): bool { if ($expr === $node) { return true; @@ -252,11 +266,20 @@ public function findSpecifiedType( return null; } } - $types = TypeCombinator::union( - ...array_column($sureTypes, 1), - ); - if ($types instanceof NeverType) { - return false; + + foreach ($sureTypes as $sureType) { + $types = $this->sureUnionTypes($sureTypes, $sureType[0]); + + if (count($types) === 1 && count($sureNotTypes) === 0) { + $findTypes = $this->findSpecifiedSureAndSureNotTypes($scope, $node, [$sureType], $sureNotTypes); + if ($findTypes !== null) { + return $findTypes; + } + } + + if (TypeCombinator::union(...$types) instanceof NeverType) { + return false; + } } } @@ -266,17 +289,40 @@ public function findSpecifiedType( return null; } } - $types = TypeCombinator::union( - ...array_column($sureNotTypes, 1), - ); - if ($types instanceof NeverType) { - return true; + + foreach ($sureNotTypes as $sureNotType) { + $types = $this->sureUnionTypes($sureNotTypes, $sureNotType[0]); + + if (count($types) === 1 && count($sureTypes) === 0) { + $findTypes = $this->findSpecifiedSureAndSureNotTypes($scope, $node, $sureTypes, [$sureNotType]); + if ($findTypes !== null) { + return $findTypes; + } + } + + if (TypeCombinator::union(...$types) instanceof NeverType) { + return true; + } } } return null; } + private function sureUnionTypes(array $sureOrSureNotTypes, Expr $expr): array + { + $types = []; + foreach ($sureOrSureNotTypes as [$sureExpr, $type]) { + if ($sureExpr !== $expr) { + continue; + } + + $types[] = $type; + } + + return $types; + } + /** * @param Node\Arg[] $args */ diff --git a/src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php b/src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php index 456fd602dbb..b73d877197a 100644 --- a/src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php +++ b/src/Type/Php/InArrayFunctionTypeSpecifyingExtension.php @@ -10,11 +10,10 @@ use PHPStan\Analyser\TypeSpecifierContext; use PHPStan\Reflection\FunctionReflection; use PHPStan\Type\Accessory\NonEmptyArrayType; -use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\FunctionTypeSpecifyingExtension; +use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; -use PHPStan\Type\UnionType; use function count; use function strtolower; @@ -48,19 +47,6 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n $arrayValueType = $arrayType->getIterableValueType(); $specifiedTypes = new SpecifiedTypes([], []); - if ($context->true() - && $arrayType->isArray()->yes() - && !$arrayType->isIterableAtLeastOnce()->yes() - && !($arrayType instanceof ConstantArrayType && $arrayType->isEmpty()) - && !$arrayType instanceof UnionType) { - $specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create( - $node->getArgs()[1]->value, - new NonEmptyArrayType(), - $context, - false, - $scope, - )); - } if ( $context->truthy() @@ -75,6 +61,17 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n )); } + if ($context->true() + && $arrayType->isArray()->yes()) { + $specifiedTypes = $specifiedTypes->unionWith($this->typeSpecifier->create( + $node->getArgs()[1]->value, + TypeCombinator::intersect($arrayType, new NonEmptyArrayType()), + $context, + false, + $scope, + )); + } + return $specifiedTypes; }