Skip to content

Commit e8244d9

Browse files
committed
More precise array_key_first/array_key_last inference
1 parent aad71ca commit e8244d9

2 files changed

Lines changed: 39 additions & 85 deletions

File tree

src/Analyser/TypeSpecifier.php

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,46 @@ public function specifyTypesInCondition(
725725

726726
if ($context->null()) {
727727
$specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->expr, $context)->setRootExpr($expr);
728+
} else {
729+
$specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context)->setRootExpr($expr);
730+
}
731+
732+
// infer $arr[$key] after $key = array_key_first/last($arr)
733+
if (
734+
$expr->expr instanceof FuncCall
735+
&& $expr->expr->name instanceof Name
736+
&& in_array($expr->expr->name->toLowerString(), ['array_key_first', 'array_key_last'], true)
737+
&& count($expr->expr->getArgs()) >= 1
738+
) {
739+
$arrayArg = $expr->expr->getArgs()[0]->value;
740+
$arrayType = $scope->getType($arrayArg);
741+
742+
if ($arrayType->isArray()->yes()) {
743+
if ($context->true()) {
744+
$specifiedTypes = $specifiedTypes->unionWith(
745+
$this->create($arrayArg, new NonEmptyArrayType(), TypeSpecifierContext::createTrue(), $scope),
746+
);
747+
$isNonEmpty = true;
748+
} else {
749+
$isNonEmpty = $arrayType->isIterableAtLeastOnce()->yes();
750+
}
728751

752+
if (
753+
$isNonEmpty
754+
) {
755+
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
756+
$iterableValueType = $expr->expr->name->toLowerString() === 'array_key_first'
757+
? $arrayType->getIterableValueType()
758+
: $arrayType->getIterableValueType();
759+
760+
$specifiedTypes = $specifiedTypes->unionWith(
761+
$this->create($dimFetch, $iterableValueType, TypeSpecifierContext::createTrue(), $scope),
762+
);
763+
}
764+
}
765+
}
766+
767+
if ($context->null()) {
729768
// infer $arr[$key] after $key = array_rand($arr)
730769
if (
731770
$expr->expr instanceof FuncCall
@@ -755,30 +794,6 @@ public function specifyTypesInCondition(
755794
}
756795
}
757796

758-
// infer $arr[$key] after $key = array_key_first/last($arr)
759-
if (
760-
$expr->expr instanceof FuncCall
761-
&& $expr->expr->name instanceof Name
762-
&& in_array($expr->expr->name->toLowerString(), ['array_key_first', 'array_key_last'], true)
763-
&& count($expr->expr->getArgs()) >= 1
764-
) {
765-
$arrayArg = $expr->expr->getArgs()[0]->value;
766-
$arrayType = $scope->getType($arrayArg);
767-
if (
768-
$arrayType->isArray()->yes()
769-
&& $arrayType->isIterableAtLeastOnce()->yes()
770-
) {
771-
$dimFetch = new ArrayDimFetch($arrayArg, $expr->var);
772-
$iterableValueType = $expr->expr->name->toLowerString() === 'array_key_first'
773-
? $arrayType->getIterableValueType()
774-
: $arrayType->getIterableValueType();
775-
776-
return $specifiedTypes->unionWith(
777-
$this->create($dimFetch, $iterableValueType, TypeSpecifierContext::createTrue(), $scope),
778-
);
779-
}
780-
}
781-
782797
// infer $list[$count] after $count = count($list) - 1
783798
if (
784799
$expr->expr instanceof Expr\BinaryOp\Minus
@@ -806,8 +821,6 @@ public function specifyTypesInCondition(
806821
return $specifiedTypes;
807822
}
808823

809-
$specifiedTypes = $this->specifyTypesInCondition($scope->exitFirstLevelStatements(), $expr->var, $context)->setRootExpr($expr);
810-
811824
if ($context->true()) {
812825
// infer $arr[$key] after $key = array_search($needle, $arr)
813826
if (

src/Type/Php/ArrayKeyFirstLastTypeSpecifyingExtension.php

Lines changed: 0 additions & 59 deletions
This file was deleted.

0 commit comments

Comments
 (0)