Skip to content

Commit c0e6269

Browse files
committed
Clean up a bit more aggressively
1 parent 5bda659 commit c0e6269

File tree

2 files changed

+81
-107
lines changed

2 files changed

+81
-107
lines changed

src/Analyser/TypeSpecifier.php

+80-106
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,6 @@ public function specifyTypesInCondition(
269269
) {
270270
$argType = $scope->getType($expr->right->getArgs()[0]->value);
271271

272-
$sizeType = null;
273272
if ($leftType instanceof ConstantIntegerType) {
274273
if ($orEqual) {
275274
$sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo($leftType->getValue());
@@ -278,6 +277,8 @@ public function specifyTypesInCondition(
278277
}
279278
} elseif ($leftType instanceof IntegerRangeType) {
280279
$sizeType = $leftType->shift($offset);
280+
} else {
281+
$sizeType = $leftType;
281282
}
282283

283284
$specifiedTypes = $this->specifyTypesForCountFuncCall($expr->right, $argType, $sizeType, $context, $scope, $expr);
@@ -967,103 +968,91 @@ public function specifyTypesInCondition(
967968
return (new SpecifiedTypes([], []))->setRootExpr($expr);
968969
}
969970

970-
private function specifyTypesForCountFuncCall(FuncCall $countFuncCall, Type $type, ?Type $sizeType, TypeSpecifierContext $context, Scope $scope, ?Expr $rootExpr): ?SpecifiedTypes
971+
private function specifyTypesForCountFuncCall(
972+
FuncCall $countFuncCall,
973+
Type $type,
974+
Type $sizeType,
975+
TypeSpecifierContext $context,
976+
Scope $scope,
977+
Expr $rootExpr,
978+
): ?SpecifiedTypes
971979
{
972-
if ($sizeType === null) {
973-
return null;
980+
if (count($countFuncCall->getArgs()) === 1) {
981+
$isNormalCount = TrinaryLogic::createYes();
982+
} else {
983+
$mode = $scope->getType($countFuncCall->getArgs()[1]->value);
984+
$isNormalCount = (new ConstantIntegerType(COUNT_NORMAL))->isSuperTypeOf($mode)->result->or($type->getIterableValueType()->isArray()->negate());
974985
}
975986

976-
if (
977-
$this->isFuncCallWithNormalCount($countFuncCall, $scope)->yes()
978-
&& $type->isConstantArray()->yes()
979-
) {
980-
$resultType = TypeTraverser::map($type, function (Type $type, callable $traverse) use ($sizeType, $context) {
981-
if ($type instanceof UnionType) {
982-
return $traverse($type);
983-
}
984-
985-
$arraySize = $type->getArraySize();
986-
$isSize = $sizeType->isSuperTypeOf($arraySize);
987-
if ($context->truthy() && $isSize->no()) {
988-
return new NeverType();
989-
}
990-
if ($context->falsey() && !$isSize->yes()) {
991-
return new NeverType();
992-
}
993-
994-
return $this->turnListIntoConstantArray($type, $sizeType) ?? $type;
995-
});
996-
997-
return $this->create($countFuncCall->getArgs()[0]->value, $resultType, $context, $scope)->setRootExpr($rootExpr);
987+
if (!$isNormalCount->yes() || (!$type->isConstantArray()->yes() && !$type->isList()->yes())) {
988+
return null;
998989
}
999990

1000-
return null;
1001-
}
991+
$resultType = TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($sizeType, $context) {
992+
if ($type instanceof UnionType) {
993+
return $traverse($type);
994+
}
1002995

1003-
private function turnListIntoConstantArray(Type $type, Type $sizeType): ?Type
1004-
{
1005-
if (
1006-
$type->isList()->yes()
1007-
&& $sizeType instanceof ConstantIntegerType
1008-
&& $sizeType->getValue() < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1009-
) {
1010-
// turn optional offsets non-optional
1011-
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1012-
for ($i = 0; $i < $sizeType->getValue(); $i++) {
1013-
$offsetType = new ConstantIntegerType($i);
1014-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
996+
$isSizeSuperTypeOfArraySize = $sizeType->isSuperTypeOf($type->getArraySize());
997+
if ($context->truthy() && $isSizeSuperTypeOfArraySize->no()) {
998+
return new NeverType();
999+
}
1000+
if ($context->falsey() && !$isSizeSuperTypeOfArraySize->yes()) {
1001+
return new NeverType();
10151002
}
1016-
return $valueTypesBuilder->getArray();
1017-
}
10181003

1019-
if (
1020-
$type->isList()->yes()
1021-
&& $sizeType instanceof IntegerRangeType
1022-
&& $sizeType->getMin() !== null
1023-
) {
1024-
// turn optional offsets non-optional
1025-
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1026-
for ($i = 0; $i < $sizeType->getMin(); $i++) {
1027-
$offsetType = new ConstantIntegerType($i);
1028-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
1029-
}
1030-
if ($sizeType->getMax() !== null) {
1031-
for ($i = $sizeType->getMin(); $i < $sizeType->getMax(); $i++) {
1032-
$offsetType = new ConstantIntegerType($i);
1033-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), true);
1034-
}
1035-
} elseif ($type->isConstantArray()->yes()) {
1036-
for ($i = $sizeType->getMin();; $i++) {
1037-
$offsetType = new ConstantIntegerType($i);
1038-
$hasOffset = $type->hasOffsetValueType($offsetType);
1039-
if ($hasOffset->no()) {
1040-
break;
1004+
if ($type->isList()->yes()) {
1005+
if (
1006+
$sizeType instanceof ConstantIntegerType
1007+
&& $sizeType->getValue() < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1008+
) {
1009+
// turn optional offsets non-optional
1010+
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1011+
for ($i = 0; $i < $sizeType->getValue(); $i++) {
1012+
$offsetType = new ConstantIntegerType($i);
1013+
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
10411014
}
1042-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), !$hasOffset->yes());
1015+
return $valueTypesBuilder->getArray();
10431016
}
1044-
} else {
1045-
return null;
1046-
}
10471017

1048-
$arrayType = $valueTypesBuilder->getArray();
1049-
if ($arrayType->isIterableAtLeastOnce()->yes()) {
1050-
return $arrayType;
1051-
}
1052-
}
1018+
if (
1019+
$sizeType instanceof IntegerRangeType
1020+
&& $sizeType->getMin() !== null
1021+
) {
1022+
// turn optional offsets non-optional
1023+
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1024+
for ($i = 0; $i < $sizeType->getMin(); $i++) {
1025+
$offsetType = new ConstantIntegerType($i);
1026+
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
1027+
}
1028+
if ($sizeType->getMax() !== null) {
1029+
for ($i = $sizeType->getMin(); $i < $sizeType->getMax(); $i++) {
1030+
$offsetType = new ConstantIntegerType($i);
1031+
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), true);
1032+
}
1033+
} elseif ($type->isConstantArray()->yes()) {
1034+
for ($i = $sizeType->getMin();; $i++) {
1035+
$offsetType = new ConstantIntegerType($i);
1036+
$hasOffset = $type->hasOffsetValueType($offsetType);
1037+
if ($hasOffset->no()) {
1038+
break;
1039+
}
1040+
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), !$hasOffset->yes());
1041+
}
1042+
} else {
1043+
return TypeCombinator::intersect($type, new NonEmptyArrayType());
1044+
}
10531045

1054-
return null;
1055-
}
1046+
return $valueTypesBuilder->getArray();
1047+
}
10561048

1057-
private function isFuncCallWithNormalCount(FuncCall $countFuncCall, Scope $scope): TrinaryLogic
1058-
{
1059-
$argType = $scope->getType($countFuncCall->getArgs()[0]->value);
1049+
return $type;
1050+
}
10601051

1061-
if (count($countFuncCall->getArgs()) === 1) {
1062-
return TrinaryLogic::createYes();
1063-
}
1064-
$mode = $scope->getType($countFuncCall->getArgs()[1]->value);
1052+
return TypeCombinator::intersect($type, new NonEmptyArrayType());
1053+
});
10651054

1066-
return (new ConstantIntegerType(COUNT_NORMAL))->isSuperTypeOf($mode)->result->or($argType->getIterableValueType()->isArray()->negate());
1055+
return $this->create($countFuncCall->getArgs()[0]->value, $resultType, $context, $scope)->setRootExpr($rootExpr);
10671056
}
10681057

10691058
private function specifyTypesForConstantBinaryExpression(
@@ -2100,30 +2089,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21002089
return $specifiedTypes;
21012090
}
21022091

2103-
if ($context->truthy()) {
2104-
if ($argType->isArray()->yes()) {
2105-
if (
2106-
$argType->isConstantArray()->yes()
2107-
&& $rightType->isSuperTypeOf($argType->getArraySize())->no()
2108-
) {
2109-
return $this->create($unwrappedLeftExpr->getArgs()[0]->value, new NeverType(), $context, $scope)->setRootExpr($expr);
2110-
}
2111-
2112-
$funcTypes = $this->create($unwrappedLeftExpr, $rightType, $context, $scope)->setRootExpr($expr);
2113-
$isNormalCount = $this->isFuncCallWithNormalCount($unwrappedLeftExpr, $scope);
2114-
$constArray = $isNormalCount->yes() ? $this->turnListIntoConstantArray($argType, $rightType) : null;
2115-
if ($constArray !== null) {
2116-
return $funcTypes->unionWith(
2117-
$this->create($unwrappedLeftExpr->getArgs()[0]->value, $constArray, $context, $scope)->setRootExpr($expr),
2118-
);
2119-
} elseif (IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($rightType)->yes()) {
2120-
return $funcTypes->unionWith(
2121-
$this->create($unwrappedLeftExpr->getArgs()[0]->value, new NonEmptyArrayType(), $context, $scope)->setRootExpr($expr),
2122-
);
2123-
}
2124-
2125-
return $funcTypes;
2092+
if ($context->truthy() && $argType->isArray()->yes()) {
2093+
$funcTypes = $this->create($unwrappedLeftExpr, $rightType, $context, $scope)->setRootExpr($expr);
2094+
if (IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($rightType)->yes()) {
2095+
return $funcTypes->unionWith(
2096+
$this->create($unwrappedLeftExpr->getArgs()[0]->value, new NonEmptyArrayType(), $context, $scope)->setRootExpr($expr),
2097+
);
21262098
}
2099+
2100+
return $funcTypes;
21272101
}
21282102
}
21292103

tests/PHPStan/Analyser/nsrt/bug11480.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public function intUnionCount(): void
8484
if (count($x) >= $count) {
8585
assertType("array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
8686
} else {
87-
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
87+
assertType("array{}", $x);
8888
}
8989
assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x);
9090
}

0 commit comments

Comments
 (0)