Skip to content

Commit

Permalink
Add Type::popArray() and Type::shiftArray()
Browse files Browse the repository at this point in the history
  • Loading branch information
herndlm authored and ondrejmirtes committed Oct 16, 2022
1 parent 74912c8 commit 67819b2
Show file tree
Hide file tree
Showing 17 changed files with 144 additions and 19 deletions.
21 changes: 3 additions & 18 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\NullType;
Expand Down Expand Up @@ -952,6 +951,7 @@ private function processStmtNode(

do {
$prevScope = $bodyScope;
$bodyScope = $bodyScope->mergeWith($scope);
$bodyScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, static function (): void {
})->filterOutLoopExitPoints();
$alwaysTerminating = $bodyScopeResult->isAlwaysTerminating();
Expand Down Expand Up @@ -1839,25 +1839,10 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
) {
$arrayArg = $expr->getArgs()[0]->value;
$arrayArgType = $scope->getType($arrayArg);
$scope = $scope->invalidateExpression($arrayArg);

$functionName = $functionReflection->getName();
$arrayArgType = TypeTraverser::map($arrayArgType, static function (Type $type, callable $traverse) use ($functionName): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}
if ($type instanceof ConstantArrayType) {
return $functionName === 'array_pop' ? $type->removeLast() : $type->removeFirst();
}
if ($type->isIterableAtLeastOnce()->yes()) {
return $type->toArray();
}
return $type;
});

$scope = $scope->assignExpression(
$scope = $scope->invalidateExpression($arrayArg)->assignExpression(
$arrayArg,
$arrayArgType,
$functionReflection->getName() === 'array_pop' ? $arrayArgType->popArray() : $arrayArgType->shiftArray(),
$scope->getNativeType($arrayArg),
);
}
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Accessory/AccessoryArrayListType.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ public function flipArray(): Type
return new MixedType();
}

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

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

public function isIterable(): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Accessory/NonEmptyArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ public function flipArray(): Type
return $this;
}

public function popArray(): Type
{
return new MixedType();
}

public function shiftArray(): Type
{
return new MixedType();
}

public function isIterable(): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Accessory/OversizedArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ public function flipArray(): Type
return $this;
}

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

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

public function isIterable(): TrinaryLogic
{
return TrinaryLogic::createYes();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,16 @@ public function flipArray(): Type
return new self(self::castToArrayKeyType($this->getIterableValueType()), $this->getIterableKeyType());
}

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

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

public function isCallable(): TrinaryLogic
{
return TrinaryLogic::createMaybe()->and($this->itemType->isString());
Expand Down
12 changes: 12 additions & 0 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,16 @@ public function flipArray(): Type
return $builder->getArray();
}

public function popArray(): Type
{
return $this->removeLastElements(1);
}

public function shiftArray(): Type
{
return $this->removeFirstElements(1);
}

public function isIterableAtLeastOnce(): TrinaryLogic
{
$keysCount = count($this->keyTypes);
Expand Down Expand Up @@ -778,6 +788,7 @@ public function isList(): TrinaryLogic
return parent::isList();
}

/** @deprecated Use popArray() instead */
public function removeLast(): self
{
return $this->removeLastElements(1);
Expand Down Expand Up @@ -837,6 +848,7 @@ private function removeLastElements(int $length): self
);
}

/** @deprecated Use shiftArray() instead */
public function removeFirst(): self
{
return $this->removeFirstElements(1);
Expand Down
10 changes: 10 additions & 0 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,16 @@ public function flipArray(): Type
return $this->intersectTypes(static fn (Type $type): Type => $type->flipArray());
}

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

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

public function isCallable(): TrinaryLogic
{
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->isCallable());
Expand Down
10 changes: 10 additions & 0 deletions src/Type/MixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ public function flipArray(): Type
return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
}

public function popArray(): Type
{
return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
}

public function shiftArray(): Type
{
return new ArrayType(new MixedType($this->isExplicitMixed), new MixedType($this->isExplicitMixed));
}

public function isCallable(): TrinaryLogic
{
if ($this->subtractedType !== null) {
Expand Down
10 changes: 10 additions & 0 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,16 @@ public function flipArray(): Type
return $this->getStaticObjectType()->flipArray();
}

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

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

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

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

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

public function isCallable(): TrinaryLogic
{
return $this->resolve()->isCallable();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Traits/MaybeArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ public function flipArray(): Type
return new ErrorType();
}

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

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

}
10 changes: 10 additions & 0 deletions src/Type/Traits/NonArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ public function flipArray(): Type
return new ErrorType();
}

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

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

}
4 changes: 4 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ public function getValuesArray(): Type;

public function flipArray(): Type;

public function popArray(): Type;

public function shiftArray(): Type;

public function isCallable(): TrinaryLogic;

/**
Expand Down
10 changes: 10 additions & 0 deletions src/Type/UnionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,16 @@ public function flipArray(): Type
return $this->unionTypes(static fn (Type $type): Type => $type->flipArray());
}

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

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

public function isCallable(): TrinaryLogic
{
return $this->unionResults(static fn (Type $type): TrinaryLogic => $type->isCallable());
Expand Down
7 changes: 7 additions & 0 deletions tests/PHPStan/Analyser/data/array-pop.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,11 @@ public function constantArraysWithOptionalKeys(array $arr): void
assertType('array{a?: 0, b?: 1}', $arr);
}

public function list(array $arr): void
{
/** @var list<string> $arr */
assertType('string|null', array_pop($arr));
assertType('list<string>', $arr);
}

}
7 changes: 7 additions & 0 deletions tests/PHPStan/Analyser/data/array-shift.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,11 @@ public function constantArraysWithOptionalKeys(array $arr): void
assertType('array{b?: 1, c?: 2}', $arr);
}

public function list(array $arr): void
{
/** @var list<string> $arr */
assertType('string|null', array_shift($arr));
assertType('list<string>', $arr);
}

}
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/bug-3993.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function doFoo($arguments)

array_shift($arguments);

assertType('mixed~null', $arguments);
assertType('array', $arguments);
assertType('int<0, max>', count($arguments));
}

Expand Down

0 comments on commit 67819b2

Please sign in to comment.