Skip to content

Commit

Permalink
Merge branch refs/heads/1.11.x into 1.12.x
Browse files Browse the repository at this point in the history
  • Loading branch information
phpstan-bot authored Aug 22, 2024
2 parents c5e0443 + 8f19e31 commit b04b3bf
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 14 deletions.
52 changes: 46 additions & 6 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,11 +246,16 @@ public function specifyTypesInCondition(
) {
$argType = $scope->getType($expr->right->getArgs()[0]->value);

if ($argType instanceof UnionType && $leftType instanceof ConstantIntegerType) {
if ($orEqual) {
$sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo($leftType->getValue());
} else {
$sizeType = IntegerRangeType::createAllGreaterThan($leftType->getValue());
if ($argType instanceof UnionType) {
$sizeType = null;
if ($leftType instanceof ConstantIntegerType) {
if ($orEqual) {
$sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo($leftType->getValue());
} else {
$sizeType = IntegerRangeType::createAllGreaterThan($leftType->getValue());
}
} elseif ($leftType instanceof IntegerRangeType) {
$sizeType = $leftType;
}

$narrowed = $this->narrowUnionByArraySize($expr->right, $argType, $sizeType, $context, $scope, $rootExpr);
Expand Down Expand Up @@ -943,8 +948,12 @@ public function specifyTypesInCondition(
return new SpecifiedTypes([], [], false, [], $rootExpr);
}

private function narrowUnionByArraySize(FuncCall $countFuncCall, UnionType $argType, Type $sizeType, TypeSpecifierContext $context, Scope $scope, ?Expr $rootExpr): ?SpecifiedTypes
private function narrowUnionByArraySize(FuncCall $countFuncCall, UnionType $argType, ?Type $sizeType, TypeSpecifierContext $context, Scope $scope, ?Expr $rootExpr): ?SpecifiedTypes
{
if ($sizeType === null) {
return null;
}

if (count($countFuncCall->getArgs()) === 1) {
$isNormalCount = TrinaryLogic::createYes();
} else {
Expand Down Expand Up @@ -1011,6 +1020,37 @@ private function turnListIntoConstantArray(FuncCall $countFuncCall, Type $type,
return $valueTypesBuilder->getArray();
}

if (
$isNormalCount->yes()
&& $type->isList()->yes()
&& $sizeType instanceof IntegerRangeType
&& $sizeType->getMin() !== null
) {
// turn optional offsets non-optional
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
for ($i = 0; $i < $sizeType->getMin(); $i++) {
$offsetType = new ConstantIntegerType($i);
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
}
if ($sizeType->getMax() !== null) {
for ($i = $sizeType->getMin(); $i < $sizeType->getMax(); $i++) {
$offsetType = new ConstantIntegerType($i);
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), true);
}
} else {
for ($i = $sizeType->getMin();; $i++) {
$offsetType = new ConstantIntegerType($i);
$hasOffset = $type->hasOffsetValueType($offsetType);
if ($hasOffset->no()) {
break;
}
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), !$hasOffset->yes());
}

}
return $valueTypesBuilder->getArray();
}

return null;
}

Expand Down
10 changes: 5 additions & 5 deletions tests/PHPStan/Analyser/nsrt/bug-4700.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ function(array $array, int $count): void {
assertType('int<1, 5>', count($a));
assertType('array{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a);
} else {
assertType('int<0, 5>', count($a));
assertType('array{}|array{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a);
assertType('0', count($a));
assertType('array{}', $a);
}
};

Expand All @@ -40,10 +40,10 @@ function(array $array, int $count): void {
if (isset($array['d'])) $a[] = $array['d'];
if (isset($array['e'])) $a[] = $array['e'];
if (count($a) > $count) {
assertType('int<2, 5>', count($a));
assertType('int<1, 5>', count($a));
assertType('array{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a);
} else {
assertType('int<0, 5>', count($a));
assertType('array{}|array{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a);
assertType('0', count($a));
assertType('array{}', $a);
}
};
6 changes: 3 additions & 3 deletions tests/PHPStan/Analyser/nsrt/bug11480.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function arrayGreatherThan(): void
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);

if (count($x) > 1) {
assertType("array{0: 'ab', 1?: 'xy'}", $x);
assertType("array{'ab', 'xy'}", $x);
} else {
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
}
Expand Down Expand Up @@ -58,7 +58,7 @@ public function arraySmallerThan(): void
if (count($x) <= 1) {
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
} else {
assertType("array{0: 'ab', 1?: 'xy'}", $x);
assertType("array{'ab', 'xy'}", $x);
}
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
}
Expand Down Expand Up @@ -106,7 +106,7 @@ public function intRangeCount($count): void
if (count($x) >= $count) {
assertType("array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
} else {
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
assertType("array{}", $x);
}
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
}
Expand Down
47 changes: 47 additions & 0 deletions tests/PHPStan/Analyser/nsrt/list-count.php
Original file line number Diff line number Diff line change
Expand Up @@ -339,4 +339,51 @@ protected function testOptionalKeysInUnionArray($row): void
}
}

/**
* @param array{string}|list{0: int, 1?: string|null, 2?: int|null, 3?: float|null} $row
* @param int<2, 3> $twoOrThree
* @param int<2, max> $twoOrMore
* @param int<min, 3> $maxThree
* @param int<10, 11> $tenOrEleven
*/
protected function testOptionalKeysInUnionListWithIntRange($row, $twoOrThree, $twoOrMore, int $maxThree, $tenOrEleven): void
{
if (count($row) >= $twoOrThree) {
assertType('array{0: int, 1: string|null, 2?: int|null}', $row);
} else {
assertType('(array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}&list)|array{string}', $row);
}

if (count($row) >= $tenOrEleven) {
assertType('*NEVER*', $row);
} else {
assertType('(array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}&list)|array{string}', $row);
}

if (count($row) >= $twoOrMore) {
assertType('array{0: int, 1: string|null, 2?: int|null, 3?: float|null}&list', $row);
} else {
assertType('(array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}&list)|array{string}', $row);
}

if (count($row) >= $maxThree) {
assertType('(array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}&list)|array{string}', $row);
} else {
assertType('array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}&list', $row);
}
}

/**
* @param array{string}|array{0: int, 1?: string|null, 2?: int|null, 3?: float|null} $row
* @param int<2, 3> $twoOrThree
*/
protected function testOptionalKeysInUnionArrayWithIntRange($row, $twoOrThree): void
{
// doesn't narrow because no list
if (count($row) >= $twoOrThree) {
assertType('array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}', $row);
} else {
assertType('array{0: int, 1?: string|null, 2?: int|null, 3?: float|null}|array{string}', $row);
}
}
}

0 comments on commit b04b3bf

Please sign in to comment.