Skip to content
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
46 changes: 6 additions & 40 deletions src/Analyser/ExprHandler/FuncCallHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\UnionType;
use Throwable;
use function array_filter;
Expand Down Expand Up @@ -274,8 +273,10 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$isAlwaysTerminating = $isAlwaysTerminating || $argsResult->isAlwaysTerminating();

if ($arrayWalkValueTypes !== null && $arrayWalkArrayArg !== null) {
$newArrayType = $this->getArrayWalkResultType($arrayWalkOriginalArrayType, $arrayWalkValueTypes[0]);
$newArrayNativeType = $this->getArrayWalkResultType($arrayWalkOriginalArrayNativeType, $arrayWalkValueTypes[1]);
$arrayWalkValueType = $arrayWalkValueTypes[0];
$arrayWalkValueNativeType = $arrayWalkValueTypes[1];
$newArrayType = $arrayWalkOriginalArrayType->mapValueType(static fn (Type $type): Type => $arrayWalkValueType);
$newArrayNativeType = $arrayWalkOriginalArrayNativeType->mapValueType(static fn (Type $type): Type => $arrayWalkValueNativeType);

$scope = $nodeScopeResolver->processVirtualAssign(
$scope,
Expand Down Expand Up @@ -462,7 +463,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$storage,
$stmt,
$arrayArg,
new NativeTypeExpr($this->getArraySortPreserveListFunctionType($scope->getType($arrayArg)), $this->getArraySortPreserveListFunctionType($scope->getNativeType($arrayArg))),
new NativeTypeExpr($scope->getType($arrayArg)->sortArray(), $scope->getNativeType($arrayArg)->sortArray()),
$nodeCallback,
)->getScope();
}
Expand All @@ -479,7 +480,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex
$storage,
$stmt,
$arrayArg,
new NativeTypeExpr($this->getArraySortDoNotPreserveListFunctionType($scope->getType($arrayArg)), $this->getArraySortDoNotPreserveListFunctionType($scope->getNativeType($arrayArg))),
new NativeTypeExpr($scope->getType($arrayArg)->makeListMaybe(), $scope->getNativeType($arrayArg)->makeListMaybe()),
$nodeCallback,
)->getScope();
}
Expand Down Expand Up @@ -722,41 +723,6 @@ static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arra
return $arrayType;
}

private function getArraySortPreserveListFunctionType(Type $type): Type
{
$isIterableAtLeastOnce = $type->isIterableAtLeastOnce();
if ($isIterableAtLeastOnce->no()) {
return $type;
}

return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($isIterableAtLeastOnce): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}

if (!$type instanceof ArrayType && !$type instanceof ConstantArrayType) {
return $type;
}

$newArrayType = new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), $type->getIterableValueType()), new AccessoryArrayListType()]);
if ($isIterableAtLeastOnce->yes()) {
$newArrayType = TypeCombinator::intersect($newArrayType, new NonEmptyArrayType());
}

return $newArrayType;
});
}

private function getArraySortDoNotPreserveListFunctionType(Type $type): Type
{
return $type->makeListMaybe();
}

private function getArrayWalkResultType(Type $arrayType, Type $newValueType): Type
{
return $arrayType->mapValueType(static fn (Type $type): Type => $newValueType);
}

public function resolveType(MutatingScope $scope, Expr $expr): Type
{
if ($expr->name instanceof Expr) {
Expand Down
3 changes: 1 addition & 2 deletions src/Analyser/ExprHandler/InstanceofHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ public function resolveType(MutatingScope $scope, Expr $expr): Type
$classType = new ObjectType($className);
}
} else {
$classType = $scope->getType($expr->class);
$result = $classType->toObjectTypeForInstanceofCheck();
$result = $scope->getType($expr->class)->toObjectTypeForInstanceofCheck();
$classType = $result->type;
$uncertainty = $result->uncertainty;
}
Expand Down
3 changes: 1 addition & 2 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,7 @@ public function specifyTypesInCondition(
return $this->create($exprNode, $type, $context, $scope)->setRootExpr($expr);
}

$classType = $scope->getType($expr->class);
$result = $classType->toObjectTypeForInstanceofCheck();
$result = $scope->getType($expr->class)->toObjectTypeForInstanceofCheck();
$type = $result->type;
$uncertainty = $result->uncertainty;

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 @@ -242,6 +242,11 @@ public function shuffleArray(): Type
return $this;
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
if ($preserveKeys->no()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasOffsetType.php
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public function shuffleArray(): Type
return new NonEmptyArrayType();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
if (
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasOffsetValueType.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ public function shuffleArray(): Type
return new NonEmptyArrayType();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
if (
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ public function shuffleArray(): Type
return $this;
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
if ((new ConstantIntegerType(0))->isSuperTypeOf($offsetType)->yes() && $lengthType->isNull()->yes()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/OversizedArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public function shuffleArray(): Type
return $this;
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return $this;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,11 @@ public function shuffleArray(): Type
return $this->intersectTypes(static fn (Type $type): Type => $type->shuffleArray());
}

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

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
$result = $this->intersectTypes(static fn (Type $type): Type => $type->sliceArray($offsetType, $lengthType, $preserveKeys));
Expand Down
5 changes: 5 additions & 0 deletions src/Type/MixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ public function shuffleArray(): Type
return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new MixedType($this->isExplicitMixed)), new AccessoryArrayListType()]);
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
if ($this->isArray()->no()) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NeverType.php
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ public function shuffleArray(): Type
return new NeverType();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return new NeverType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,11 @@ public function shuffleArray(): Type
return $this->getStaticObjectType()->shuffleArray();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return $this->getStaticObjectType()->sliceArray($offsetType, $lengthType, $preserveKeys);
Expand Down
19 changes: 19 additions & 0 deletions src/Type/Traits/ArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,23 @@ public function chunkArray(Type $lengthType, TrinaryLogic $preserveKeys): Type
: $arrayType;
}

public function sortArray(): Type
{
$isIterableAtLeastOnce = $this->isIterableAtLeastOnce();
if ($isIterableAtLeastOnce->no()) {
return $this;
}

$listType = new IntersectionType([
new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), $this->getIterableValueType()),
new AccessoryArrayListType(),
]);

if ($isIterableAtLeastOnce->yes()) {
$listType = TypeCombinator::intersect($listType, new NonEmptyArrayType());
}

return $listType;
}

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

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

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return $this->resolve()->sliceArray($offsetType, $lengthType, $preserveKeys);
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/MaybeArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public function shuffleArray(): Type
return new ErrorType();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return new ErrorType();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/NonArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ public function shuffleArray(): Type
return new ErrorType();
}

public function sortArray(): Type
{
return $this;
}

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return new ErrorType();
Expand Down
8 changes: 8 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ public function shiftArray(): Type;
/** Models shuffle() effect on the array. Result is always a list. */
public function shuffleArray(): Type;

/**
* Models `sort` / `rsort` / `usort`: values are reordered and the array
* is re-indexed as a list. Empty arrays stay empty; non-array leaves
* pass through unchanged (the call site is responsible for arg-type
* checks).
*/
public function sortArray(): Type;

/** Models array_slice($array, $offset, $length, $preserveKeys). */
public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): 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 @@ -895,6 +895,11 @@ public function shuffleArray(): Type
return $this->unionTypes(static fn (Type $type): Type => $type->shuffleArray());
}

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

public function sliceArray(Type $offsetType, Type $lengthType, TrinaryLogic $preserveKeys): Type
{
return $this->unionTypes(static fn (Type $type): Type => $type->sliceArray($offsetType, $lengthType, $preserveKeys));
Expand Down
Loading