Skip to content

Commit 2f65530

Browse files
committed
Clean up a bit more aggressively
1 parent e6b241d commit 2f65530

File tree

2 files changed

+81
-107
lines changed

2 files changed

+81
-107
lines changed

Diff for: src/Analyser/TypeSpecifier.php

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

275-
$sizeType = null;
276275
if ($leftType instanceof ConstantIntegerType) {
277276
if ($orEqual) {
278277
$sizeType = IntegerRangeType::createAllGreaterThanOrEqualTo($leftType->getValue());
@@ -281,6 +280,8 @@ public function specifyTypesInCondition(
281280
}
282281
} elseif ($leftType instanceof IntegerRangeType) {
283282
$sizeType = $leftType->shift($offset);
283+
} else {
284+
$sizeType = $leftType;
284285
}
285286

286287
$specifiedTypes = $this->specifyTypesForCountFuncCall($expr->right, $argType, $sizeType, $context, $scope, $expr);
@@ -970,103 +971,91 @@ public function specifyTypesInCondition(
970971
return (new SpecifiedTypes([], []))->setRootExpr($expr);
971972
}
972973

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

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

1003-
return null;
1004-
}
994+
$resultType = TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($sizeType, $context) {
995+
if ($type instanceof UnionType) {
996+
return $traverse($type);
997+
}
1005998

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

1022-
if (
1023-
$type->isList()->yes()
1024-
&& $sizeType instanceof IntegerRangeType
1025-
&& $sizeType->getMin() !== null
1026-
) {
1027-
// turn optional offsets non-optional
1028-
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1029-
for ($i = 0; $i < $sizeType->getMin(); $i++) {
1030-
$offsetType = new ConstantIntegerType($i);
1031-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
1032-
}
1033-
if ($sizeType->getMax() !== null) {
1034-
for ($i = $sizeType->getMin(); $i < $sizeType->getMax(); $i++) {
1035-
$offsetType = new ConstantIntegerType($i);
1036-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), true);
1037-
}
1038-
} elseif ($type->isConstantArray()->yes()) {
1039-
for ($i = $sizeType->getMin();; $i++) {
1040-
$offsetType = new ConstantIntegerType($i);
1041-
$hasOffset = $type->hasOffsetValueType($offsetType);
1042-
if ($hasOffset->no()) {
1043-
break;
1007+
if ($type->isList()->yes()) {
1008+
if (
1009+
$sizeType instanceof ConstantIntegerType
1010+
&& $sizeType->getValue() < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT
1011+
) {
1012+
// turn optional offsets non-optional
1013+
$valueTypesBuilder = ConstantArrayTypeBuilder::createEmpty();
1014+
for ($i = 0; $i < $sizeType->getValue(); $i++) {
1015+
$offsetType = new ConstantIntegerType($i);
1016+
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType));
10441017
}
1045-
$valueTypesBuilder->setOffsetValueType($offsetType, $type->getOffsetValueType($offsetType), !$hasOffset->yes());
1018+
return $valueTypesBuilder->getArray();
10461019
}
1047-
} else {
1048-
return null;
1049-
}
10501020

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

1057-
return null;
1058-
}
1049+
return $valueTypesBuilder->getArray();
1050+
}
10591051

1060-
private function isFuncCallWithNormalCount(FuncCall $countFuncCall, Scope $scope): TrinaryLogic
1061-
{
1062-
$argType = $scope->getType($countFuncCall->getArgs()[0]->value);
1052+
return $type;
1053+
}
10631054

1064-
if (count($countFuncCall->getArgs()) === 1) {
1065-
return TrinaryLogic::createYes();
1066-
}
1067-
$mode = $scope->getType($countFuncCall->getArgs()[1]->value);
1055+
return TypeCombinator::intersect($type, new NonEmptyArrayType());
1056+
});
10681057

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

10721061
private function specifyTypesForConstantBinaryExpression(
@@ -2103,30 +2092,15 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty
21032092
return $specifiedTypes;
21042093
}
21052094

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

Diff for: 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)