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
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 getIterableKeyType(): Type
return IntegerRangeType::fromInterval(0, null);
}

public function getFirstIterableKeyType(): Type
{
return new ConstantIntegerType(0);
}

public function getLastIterableKeyType(): Type
{
return $this->getIterableKeyType();
}

public function getIterableValueType(): Type
{
return new MixedType();
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 @@ -141,6 +141,16 @@ public function getIterableKeyType(): Type
return new MixedType();
}

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

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

public function getIterableValueType(): Type
{
return new MixedType();
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 @@ -140,6 +140,16 @@ public function getIterableKeyType(): Type
return new MixedType();
}

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

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

public function getIterableValueType(): Type
{
return new MixedType();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/ArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ public function getIterableKeyType(): Type
return $keyType;
}

public function getFirstIterableKeyType(): Type
{
return $this->getIterableKeyType();
}

public function getLastIterableKeyType(): Type
{
return $this->getIterableKeyType();
}

public function getIterableValueType(): Type
{
return $this->getItemType();
Expand Down
48 changes: 30 additions & 18 deletions src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,30 +227,16 @@ public function getKeyTypes(): array
return $this->keyTypes;
}

/** @deprecated Use getFirstIterableKeyType() instead */
public function getFirstKeyType(): Type
{
$keyTypes = [];
foreach ($this->keyTypes as $i => $keyType) {
$keyTypes[] = $keyType;
if (!$this->isOptionalKey($i)) {
break;
}
}

return TypeCombinator::union(...$keyTypes);
return $this->getFirstIterableKeyType();
}

/** @deprecated Use getLastIterableKeyType() instead */
public function getLastKeyType(): Type
{
$keyTypes = [];
for ($i = count($this->keyTypes) - 1; $i >= 0; $i--) {
$keyTypes[] = $this->keyTypes[$i];
if (!$this->isOptionalKey($i)) {
break;
}
}

return TypeCombinator::union(...$keyTypes);
return $this->getLastIterableKeyType();
}

/**
Expand Down Expand Up @@ -698,6 +684,32 @@ public function isIterableAtLeastOnce(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getFirstIterableKeyType(): Type
{
$keyTypes = [];
foreach ($this->keyTypes as $i => $keyType) {
$keyTypes[] = $keyType;
if (!$this->isOptionalKey($i)) {
break;
}
}

return TypeCombinator::union(...$keyTypes);
}

public function getLastIterableKeyType(): Type
{
$keyTypes = [];
for ($i = count($this->keyTypes) - 1; $i >= 0; $i--) {
$keyTypes[] = $this->keyTypes[$i];
if (!$this->isOptionalKey($i)) {
break;
}
}

return TypeCombinator::union(...$keyTypes);
}

public function getFirstIterableValueType(): Type
{
$valueTypes = [];
Expand Down
10 changes: 10 additions & 0 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ public function getIterableKeyType(): Type
return $this->intersectTypes(static fn (Type $type): Type => $type->getIterableKeyType());
}

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

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

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

public function getFirstIterableKeyType(): Type
{
return new self($this->isExplicitMixed);
}

public function getLastIterableKeyType(): Type
{
return new self($this->isExplicitMixed);
}

public function getIterableValueType(): Type
{
return new self($this->isExplicitMixed);
Expand Down
32 changes: 3 additions & 29 deletions src/Type/Php/ArrayKeyFirstDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
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\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;

class ArrayKeyFirstDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{
Expand All @@ -21,10 +18,10 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
return $functionReflection->getName() === 'array_key_first';
}

public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
{
if (!isset($functionCall->getArgs()[0])) {
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
return null;
}

$argType = $scope->getType($functionCall->getArgs()[0]->value);
Expand All @@ -33,30 +30,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$constantArrays = $argType->getConstantArrays();
if (count($constantArrays) > 0) {
$keyTypes = [];
foreach ($constantArrays as $constantArray) {
$iterableAtLeastOnce = $constantArray->isIterableAtLeastOnce();
if (!$iterableAtLeastOnce->yes()) {
$keyTypes[] = new NullType();
}
if ($iterableAtLeastOnce->no()) {
continue;
}

$keyTypes[] = $constantArray->getFirstKeyType();
}

return TypeCombinator::union(...$keyTypes);
}

if ($argType->isList()->yes()) {
$keyType = new ConstantIntegerType(0);
} else {
$keyType = $argType->getIterableKeyType();
}

$keyType = $argType->getFirstIterableKeyType();
if ($iterableAtLeastOnce->yes()) {
return $keyType;
}
Expand Down
26 changes: 3 additions & 23 deletions src/Type/Php/ArrayKeyLastDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use function count;

class ArrayKeyLastDynamicReturnTypeExtension implements DynamicFunctionReturnTypeExtension
{
Expand All @@ -20,10 +18,10 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
return $functionReflection->getName() === 'array_key_last';
}

public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
{
if (!isset($functionCall->getArgs()[0])) {
return ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType();
return null;
}

$argType = $scope->getType($functionCall->getArgs()[0]->value);
Expand All @@ -32,25 +30,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
return new NullType();
}

$constantArrays = $argType->getConstantArrays();
if (count($constantArrays) > 0) {
$keyTypes = [];
foreach ($constantArrays as $constantArray) {
$iterableAtLeastOnce = $constantArray->isIterableAtLeastOnce();
if (!$iterableAtLeastOnce->yes()) {
$keyTypes[] = new NullType();
}
if ($iterableAtLeastOnce->no()) {
continue;
}

$keyTypes[] = $constantArray->getLastKeyType();
}

return TypeCombinator::union(...$keyTypes);
}

$keyType = $argType->getIterableKeyType();
$keyType = $argType->getLastIterableKeyType();
if ($iterableAtLeastOnce->yes()) {
return $keyType;
}
Expand Down
10 changes: 10 additions & 0 deletions src/Type/StaticType.php
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ public function getIterableKeyType(): Type
return $this->getStaticObjectType()->getIterableKeyType();
}

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

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

public function getIterableValueType(): Type
{
return $this->getStaticObjectType()->getIterableValueType();
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 @@ -130,6 +130,16 @@ public function getIterableKeyType(): Type
return $this->resolve()->getIterableKeyType();
}

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

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

public function getIterableValueType(): Type
{
return $this->resolve()->getIterableValueType();
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 @@ -19,6 +19,16 @@ public function getConstantArrays(): array
return [];
}

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

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

public function getFirstIterableValueType(): Type
{
return new MixedType();
Expand Down
10 changes: 10 additions & 0 deletions src/Type/Traits/NonArrayTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ public function getConstantArrays(): array
return [];
}

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

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

public function getFirstIterableValueType(): Type
{
return new ErrorType();
Expand Down
4 changes: 4 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ public function isIterableAtLeastOnce(): TrinaryLogic;

public function getIterableKeyType(): Type;

public function getFirstIterableKeyType(): Type;

public function getLastIterableKeyType(): Type;

public function getIterableValueType(): Type;

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

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

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

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