diff --git a/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php b/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php index f95f947..493b873 100644 --- a/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php +++ b/src/Rule/ForbidArithmeticOperationOnNonNumberRule.php @@ -18,6 +18,7 @@ use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\FloatType; use PHPStan\Type\IntegerType; +use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; @@ -113,6 +114,12 @@ private function processBinary( return []; // array merge syntax } + if (($this->isBcMathNumber($leftType) && $this->isFloat($rightType)) + || ($this->isFloat($leftType) && $this->isBcMathNumber($rightType)) + ) { + return $this->buildBinaryErrors($operator, 'BcMath\\Number and float', $leftType, $rightType); + } + if ( $operator === '%' && (!$leftType->isInteger()->yes() || !$rightType->isInteger()->yes()) @@ -136,7 +143,8 @@ private function isNumeric(Type $type): bool return $int->isSuperTypeOf($type)->yes() || $float->isSuperTypeOf($type)->yes() || $intOrFloat->isSuperTypeOf($type)->yes() - || ($this->allowNumericString && $type->isNumericString()->yes()); + || ($this->allowNumericString && $type->isNumericString()->yes()) + || $this->isBcMathNumber($type); } /** @@ -164,4 +172,18 @@ private function buildBinaryErrors( return [$error]; } + private function isBcMathNumber(Type $type): bool + { + $bcMathNumber = new ObjectType('BcMath\Number'); + + return $type->isSuperTypeOf($bcMathNumber)->yes(); + } + + private function isFloat(Type $type): bool + { + $float = new FloatType(); + + return $type->isSuperTypeOf($float)->yes(); + } + } diff --git a/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php b/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php index 458030e..a0b340c 100644 --- a/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php +++ b/tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php @@ -5,6 +5,7 @@ use LogicException; use PHPStan\Rules\Rule; use ShipMonk\PHPStan\RuleTestCase; +use const PHP_VERSION_ID; /** * @extends RuleTestCase @@ -35,6 +36,26 @@ public function testNoNumericString(): void $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/no-numeric-string.php'); } + public function testBcMathNumber(): void + { + if (PHP_VERSION_ID < 80_400) { + self::markTestSkipped('Requires PHP 8.4'); + } + + $this->allowNumericString = true; + $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number.php'); + } + + public function testBcMathNumberNoNumeric(): void + { + if (PHP_VERSION_ID < 80_400) { + self::markTestSkipped('Requires PHP 8.4'); + } + + $this->allowNumericString = false; + $this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php'); + } + protected function shouldFailOnPhpErrors(): bool { return false; // https://github.com/phpstan/phpstan-src/pull/3031 diff --git a/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php b/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php new file mode 100644 index 0000000..7d198ef --- /dev/null +++ b/tests/Rule/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php @@ -0,0 +1,38 @@ +