From 6b99e0cb96f59c814ce4619c101b49b2075d0790 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Wed, 24 Jul 2024 14:46:22 +0200 Subject: [PATCH] fixup! Improve `abs()` return type --- .../AbsFunctionDynamicReturnTypeExtension.php | 44 ++++++++++--------- tests/PHPStan/Analyser/nsrt/abs.php | 36 +++++++++++---- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php b/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php index 7c7bf154ed..f5b2a32dc5 100644 --- a/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php +++ b/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php @@ -38,49 +38,53 @@ public function getTypeFromFunctionCall( $type = $scope->getType($args[0]->value); if ($type instanceof UnionType) { - $ranges = []; + $absUnionTypes = []; foreach ($type->getTypes() as $unionType) { - if ( - !$unionType instanceof ConstantIntegerType - && !$unionType instanceof IntegerRangeType - && !$unionType instanceof ConstantFloatType - ) { + $absUnionType = $this->tryAbsType($unionType); + + if ($absUnionType === null) { return null; } - $absRange = $this->absType($unionType); - - foreach ($ranges as $index => $range) { - if (!($range instanceof IntegerRangeType)) { + foreach ($absUnionTypes as $index => $otherAbsUnionType) { + if (!($otherAbsUnionType instanceof IntegerRangeType)) { continue; } - $unionRange = $range->tryUnion($absRange); + $unionRange = $otherAbsUnionType->tryUnion($absUnionType); if ($unionRange !== null) { - $ranges[$index] = $unionRange; + $absUnionTypes[$index] = $unionRange; continue 2; } } - $ranges[] = $absRange; + $absUnionTypes[] = $absUnionType; } - if (count($ranges) === 1) { - return $ranges[0]; + if (count($absUnionTypes) === 1) { + return $absUnionTypes[0]; } - return new UnionType($ranges); + return new UnionType($absUnionTypes); } + return $this->tryAbsType($type); + } + + private function tryAbsType(Type $type): ?Type + { + $numberType = $type->toNumber(); + if ( - $type instanceof ConstantIntegerType - || $type instanceof IntegerRangeType - || $type instanceof ConstantFloatType + $numberType instanceof IntegerRangeType + || $numberType instanceof ConstantIntegerType + || $numberType instanceof ConstantFloatType + ) { - return $this->absType($type); + return $this->absType($numberType); } return null; diff --git a/tests/PHPStan/Analyser/nsrt/abs.php b/tests/PHPStan/Analyser/nsrt/abs.php index 49760b73c0..12a3acdf3c 100644 --- a/tests/PHPStan/Analyser/nsrt/abs.php +++ b/tests/PHPStan/Analyser/nsrt/abs.php @@ -121,19 +121,37 @@ public function constantFloat(float $float): void assertType('1.0', abs($float)); } - public function mixedUnion(float $float): void + public function string(string $string): void { - /** @var 1.0|int<2, 3> $float */ - assertType('1.0|int<2, 3>', abs($float)); + /** @var string $string */ + assertType('float|int<0, max>', abs($string)); - /** @var -1.0|int<-3, -2> $float */ - assertType('1.0|int<2, 3>', abs($float)); + /** @var numeric-string $string */ + assertType('float|int<0, max>', abs($string)); - /** @var 2.0|int<1, 3> $float */ - assertType('2.0|int<1, 3>', abs($float)); + /** @var '-1' $string */ + assertType('1', abs($string)); - /** @var -2.0|int<-3, -1> $float */ - assertType('2.0|int<1, 3>', abs($float)); + /** @var '-1'|'-2.0'|'3.0'|'4' $string */ + assertType('1|2.0|3.0|4', abs($string)); + } + + public function mixedUnion(mixed $value): void + { + /** @var 1.0|int<2, 3> $value */ + assertType('1.0|int<2, 3>', abs($value)); + + /** @var -1.0|int<-3, -2> $value */ + assertType('1.0|int<2, 3>', abs($value)); + + /** @var 2.0|int<1, 3> $value */ + assertType('2.0|int<1, 3>', abs($value)); + + /** @var -2.0|int<-3, -1> $value */ + assertType('2.0|int<1, 3>', abs($value)); + + /** @var -1.0|int<2, 3>|numeric-string $value */ + assertType('float|int<0, max>', abs($value)); } public function invalidType(mixed $nonInt): void