diff --git a/conf/config.neon b/conf/config.neon index 60fb7e44f1..4c41181c4d 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1108,6 +1108,11 @@ services: - class: PHPStan\Type\BitwiseFlagHelper + - + class: PHPStan\Type\Php\AbsFunctionDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\ArgumentBasedFunctionReturnTypeExtension tags: diff --git a/src/Type/Accessory/AccessoryArrayListType.php b/src/Type/Accessory/AccessoryArrayListType.php index 6f76467aac..874b177716 100644 --- a/src/Type/Accessory/AccessoryArrayListType.php +++ b/src/Type/Accessory/AccessoryArrayListType.php @@ -408,6 +408,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return TypeCombinator::union( diff --git a/src/Type/Accessory/AccessoryLiteralStringType.php b/src/Type/Accessory/AccessoryLiteralStringType.php index baf139b2dd..eac0986542 100644 --- a/src/Type/Accessory/AccessoryLiteralStringType.php +++ b/src/Type/Accessory/AccessoryLiteralStringType.php @@ -171,6 +171,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/Accessory/AccessoryNonEmptyStringType.php b/src/Type/Accessory/AccessoryNonEmptyStringType.php index 6ac04d9922..8e98af9d78 100644 --- a/src/Type/Accessory/AccessoryNonEmptyStringType.php +++ b/src/Type/Accessory/AccessoryNonEmptyStringType.php @@ -177,6 +177,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/Accessory/AccessoryNonFalsyStringType.php b/src/Type/Accessory/AccessoryNonFalsyStringType.php index c261682b6a..5aff19f63b 100644 --- a/src/Type/Accessory/AccessoryNonFalsyStringType.php +++ b/src/Type/Accessory/AccessoryNonFalsyStringType.php @@ -173,6 +173,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/Accessory/AccessoryNumericStringType.php b/src/Type/Accessory/AccessoryNumericStringType.php index e2ebaa514a..292d00f7a0 100644 --- a/src/Type/Accessory/AccessoryNumericStringType.php +++ b/src/Type/Accessory/AccessoryNumericStringType.php @@ -179,6 +179,11 @@ public function toNumber(): Type ]); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/Accessory/HasOffsetType.php b/src/Type/Accessory/HasOffsetType.php index 91a844d20c..cdb258d613 100644 --- a/src/Type/Accessory/HasOffsetType.php +++ b/src/Type/Accessory/HasOffsetType.php @@ -328,6 +328,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ErrorType(); diff --git a/src/Type/Accessory/HasOffsetValueType.php b/src/Type/Accessory/HasOffsetValueType.php index 8f07251305..a87f7879ab 100644 --- a/src/Type/Accessory/HasOffsetValueType.php +++ b/src/Type/Accessory/HasOffsetValueType.php @@ -374,6 +374,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ErrorType(); diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index 353dfe36e0..8ef7ecbb88 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -389,6 +389,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ConstantIntegerType(1); diff --git a/src/Type/Accessory/OversizedArrayType.php b/src/Type/Accessory/OversizedArrayType.php index dc309f1a0e..c6c9c5b6d7 100644 --- a/src/Type/Accessory/OversizedArrayType.php +++ b/src/Type/Accessory/OversizedArrayType.php @@ -385,6 +385,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ConstantIntegerType(1); diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 54dd4b36dd..c58bcf951a 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -588,6 +588,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/src/Type/BooleanType.php b/src/Type/BooleanType.php index aa8665f522..72be662c95 100644 --- a/src/Type/BooleanType.php +++ b/src/Type/BooleanType.php @@ -56,6 +56,11 @@ public function toNumber(): Type return $this->toInteger(); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toString(): Type { return TypeCombinator::union( diff --git a/src/Type/CallableType.php b/src/Type/CallableType.php index 1f8b5981a9..e51cf45631 100644 --- a/src/Type/CallableType.php +++ b/src/Type/CallableType.php @@ -299,6 +299,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/src/Type/ClosureType.php b/src/Type/ClosureType.php index e7c48df9bb..93b9989102 100644 --- a/src/Type/ClosureType.php +++ b/src/Type/ClosureType.php @@ -422,6 +422,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ErrorType(); diff --git a/src/Type/Constant/ConstantBooleanType.php b/src/Type/Constant/ConstantBooleanType.php index 88b7822f6a..db5328d0d2 100644 --- a/src/Type/Constant/ConstantBooleanType.php +++ b/src/Type/Constant/ConstantBooleanType.php @@ -82,6 +82,11 @@ public function toNumber(): Type return new ConstantIntegerType((int) $this->value); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toString(): Type { return new ConstantStringType((string) $this->value); diff --git a/src/Type/Constant/ConstantFloatType.php b/src/Type/Constant/ConstantFloatType.php index 3468ab5de8..3aeb1e2e18 100644 --- a/src/Type/Constant/ConstantFloatType.php +++ b/src/Type/Constant/ConstantFloatType.php @@ -12,6 +12,7 @@ use PHPStan\Type\Traits\ConstantScalarTypeTrait; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; +use function abs; use function ini_get; use function ini_set; use function is_finite; @@ -76,6 +77,11 @@ public function toInteger(): Type return new ConstantIntegerType((int) $this->value); } + public function toAbsoluteNumber(): Type + { + return new self(abs($this->value)); + } + public function toArrayKey(): Type { return new ConstantIntegerType((int) $this->value); diff --git a/src/Type/Constant/ConstantIntegerType.php b/src/Type/Constant/ConstantIntegerType.php index ae3d0511a8..9226acacc2 100644 --- a/src/Type/Constant/ConstantIntegerType.php +++ b/src/Type/Constant/ConstantIntegerType.php @@ -15,6 +15,7 @@ use PHPStan\Type\Traits\ConstantScalarTypeTrait; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; +use function abs; use function sprintf; /** @api */ @@ -76,6 +77,11 @@ public function toFloat(): Type return new ConstantFloatType($this->value); } + public function toAbsoluteNumber(): Type + { + return new self(abs($this->value)); + } + public function toString(): Type { return new ConstantStringType((string) $this->value); diff --git a/src/Type/Constant/ConstantStringType.php b/src/Type/Constant/ConstantStringType.php index 95ea40c601..fbdbaf2f97 100644 --- a/src/Type/Constant/ConstantStringType.php +++ b/src/Type/Constant/ConstantStringType.php @@ -292,6 +292,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toInteger(): Type { return new ConstantIntegerType((int) $this->value); diff --git a/src/Type/FloatType.php b/src/Type/FloatType.php index eaf79b1794..2111d4d912 100644 --- a/src/Type/FloatType.php +++ b/src/Type/FloatType.php @@ -110,6 +110,11 @@ public function toNumber(): Type return $this; } + public function toAbsoluteNumber(): Type + { + return $this; + } + public function toFloat(): Type { return $this; diff --git a/src/Type/IntegerRangeType.php b/src/Type/IntegerRangeType.php index b1134d9c20..b7962f71ae 100644 --- a/src/Type/IntegerRangeType.php +++ b/src/Type/IntegerRangeType.php @@ -447,6 +447,21 @@ public function toBoolean(): BooleanType return new ConstantBooleanType(false); } + public function toAbsoluteNumber(): Type + { + if ($this->min !== null && $this->min >= 0) { + return $this; + } + + if ($this->max === null || $this->max >= 0) { + $inversedMin = $this->min !== null ? $this->min * -1 : null; + + return self::fromInterval(0, $inversedMin !== null && $this->max !== null ? max($inversedMin, $this->max) : null); + } + + return self::fromInterval($this->max * -1, $this->min !== null ? $this->min * -1 : null); + } + public function toString(): Type { $isZero = (new ConstantIntegerType(0))->isSuperTypeOf($this); diff --git a/src/Type/IntegerType.php b/src/Type/IntegerType.php index 0c57a8087a..f91a646c9d 100644 --- a/src/Type/IntegerType.php +++ b/src/Type/IntegerType.php @@ -62,6 +62,11 @@ public function toNumber(): Type return $this; } + public function toAbsoluteNumber(): Type + { + return IntegerRangeType::createAllGreaterThanOrEqualTo(0); + } + public function toFloat(): Type { return new FloatType(); diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index f04fe05ef7..9b4fbf22ba 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -914,6 +914,13 @@ public function toNumber(): Type return $type; } + public function toAbsoluteNumber(): Type + { + $type = $this->intersectTypes(static fn (Type $type): Type => $type->toAbsoluteNumber()); + + return $type; + } + public function toString(): Type { $type = $this->intersectTypes(static fn (Type $type): Type => $type->toString()); diff --git a/src/Type/IterableType.php b/src/Type/IterableType.php index 25c1ff3039..f36cab2e7f 100644 --- a/src/Type/IterableType.php +++ b/src/Type/IterableType.php @@ -217,6 +217,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index 78b80cd897..402d192a7d 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -468,6 +468,11 @@ public function toNumber(): Type ]); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/NeverType.php b/src/Type/NeverType.php index b6e378a330..b619768b6d 100644 --- a/src/Type/NeverType.php +++ b/src/Type/NeverType.php @@ -351,6 +351,11 @@ public function toNumber(): Type return $this; } + public function toAbsoluteNumber(): Type + { + return $this; + } + public function toString(): Type { return $this; diff --git a/src/Type/NonexistentParentClassType.php b/src/Type/NonexistentParentClassType.php index b13f5d5105..bbf9dceec8 100644 --- a/src/Type/NonexistentParentClassType.php +++ b/src/Type/NonexistentParentClassType.php @@ -127,6 +127,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/src/Type/NullType.php b/src/Type/NullType.php index 469134dad3..e72cf25be2 100644 --- a/src/Type/NullType.php +++ b/src/Type/NullType.php @@ -144,6 +144,11 @@ public function toNumber(): Type return new ConstantIntegerType(0); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toString(): Type { return new ConstantStringType(''); diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 6a7b22e829..45a04563c9 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -563,6 +563,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return $this->toNumber()->toAbsoluteNumber(); + } + public function toInteger(): Type { if ($this->isInstanceOf('SimpleXMLElement')->yes()) { diff --git a/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php b/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php new file mode 100644 index 0000000000..d47d7f39eb --- /dev/null +++ b/src/Type/Php/AbsFunctionDynamicReturnTypeExtension.php @@ -0,0 +1,43 @@ +getName() === 'abs'; + } + + public function getTypeFromFunctionCall( + FunctionReflection $functionReflection, + FuncCall $functionCall, + Scope $scope, + ): ?Type + { + $args = $functionCall->getArgs(); + + if (!isset($args[0])) { + return null; + } + + $inputType = $scope->getType($args[0]->value); + + $outputType = $inputType->toAbsoluteNumber(); + + if ($outputType instanceof ErrorType) { + return null; + } + + return $outputType; + } + +} diff --git a/src/Type/ResourceType.php b/src/Type/ResourceType.php index afdcc5d432..4327f30bde 100644 --- a/src/Type/ResourceType.php +++ b/src/Type/ResourceType.php @@ -55,6 +55,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new StringType(); diff --git a/src/Type/StaticType.php b/src/Type/StaticType.php index df29b9eda2..766cede665 100644 --- a/src/Type/StaticType.php +++ b/src/Type/StaticType.php @@ -600,6 +600,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return $this->getStaticObjectType()->toString(); diff --git a/src/Type/StrictMixedType.php b/src/Type/StrictMixedType.php index 8bdd24093d..cc114d5c34 100644 --- a/src/Type/StrictMixedType.php +++ b/src/Type/StrictMixedType.php @@ -365,6 +365,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new ErrorType(); diff --git a/src/Type/StringType.php b/src/Type/StringType.php index 2090d52c85..3fe7dfca68 100644 --- a/src/Type/StringType.php +++ b/src/Type/StringType.php @@ -143,6 +143,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toInteger(): Type { return new IntegerType(); diff --git a/src/Type/Traits/LateResolvableTypeTrait.php b/src/Type/Traits/LateResolvableTypeTrait.php index df4d0c286b..a749a6fae0 100644 --- a/src/Type/Traits/LateResolvableTypeTrait.php +++ b/src/Type/Traits/LateResolvableTypeTrait.php @@ -327,6 +327,11 @@ public function toNumber(): Type return $this->resolve()->toNumber(); } + public function toAbsoluteNumber(): Type + { + return $this->resolve()->toAbsoluteNumber(); + } + public function toInteger(): Type { return $this->resolve()->toInteger(); diff --git a/src/Type/Traits/ObjectTypeTrait.php b/src/Type/Traits/ObjectTypeTrait.php index 8105cd0555..0c37e439c7 100644 --- a/src/Type/Traits/ObjectTypeTrait.php +++ b/src/Type/Traits/ObjectTypeTrait.php @@ -233,6 +233,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/src/Type/Type.php b/src/Type/Type.php index 8b56ea16af..3ac0a41c24 100644 --- a/src/Type/Type.php +++ b/src/Type/Type.php @@ -313,6 +313,8 @@ public function inferTemplateTypes(Type $receivedType): TemplateTypeMap; */ public function getReferencedTemplateTypes(TemplateTypeVariance $positionVariance): array; + public function toAbsoluteNumber(): Type; + /** * Traverses inner types * diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index b754092e81..241fb0aa69 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -878,6 +878,13 @@ public function toNumber(): Type return $type; } + public function toAbsoluteNumber(): Type + { + $type = $this->unionTypes(static fn (Type $type): Type => $type->toAbsoluteNumber()); + + return $type; + } + public function toString(): Type { $type = $this->unionTypes(static fn (Type $type): Type => $type->toString()); diff --git a/src/Type/VoidType.php b/src/Type/VoidType.php index d0c9273d40..8cbd1076ba 100644 --- a/src/Type/VoidType.php +++ b/src/Type/VoidType.php @@ -97,6 +97,11 @@ public function toNumber(): Type return new ErrorType(); } + public function toAbsoluteNumber(): Type + { + return new ErrorType(); + } + public function toString(): Type { return new ErrorType(); diff --git a/tests/PHPStan/Analyser/nsrt/abs.php b/tests/PHPStan/Analyser/nsrt/abs.php new file mode 100644 index 0000000000..eb644eb4bd --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/abs.php @@ -0,0 +1,215 @@ +', abs($int)); + + /** @var positive-int $int */ + assertType('int<1, max>', abs($int)); + + /** @var negative-int $int */ + assertType('int<1, max>', abs($int)); + + /** @var non-negative-int $int */ + assertType('int<0, max>', abs($int)); + + /** @var non-positive-int $int */ + assertType('int<0, max>', abs($int)); + + /** @var int<0, max> $int */ + assertType('int<0, max>', abs($int)); + + /** @var int<0, 123> $int */ + assertType('int<0, 123>', abs($int)); + + /** @var int<-123, 0> $int */ + assertType('int<0, 123>', abs($int)); + + /** @var int<1, max> $int */ + assertType('int<1, max>', abs($int)); + + /** @var int<123, max> $int */ + assertType('int<123, max>', abs($int)); + + /** @var int<123, 456> $int */ + assertType('int<123, 456>', abs($int)); + + /** @var int $int */ + assertType('int<0, max>', abs($int)); + + /** @var int $int */ + assertType('int<1, max>', abs($int)); + + /** @var int $int */ + assertType('int<123, max>', abs($int)); + + /** @var int<-456, -123> $int */ + assertType('int<123, 456>', abs($int)); + + /** @var int<-123, 123> $int */ + assertType('int<0, 123>', abs($int)); + + /** @var int $int */ + assertType('int<0, max>', abs($int)); + } + + public function multipleIntegerRanges(int $int): void + { + /** @var non-zero-int $int */ + assertType('int<1, max>', abs($int)); + + /** @var int|int<1, max> $int */ + assertType('int<1, max>', abs($int)); + + /** @var int<-20, -10>|int<5, 25> $int */ + assertType('int<5, 25>', abs($int)); + + /** @var int<-20, -5>|int<10, 25> $int */ + assertType('int<5, 25>', abs($int)); + + /** @var int<-25, -10>|int<5, 20> $int */ + assertType('int<5, 25>', abs($int)); + + /** @var int<-20, -10>|int<20, 30> $int */ + assertType('int<10, 30>', abs($int)); + } + + public function constantInteger(int $int): void + { + /** @var 0 $int */ + assertType('0', abs($int)); + + /** @var 1 $int */ + assertType('1', abs($int)); + + /** @var -1 $int */ + assertType('1', abs($int)); + + assertType('123', abs(123)); + + assertType('123', abs(-123)); + } + + public function mixedIntegerUnion(int $int): void + { + /** @var 123|int<456, max> $int */ + assertType('123|int<456, max>', abs($int)); + + /** @var int|-123 $int */ + assertType('123|int<456, max>', abs($int)); + + /** @var -123|int<124, 125> $int */ + assertType('int<123, 125>', abs($int)); + + /** @var int<124, 125>|-123 $int */ + assertType('int<123, 125>', abs($int)); + } + + public function constantFloat(float $float): void + { + /** @var 0.0 $float */ + assertType('0.0', abs($float)); + + /** @var 1.0 $float */ + assertType('1.0', abs($float)); + + /** @var -1.0 $float */ + assertType('1.0', abs($float)); + + assertType('123.4', abs(123.4)); + + assertType('123.4', abs(-123.4)); + } + + public function string(string $string): void + { + /** @var string $string */ + assertType('float|int<0, max>', abs($string)); + + /** @var numeric-string $string */ + assertType('float|int<0, max>', abs($string)); + + /** @var '-1' $string */ + assertType('1', abs($string)); + + /** @var '-1'|'-2.0'|'3.0'|'4' $string */ + assertType('1|2.0|3.0|4', abs($string)); + + /** @var literal-string $string */ + assertType('float|int<0, max>', abs($string)); + + assertType('123', abs('123')); + + assertType('123', abs('-123')); + + assertType('123.0', abs('123.0')); + + assertType('123.0', abs('-123.0')); + + assertType('float|int<0, max>', abs('foo')); + } + + 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 intersection(mixed $value): void + { + /** @var int&int<-10, 10> $value */ + assertType('int<0, 10>', abs($value)); + } + + public function invalidType(mixed $nonInt): void + { + /** @var string $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var string|positive-int $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var 'foo' $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var array $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var non-empty-list $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var object $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var \DateTime $nonInt */ + assertType('float|int<0, max>', abs($nonInt)); + + /** @var null $nonInt */ + assertType('0', abs($nonInt)); + + assertType('float|int<0, max>', abs('foo')); + + assertType('0', abs(null)); + } + +} diff --git a/tests/PHPStan/Rules/Api/ApiClassImplementsRuleTest.php b/tests/PHPStan/Rules/Api/ApiClassImplementsRuleTest.php index 9b95f57e25..a6a68a9b5e 100644 --- a/tests/PHPStan/Rules/Api/ApiClassImplementsRuleTest.php +++ b/tests/PHPStan/Rules/Api/ApiClassImplementsRuleTest.php @@ -42,22 +42,22 @@ public function testRuleOutOfPhpStan(): void ], [ 'Implementing PHPStan\Reflection\ReflectionProvider is not covered by backward compatibility promise. The interface might change in a minor PHPStan version.', - 333, + 338, $tip, ], [ 'Implementing PHPStan\Analyser\Scope is not covered by backward compatibility promise. The interface might change in a minor PHPStan version.', - 338, + 343, $tip, ], [ 'Implementing PHPStan\Reflection\FunctionReflection is not covered by backward compatibility promise. The interface might change in a minor PHPStan version.', - 343, + 348, $tip, ], [ 'Implementing PHPStan\Reflection\ExtendedMethodReflection is not covered by backward compatibility promise. The interface might change in a minor PHPStan version.', - 347, + 352, $tip, ], ]); diff --git a/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php b/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php index b7b9d69943..c7b42d7e6e 100644 --- a/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php +++ b/tests/PHPStan/Rules/Api/data/class-implements-out-of-phpstan.php @@ -217,6 +217,11 @@ public function toNumber(): \PHPStan\Type\Type // TODO: Implement toNumber() method. } + public function toAbsoluteNumber(): \PHPStan\Type\Type + { + // TODO: Implement toAbsoluteNumber() method. + } + public function toInteger(): \PHPStan\Type\Type { // TODO: Implement toInteger() method.