Skip to content

Commit

Permalink
Type::getEnumCases()
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 10, 2023
1 parent 8144f4a commit a0242e0
Show file tree
Hide file tree
Showing 24 changed files with 205 additions and 47 deletions.
5 changes: 2 additions & 3 deletions src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ConstantScalarType;
use PHPStan\Type\ConstantType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\FloatType;
use PHPStan\Type\FunctionTypeSpecifyingExtension;
use PHPStan\Type\Generic\GenericClassStringType;
Expand Down Expand Up @@ -232,7 +231,7 @@ public function specifyTypesInCondition(
$exprRightType = $scope->getType($expr->right);
if (
$exprLeftType instanceof ConstantScalarType
|| $exprLeftType instanceof EnumCaseObjectType
|| count($exprLeftType->getEnumCases()) === 1
|| ($exprLeftType instanceof ConstantType && !$exprRightType->equals($exprLeftType) && $exprRightType->isSuperTypeOf($exprLeftType)->yes())
) {
$types = $this->create(
Expand All @@ -246,7 +245,7 @@ public function specifyTypesInCondition(
}
if (
$exprRightType instanceof ConstantScalarType
|| $exprRightType instanceof EnumCaseObjectType
|| count($exprRightType->getEnumCases()) === 1
|| ($exprRightType instanceof ConstantType && !$exprLeftType->equals($exprRightType) && $exprLeftType->isSuperTypeOf($exprRightType)->yes())
) {
$leftType = $this->create(
Expand Down
51 changes: 7 additions & 44 deletions src/Rules/Comparison/MatchExpressionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,13 @@
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\NeverType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\SubtractableType;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use UnhandledMatchError;
use function array_keys;
use function array_map;
use function array_values;
use function count;
use function sprintf;

Expand Down Expand Up @@ -92,44 +86,13 @@ public function processNode(Node $node, Scope $scope): array

if (!$hasDefault && !$nextArmIsDead) {
$remainingType = $node->getEndScope()->getType($matchCondition);
if ($remainingType instanceof TypeWithClassName && $remainingType instanceof SubtractableType) {
$subtractedType = $remainingType->getSubtractedType();
if ($subtractedType !== null && $remainingType->getClassReflection() !== null) {
$classReflection = $remainingType->getClassReflection();
if ($classReflection->isEnum()) {
$cases = [];
foreach (array_keys($classReflection->getEnumCases()) as $name) {
$cases[$name] = new EnumCaseObjectType($classReflection->getName(), $name);
}

$subtractedTypes = TypeUtils::flattenTypes($subtractedType);
$set = true;
foreach ($subtractedTypes as $subType) {
if (!$subType instanceof EnumCaseObjectType) {
$set = false;
break;
}

if ($subType->getClassName() !== $classReflection->getName()) {
$set = false;
break;
}

unset($cases[$subType->getEnumCaseName()]);
}

$cases = array_values($cases);
$casesCount = count($cases);
if ($set) {
if ($casesCount > 1) {
$remainingType = new UnionType($cases);
}
if ($casesCount === 1) {
$remainingType = $cases[0];
}
}
}
}
$cases = $remainingType->getEnumCases();
$casesCount = count($cases);
if ($casesCount > 1) {
$remainingType = new UnionType($cases);
}
if ($casesCount === 1) {
$remainingType = $cases[0];
}
if (
!$remainingType instanceof NeverType
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasMethodType.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope)
];
}

public function getEnumCases(): array
{
return [];
}

public function traverse(callable $cb): Type
{
return $this;
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 @@ -284,6 +284,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function getEnumCases(): array
{
return [];
}

public function traverse(callable $cb): Type
{
return $this;
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 @@ -325,6 +325,11 @@ public function toArrayKey(): Type
return new ErrorType();
}

public function getEnumCases(): array
{
return [];
}

public function traverse(callable $cb): Type
{
$newValueType = $cb($this->valueType);
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Accessory/HasPropertyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope)
return [new TrivialParametersAcceptor()];
}

public function getEnumCases(): array
{
return [];
}

public function traverse(callable $cb): Type
{
return $this;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/CallableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,11 @@ public function isScalar(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getEnumCases(): array
{
return [];
}

public function isCommonCallable(): bool
{
return $this->isCommonCallable;
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ClosureType.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ public function isCallable(): TrinaryLogic
return TrinaryLogic::createYes();
}

public function getEnumCases(): array
{
return [];
}

/**
* @return ParametersAcceptor[]
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Enum/EnumCaseObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ public function isSmallerThanOrEqual(Type $otherType): TrinaryLogic
return TrinaryLogic::createNo();
}

public function getEnumCases(): array
{
return [$this];
}

/**
* @param mixed[] $properties
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,18 @@ public function shuffleArray(): Type
return $this->intersectTypes(static fn (Type $type): Type => $type->shuffleArray());
}

public function getEnumCases(): array
{
$enumCases = [];
foreach ($this->types as $type) {
foreach ($type->getEnumCases() as $enumCase) {
$enumCases[] = $enumCase;
}
}

return $enumCases;
}

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

public function getEnumCases(): array
{
return [];
}

public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
{
if ($receivedType instanceof UnionType || $receivedType instanceof IntersectionType) {
Expand Down
5 changes: 5 additions & 0 deletions src/Type/MixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ public function isCallable(): TrinaryLogic
return TrinaryLogic::createMaybe();
}

public function getEnumCases(): array
{
return [];
}

/**
* @return ParametersAcceptor[]
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NeverType.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ public function isScalar(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function getEnumCases(): array
{
return [];
}

/**
* @param mixed[] $properties
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Type/NonexistentParentClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ public function isScalar(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function getEnumCases(): array
{
return [];
}

/**
* @param mixed[] $properties
*/
Expand Down
29 changes: 29 additions & 0 deletions src/Type/ObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,35 @@ public function unsetOffset(Type $offsetType): Type
return $this;
}

public function getEnumCases(): array
{
$classReflection = $this->getClassReflection();
if ($classReflection === null) {
return [];
}

if (!$classReflection->isEnum()) {
return [];
}

$subtracted = [];
if ($this->subtractedType !== null) {
foreach ($this->subtractedType->getEnumCases() as $enumCase) {
$subtracted[$enumCase->getEnumCaseName()] = true;
}
}

$cases = [];
foreach ($classReflection->getEnumCases() as $enumCase) {
if (array_key_exists($enumCase->getName(), $subtracted)) {
continue;
}
$cases[] = new EnumCaseObjectType($classReflection->getName(), $enumCase->getName(), $classReflection);
}

return $cases;
}

public function isCallable(): TrinaryLogic
{
$parametersAcceptors = $this->findCallableParametersAcceptors();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/ObjectWithoutClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ function () use ($level): string {
);
}

public function getEnumCases(): array
{
return [];
}

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

public function getEnumCases(): array
{
return $this->getStaticObjectType()->getEnumCases();
}

public function isArray(): TrinaryLogic
{
return $this->getStaticObjectType()->isArray();
Expand Down
5 changes: 5 additions & 0 deletions src/Type/StrictMixedType.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,11 @@ public function getReferencedTemplateTypes(TemplateTypeVariance $positionVarianc
return [];
}

public function getEnumCases(): array
{
return [];
}

public function traverse(callable $cb): Type
{
return $this;
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 @@ -260,6 +260,11 @@ public function isCallable(): TrinaryLogic
return $this->resolve()->isCallable();
}

public function getEnumCases(): array
{
return $this->resolve()->getEnumCases();
}

public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope): array
{
return $this->resolve()->getCallableParametersAcceptors($scope);
Expand Down
5 changes: 5 additions & 0 deletions src/Type/Traits/NonObjectTypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,9 @@ public function isCloneable(): TrinaryLogic
return TrinaryLogic::createNo();
}

public function getEnumCases(): array
{
return [];
}

}
6 changes: 6 additions & 0 deletions src/Type/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use PHPStan\TrinaryLogic;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Enum\EnumCaseObjectType;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Generic\TemplateTypeReference;
use PHPStan\Type\Generic\TemplateTypeVariance;
Expand Down Expand Up @@ -118,6 +119,11 @@ public function shiftArray(): Type;

public function shuffleArray(): Type;

/**
* @return list<EnumCaseObjectType>
*/
public function getEnumCases(): array;

public function isCallable(): TrinaryLogic;

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

public function getEnumCases(): array
{
return $this->pickTypes(static fn (Type $type) => $type->getEnumCases());
}

public function isCallable(): TrinaryLogic
{
return $this->unionResults(static fn (Type $type): TrinaryLogic => $type->isCallable());
Expand Down
Loading

0 comments on commit a0242e0

Please sign in to comment.