From a815d575dc10616e510713e01627369b10980d31 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Thu, 17 Oct 2024 10:36:01 +0200 Subject: [PATCH] Introduce TypeResult --- src/Analyser/MutatingScope.php | 4 +- .../InitializerExprTypeResolver.php | 55 +++++++++++-------- src/Type/TypeResult.php | 22 ++++++++ 3 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 src/Type/TypeResult.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 5d6636683f..401f011dc5 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -799,7 +799,7 @@ private function resolveType(string $exprString, Expr $node): Type $leftType = $this->getType($node->left); $rightType = $this->getType($node->right); - return $this->initializerExprTypeResolver->resolveEqualType($leftType, $rightType); + return $this->initializerExprTypeResolver->resolveEqualType($leftType, $rightType)->type; } if ($node instanceof Expr\BinaryOp\NotEqual) { @@ -959,7 +959,7 @@ private function resolveType(string $exprString, Expr $node): Type return new BooleanType(); } - return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType); + return $this->initializerExprTypeResolver->resolveIdenticalType($leftType, $rightType)->type; } if ($node instanceof Expr\BinaryOp\NotIdentical) { diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 8b015c91ab..178c16b4a8 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -65,6 +65,7 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypehintHelper; +use PHPStan\Type\TypeResult; use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeUtils; use PHPStan\Type\TypeWithClassName; @@ -298,7 +299,7 @@ public function getType(Expr $expr, InitializerExprContext $context): Type return $this->resolveIdenticalType( $this->getType($expr->left, $context), $this->getType($expr->right, $context), - ); + )->type; } if ($expr instanceof BinaryOp\NotIdentical) { @@ -309,7 +310,7 @@ public function getType(Expr $expr, InitializerExprContext $context): Type return $this->resolveEqualType( $this->getType($expr->left, $context), $this->getType($expr->right, $context), - ); + )->type; } if ($expr instanceof BinaryOp\NotEqual) { @@ -1349,34 +1350,42 @@ public function getShiftRightType(Expr $left, Expr $right, callable $getTypeCall return $this->resolveCommonMath(new Expr\BinaryOp\ShiftRight($left, $right), $leftType, $rightType); } - public function resolveIdenticalType(Type $leftType, Type $rightType): BooleanType + /** + * @return TypeResult + */ + public function resolveIdenticalType(Type $leftType, Type $rightType): TypeResult { if ($leftType instanceof NeverType || $rightType instanceof NeverType) { - return new ConstantBooleanType(false); + return new TypeResult(new ConstantBooleanType(false), []); } if ($leftType instanceof ConstantScalarType && $rightType instanceof ConstantScalarType) { - return new ConstantBooleanType($leftType->getValue() === $rightType->getValue()); + return new TypeResult(new ConstantBooleanType($leftType->getValue() === $rightType->getValue()), []); } $leftTypeFiniteTypes = $leftType->getFiniteTypes(); $rightTypeFiniteType = $rightType->getFiniteTypes(); if (count($leftTypeFiniteTypes) === 1 && count($rightTypeFiniteType) === 1) { - return new ConstantBooleanType($leftTypeFiniteTypes[0]->equals($rightTypeFiniteType[0])); + return new TypeResult(new ConstantBooleanType($leftTypeFiniteTypes[0]->equals($rightTypeFiniteType[0])), []); } - if ($leftType->isSuperTypeOf($rightType)->no() && $rightType->isSuperTypeOf($leftType)->no()) { - return new ConstantBooleanType(false); + $leftIsSuperTypeOfRight = $leftType->isSuperTypeOfWithReason($rightType); + $rightIsSuperTypeOfLeft = $rightType->isSuperTypeOfWithReason($leftType); + if ($leftIsSuperTypeOfRight->no() && $rightIsSuperTypeOfLeft->no()) { + return new TypeResult(new ConstantBooleanType(false), array_merge($leftIsSuperTypeOfRight->reasons, $rightIsSuperTypeOfLeft->reasons)); } if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) { - return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveIdenticalType($leftValueType, $rightValueType)); + return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): TypeResult => $this->resolveIdenticalType($leftValueType, $rightValueType)); } - return new BooleanType(); + return new TypeResult(new BooleanType(), []); } - public function resolveEqualType(Type $leftType, Type $rightType): BooleanType + /** + * @return TypeResult + */ + public function resolveEqualType(Type $leftType, Type $rightType): TypeResult { if ( ($leftType->isEnum()->yes() && $rightType->isTrue()->no()) @@ -1386,16 +1395,17 @@ public function resolveEqualType(Type $leftType, Type $rightType): BooleanType } if ($leftType instanceof ConstantArrayType && $rightType instanceof ConstantArrayType) { - return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): BooleanType => $this->resolveEqualType($leftValueType, $rightValueType)); + return $this->resolveConstantArrayTypeComparison($leftType, $rightType, fn ($leftValueType, $rightValueType): TypeResult => $this->resolveEqualType($leftValueType, $rightValueType)); } - return $leftType->looseCompare($rightType, $this->phpVersion); + return new TypeResult($leftType->looseCompare($rightType, $this->phpVersion), []); } /** - * @param callable(Type, Type): BooleanType $valueComparisonCallback + * @param callable(Type, Type): TypeResult $valueComparisonCallback + * @return TypeResult */ - private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, ConstantArrayType $rightType, callable $valueComparisonCallback): BooleanType + private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, ConstantArrayType $rightType, callable $valueComparisonCallback): TypeResult { $leftKeyTypes = $leftType->getKeyTypes(); $rightKeyTypes = $rightType->getKeyTypes(); @@ -1412,7 +1422,7 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, if (count($rightKeyTypes) === 0) { if (!$leftOptional) { - return new ConstantBooleanType(false); + return new TypeResult(new ConstantBooleanType(false), []); } continue; } @@ -1425,13 +1435,13 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, $found = true; break; } elseif (!$rightType->isOptionalKey($j)) { - return new ConstantBooleanType(false); + return new TypeResult(new ConstantBooleanType(false), []); } } if (!$found) { if (!$leftOptional) { - return new ConstantBooleanType(false); + return new TypeResult(new ConstantBooleanType(false), []); } continue; } @@ -1448,21 +1458,22 @@ private function resolveConstantArrayTypeComparison(ConstantArrayType $leftType, } } - $leftIdenticalToRight = $valueComparisonCallback($leftValueTypes[$i], $rightValueTypes[$j]); + $leftIdenticalToRightResult = $valueComparisonCallback($leftValueTypes[$i], $rightValueTypes[$j]); + $leftIdenticalToRight = $leftIdenticalToRightResult->type; if ($leftIdenticalToRight->isFalse()->yes()) { - return new ConstantBooleanType(false); + return $leftIdenticalToRightResult; } $resultType = TypeCombinator::union($resultType, $leftIdenticalToRight); } foreach (array_keys($rightKeyTypes) as $j) { if (!$rightType->isOptionalKey($j)) { - return new ConstantBooleanType(false); + return new TypeResult(new ConstantBooleanType(false), []); } $resultType = new BooleanType(); } - return $resultType->toBoolean(); + return new TypeResult($resultType->toBoolean(), []); } private function callOperatorTypeSpecifyingExtensions(Expr\BinaryOp $expr, Type $leftType, Type $rightType): ?Type diff --git a/src/Type/TypeResult.php b/src/Type/TypeResult.php new file mode 100644 index 0000000000..76d79326e8 --- /dev/null +++ b/src/Type/TypeResult.php @@ -0,0 +1,22 @@ + $reasons + */ + public function __construct( + public readonly Type $type, + public readonly array $reasons, + ) + { + } + +}