Skip to content

Add Type::getIterableCount() #1828

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Rules/FunctionCallParametersCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function check(
if (count($arrays) > 0) {
$minKeys = null;
foreach ($arrays as $array) {
$countType = $array->count();
$countType = $array->getIterableCount();
if ($countType instanceof ConstantIntegerType) {
$keysCount = $countType->getValue();
} elseif ($countType instanceof IntegerRangeType) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/AccessoryArrayListType.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getIterableCount(): Type
{
return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
return IntegerRangeType::fromInterval(0, null);
Expand Down
6 changes: 6 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
Expand Down Expand Up @@ -136,6 +137,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function getIterableCount(): Type
{
return IntegerRangeType::fromInterval(1, null);
}

public function getIterableKeyType(): Type
{
return new MixedType();
Expand Down
6 changes: 6 additions & 0 deletions src/Type/Accessory/OversizedArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Type\Constant\ConstantFloatType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\ErrorType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Traits\MaybeCallableTypeTrait;
Expand Down Expand Up @@ -135,6 +136,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function getIterableCount(): Type
{
return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
return new MixedType();
Expand Down
8 changes: 7 additions & 1 deletion src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getIterableCount(): Type
{
return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
$keyType = $this->keyType;
Expand Down Expand Up @@ -407,9 +412,10 @@ public function toArray(): Type
return $this;
}

/** @deprecated Use getIterableCount() instead */
public function count(): Type
{
return IntegerRangeType::fromInterval(0, null);
return $this->getIterableCount();
}

public static function castToArrayKeyType(Type $offsetType): Type
Expand Down
22 changes: 14 additions & 8 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,17 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getIterableCount(): Type
{
$optionalKeysCount = count($this->optionalKeys);
$totalKeysCount = count($this->getKeyTypes());
if ($optionalKeysCount === 0) {
return new ConstantIntegerType($totalKeysCount);
}

return IntegerRangeType::fromInterval($totalKeysCount - $optionalKeysCount, $totalKeysCount);
}

public function getFirstIterableKeyType(): Type
{
$keyTypes = [];
Expand Down Expand Up @@ -979,7 +990,7 @@ private function reindex(): self

public function toBoolean(): BooleanType
{
return $this->count()->toBoolean();
return $this->getIterableCount()->toBoolean();
}

public function toInteger(): Type
Expand Down Expand Up @@ -1116,15 +1127,10 @@ public function getValuesArray(): Type
return new self($keyTypes, $valueTypes, $autoIndex, $optionalKeys, true);
}

/** @deprecated Use getIterableCount() instead */
public function count(): Type
{
$optionalKeysCount = count($this->optionalKeys);
$totalKeysCount = count($this->getKeyTypes());
if ($optionalKeysCount === 0) {
return new ConstantIntegerType($totalKeysCount);
}

return IntegerRangeType::fromInterval($totalKeysCount - $optionalKeysCount, $totalKeysCount);
return $this->getIterableCount();
}

public function describe(VerbosityLevel $level): string
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isIterableAtLeastOnce());
}

public function getIterableCount(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableCount());
}

public function getIterableKeyType(): Type
{
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableKeyType());
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IterableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getIterableCount(): Type
{
return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
return $this->keyType;
Expand Down
9 changes: 9 additions & 0 deletions src/Type/MixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,15 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return $this->isIterable();
}

public function getIterableCount(): Type
{
if ($this->isIterable()->no()) {
return new ErrorType();
}

return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
return new self($this->isExplicitMixed);
Expand Down
25 changes: 4 additions & 21 deletions src/Type/Php/CountFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;
use function in_array;
use const COUNT_RECURSIVE;
Expand All @@ -27,34 +24,20 @@ public function getTypeFromFunctionCall(
FunctionReflection $functionReflection,
FuncCall $functionCall,
Scope $scope,
): Type
): ?Type
{
if (count($functionCall->getArgs()) < 1) {
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
return null;
}

if (count($functionCall->getArgs()) > 1) {
$mode = $scope->getType($functionCall->getArgs()[1]->value);
if ($mode->isSuperTypeOf(new ConstantIntegerType(COUNT_RECURSIVE))->yes()) {
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
return null;
}
}

$argType = $scope->getType($functionCall->getArgs()[0]->value);
$constantArrays = $scope->getType($functionCall->getArgs()[0]->value)->getConstantArrays();
if (count($constantArrays) === 0) {
if ($argType->isIterableAtLeastOnce()->yes()) {
return IntegerRangeType::fromInterval(1, null);
}

return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
}
$countTypes = [];
foreach ($constantArrays as $array) {
$countTypes[] = $array->count();
}

return TypeCombinator::union(...$countTypes);
return $scope->getType($functionCall->getArgs()[0]->value)->getIterableCount();
}

}
4 changes: 2 additions & 2 deletions src/Type/Php/MinMaxFunctionReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ private function compareTypes(
$firstType instanceof ConstantArrayType
&& $secondType instanceof ConstantArrayType
) {
if ($secondType->count() < $firstType->count()) {
if ($secondType->getIterableCount() < $firstType->getIterableCount()) {
return $secondType;
} elseif ($firstType->count() < $secondType->count()) {
} elseif ($firstType->getIterableCount() < $secondType->getIterableCount()) {
return $firstType;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return $this->getStaticObjectType()->isIterableAtLeastOnce();
}

public function getIterableCount(): Type
{
return $this->getStaticObjectType()->getIterableCount();
}

public function getIterableKeyType(): Type
{
return $this->getStaticObjectType()->getIterableKeyType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/LateResolvableTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return $this->resolve()->isIterableAtLeastOnce();
}

public function getIterableCount(): Type
{
return $this->resolve()->getIterableCount();
}

public function getIterableKeyType(): Type
{
return $this->resolve()->getIterableKeyType();
Expand Down
15 changes: 15 additions & 0 deletions src/Type/Traits/MaybeIterableTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace PHPStan\Type\Traits;

use PHPStan\TrinaryLogic;
use PHPStan\Type\ErrorType;
use PHPStan\Type\IntegerRangeType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

Expand All @@ -19,6 +21,19 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getIterableCount(): Type
{
if ($this->isIterable()->no()) {
return new ErrorType();
}

if ($this->isIterableAtLeastOnce()->yes()) {
return IntegerRangeType::fromInterval(1, null);
}

return IntegerRangeType::fromInterval(0, null);
}

public function getIterableKeyType(): Type
{
return new MixedType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/NonIterableTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function getIterableCount(): Type
{
return new ErrorType();
}

public function getIterableKeyType(): Type
{
return new ErrorType();
Expand Down
2 changes: 2 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public function isIterable(): TrinaryLogic;

public function isIterableAtLeastOnce(): TrinaryLogic;

public function getIterableCount(): Type;

public function getIterableKeyType(): Type;

public function getFirstIterableKeyType(): Type;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return $this->unionResults(static fn (Type $type): TrinaryLogic => $type->isIterableAtLeastOnce());
}

public function getIterableCount(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableCount());
}

public function getIterableKeyType(): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->getIterableKeyType());
Expand Down