Skip to content

Commit 1a56b90

Browse files
committed
Fix (object + float) classification as BcNumber issue, extend test dataset
1 parent 2e9161a commit 1a56b90

File tree

3 files changed

+190
-63
lines changed

3 files changed

+190
-63
lines changed

src/Rule/ForbidArithmeticOperationOnNonNumberRule.php

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use PHPStan\Type\IntegerType;
2121
use PHPStan\Type\ObjectType;
2222
use PHPStan\Type\Type;
23+
use PHPStan\Type\TypeTraverser;
2324
use PHPStan\Type\UnionType;
2425
use PHPStan\Type\VerbosityLevel;
2526
use function sprintf;
@@ -114,8 +115,8 @@ private function processBinary(
114115
return []; // array merge syntax
115116
}
116117

117-
if (($this->isBcMathNumber($leftType) && $this->isFloat($rightType))
118-
|| ($this->isFloat($leftType) && $this->isBcMathNumber($rightType))
118+
if (($this->containsBcMathNumber($leftType) && $this->isFloat($rightType))
119+
|| ($this->isFloat($leftType) && $this->containsBcMathNumber($rightType))
119120
) {
120121
return $this->buildBinaryErrors($operator, 'BcMath\\Number and float', $leftType, $rightType);
121122
}
@@ -138,13 +139,14 @@ private function isNumeric(Type $type): bool
138139
{
139140
$int = new IntegerType();
140141
$float = new FloatType();
141-
$intOrFloat = new UnionType([$int, $float]);
142+
$bcNumber = new ObjectType('BcMath\Number');
143+
$intOrFloatOrBcNumber = new UnionType([$int, $float, $bcNumber]);
142144

143145
return $int->isSuperTypeOf($type)->yes()
144146
|| $float->isSuperTypeOf($type)->yes()
145-
|| $intOrFloat->isSuperTypeOf($type)->yes()
146-
|| ($this->allowNumericString && $type->isNumericString()->yes())
147-
|| $this->isBcMathNumber($type);
147+
|| $bcNumber->isSuperTypeOf($type)->yes()
148+
|| $intOrFloatOrBcNumber->isSuperTypeOf($type)->yes()
149+
|| ($this->allowNumericString && $type->isNumericString()->yes());
148150
}
149151

150152
/**
@@ -172,18 +174,24 @@ private function buildBinaryErrors(
172174
return [$error];
173175
}
174176

175-
private function isBcMathNumber(Type $type): bool
176-
{
177-
$bcMathNumber = new ObjectType('BcMath\Number');
178-
179-
return $type->isSuperTypeOf($bcMathNumber)->yes();
180-
}
181-
182177
private function isFloat(Type $type): bool
183178
{
184179
$float = new FloatType();
185180

186181
return $type->isSuperTypeOf($float)->yes();
187182
}
188183

184+
private function containsBcMathNumber(Type $type): bool
185+
{
186+
$bcMathFound = false;
187+
TypeTraverser::map($type, static function (Type $traversedTyped, callable $traverse) use (&$bcMathFound): Type {
188+
$bcMathNumber = new ObjectType('BcMath\Number');
189+
if ($bcMathNumber->isSuperTypeOf($traversedTyped)->yes()) {
190+
$bcMathFound = true;
191+
}
192+
return $traverse($traversedTyped);
193+
});
194+
return $bcMathFound;
195+
}
196+
189197
}

tests/Rule/ForbidArithmeticOperationOnNonNumberRuleTest.php

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use LogicException;
66
use PHPStan\Rules\Rule;
77
use ShipMonk\PHPStan\RuleTestCase;
8-
use const PHP_VERSION_ID;
98

109
/**
1110
* @extends RuleTestCase<ForbidArithmeticOperationOnNonNumberRule>
@@ -38,20 +37,12 @@ public function testNoNumericString(): void
3837

3938
public function testBcMathNumber(): void
4039
{
41-
if (PHP_VERSION_ID < 80_400) {
42-
self::markTestSkipped('Requires PHP 8.4');
43-
}
44-
4540
$this->allowNumericString = true;
4641
$this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number.php');
4742
}
4843

4944
public function testBcMathNumberNoNumeric(): void
5045
{
51-
if (PHP_VERSION_ID < 80_400) {
52-
self::markTestSkipped('Requires PHP 8.4');
53-
}
54-
5546
$this->allowNumericString = false;
5647
$this->analyseFile(__DIR__ . '/data/ForbidArithmeticOperationOnNonNumberRule/bcmath-number-no-numeric.php');
5748
}
Lines changed: 169 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,182 @@
11
<?php declare(strict_types = 1);
22

33
namespace ForbidArithmeticOperationOnNonNumberRule;
4+
45
use BcMath\Number;
56

67
class BcMathNumber {
78

89
/**
910
* @param numeric-string $ns
1011
*/
11-
public function testBcMathNumbers(Number $n, string $ns, int $int, float $float, int|float $intFloat): void {
12-
+$n;
13-
-$n;
14-
15-
$x = $n + $n;
16-
$x = $n - $n;
17-
$x = $n * $n;
18-
$x = $n / $n;
19-
20-
$x = $n + $int;
21-
$x = $int + $n;
22-
$x = $n + $ns;
23-
$x = $ns + $n;
24-
$x = $n - $int;
25-
$x = $int - $n;
26-
$x = $n - $ns;
27-
$x = $ns - $n;
28-
$x = $n * $int;
29-
$x = $int * $n;
30-
$x = $n * $ns;
31-
$x = $ns * $n;
32-
$x = $n / $int;
33-
$x = $int / $n;
34-
$x = $n / $ns;
35-
$x = $ns / $n;
36-
37-
$x = $n + $float; // error: Using + over BcMath\Number and float (BcMath\Number + float)
38-
$x = $float + $n; // error: Using + over BcMath\Number and float (float + BcMath\Number)
39-
$x = $n - $float; // error: Using - over BcMath\Number and float (BcMath\Number - float)
40-
$x = $float - $n; // error: Using - over BcMath\Number and float (float - BcMath\Number)
41-
$x = $n * $float; // error: Using * over BcMath\Number and float (BcMath\Number * float)
42-
$x = $float * $n; // error: Using * over BcMath\Number and float (float * BcMath\Number)
43-
$x = $n / $float; // error: Using / over BcMath\Number and float (BcMath\Number / float)
44-
$x = $float / $n; // error: Using / over BcMath\Number and float (float / BcMath\Number)
45-
$x = $n % $float; // error: Using % over BcMath\Number and float (BcMath\Number % float)
46-
$x = $float % $n; // error: Using % over BcMath\Number and float (float % BcMath\Number)
47-
$x = $n ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number ** float)
48-
$x = $float ** $n; // error: Using ** over BcMath\Number and float (float ** BcMath\Number)
49-
50-
$x = $n + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number + float|int)
51-
$x = $intFloat + $n; // error: Using + over BcMath\Number and float (float|int + BcMath\Number)
12+
public function testBcMathNumbers(
13+
object $object,
14+
Number $number,
15+
string $ns,
16+
int $int,
17+
float $float,
18+
bool|float $boolFloat,
19+
int|float $intFloat,
20+
Number|int $nInt,
21+
Number|float $nFloat,
22+
Number|int|float $nIntFloat,
23+
Number|int|float|bool $nIntFloatBool
24+
): void {
25+
+$number;
26+
-$number;
27+
28+
$x = $object + $float; // error: Using + over non-number (object + float)
29+
$x = $object + $number; // error: Using + over non-number (object + BcMath\Number)
30+
31+
$x = $number + $number;
32+
$x = $number - $number;
33+
$x = $number * $number;
34+
$x = $number / $number;
35+
36+
$x = $number / $boolFloat; // error: Using / over BcMath\Number and float (BcMath\Number / bool|float)
37+
38+
$x = $number + $int;
39+
$x = $int + $number;
40+
$x = $number + $ns;
41+
$x = $ns + $number;
42+
$x = $number - $int;
43+
$x = $int - $number;
44+
$x = $number - $ns;
45+
$x = $ns - $number;
46+
$x = $number * $int;
47+
$x = $int * $number;
48+
$x = $number * $ns;
49+
$x = $ns * $number;
50+
$x = $number / $int;
51+
$x = $int / $number;
52+
$x = $number / $ns;
53+
$x = $ns / $number;
54+
55+
$x = $nInt + $int;
56+
$x = $int + $nInt;
57+
$x = $nInt + $ns;
58+
$x = $ns + $nInt;
59+
$x = $nInt - $int;
60+
$x = $int - $nInt;
61+
$x = $nInt - $ns;
62+
$x = $ns - $nInt;
63+
$x = $nInt * $int;
64+
$x = $int * $nInt;
65+
$x = $nInt * $ns;
66+
$x = $ns * $nInt;
67+
$x = $nInt / $int;
68+
$x = $int / $nInt;
69+
$x = $nInt / $ns;
70+
$x = $ns / $nInt;
71+
72+
$x = $nFloat + $int;
73+
$x = $int + $nFloat;
74+
$x = $nFloat + $ns;
75+
$x = $ns + $nFloat;
76+
$x = $nFloat - $int;
77+
$x = $int - $nFloat;
78+
$x = $nFloat - $ns;
79+
$x = $ns - $nFloat;
80+
$x = $nFloat * $int;
81+
$x = $int * $nFloat;
82+
$x = $nFloat * $ns;
83+
$x = $ns * $nFloat;
84+
$x = $nFloat / $int;
85+
$x = $int / $nFloat;
86+
$x = $nFloat / $ns;
87+
$x = $ns / $nFloat;
88+
89+
$x = $nIntFloat + $int;
90+
$x = $int + $nIntFloat;
91+
$x = $nIntFloat + $ns;
92+
$x = $ns + $nIntFloat;
93+
$x = $nIntFloat - $int;
94+
$x = $int - $nIntFloat;
95+
$x = $nIntFloat - $ns;
96+
$x = $ns - $nIntFloat;
97+
$x = $nIntFloat * $int;
98+
$x = $int * $nIntFloat;
99+
$x = $nIntFloat * $ns;
100+
$x = $ns * $nIntFloat;
101+
$x = $nIntFloat / $int;
102+
$x = $int / $nIntFloat;
103+
$x = $nIntFloat / $ns;
104+
$x = $ns / $nIntFloat;
105+
106+
$x = $number + $float; // error: Using + over BcMath\Number and float (BcMath\Number + float)
107+
$x = $float + $number; // error: Using + over BcMath\Number and float (float + BcMath\Number)
108+
$x = $number - $float; // error: Using - over BcMath\Number and float (BcMath\Number - float)
109+
$x = $float - $number; // error: Using - over BcMath\Number and float (float - BcMath\Number)
110+
$x = $number * $float; // error: Using * over BcMath\Number and float (BcMath\Number * float)
111+
$x = $float * $number; // error: Using * over BcMath\Number and float (float * BcMath\Number)
112+
$x = $number / $float; // error: Using / over BcMath\Number and float (BcMath\Number / float)
113+
$x = $float / $number; // error: Using / over BcMath\Number and float (float / BcMath\Number)
114+
$x = $number % $float; // error: Using % over BcMath\Number and float (BcMath\Number % float)
115+
$x = $float % $number; // error: Using % over BcMath\Number and float (float % BcMath\Number)
116+
$x = $number ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number ** float)
117+
$x = $float ** $number; // error: Using ** over BcMath\Number and float (float ** BcMath\Number)
118+
$x = $number + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number + float|int)
119+
$x = $intFloat + $number; // error: Using + over BcMath\Number and float (float|int + BcMath\Number)
120+
121+
$x = $nInt + $float; // error: Using + over BcMath\Number and float (BcMath\Number|int + float)
122+
$x = $float + $nInt; // error: Using + over BcMath\Number and float (float + BcMath\Number|int)
123+
$x = $nInt - $float; // error: Using - over BcMath\Number and float (BcMath\Number|int - float)
124+
$x = $float - $nInt; // error: Using - over BcMath\Number and float (float - BcMath\Number|int)
125+
$x = $nInt * $float; // error: Using * over BcMath\Number and float (BcMath\Number|int * float)
126+
$x = $float * $nInt; // error: Using * over BcMath\Number and float (float * BcMath\Number|int)
127+
$x = $nInt / $float; // error: Using / over BcMath\Number and float (BcMath\Number|int / float)
128+
$x = $float / $nInt; // error: Using / over BcMath\Number and float (float / BcMath\Number|int)
129+
$x = $nInt % $float; // error: Using % over BcMath\Number and float (BcMath\Number|int % float)
130+
$x = $float % $nInt; // error: Using % over BcMath\Number and float (float % BcMath\Number|int)
131+
$x = $nInt ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number|int ** float)
132+
$x = $float ** $nInt; // error: Using ** over BcMath\Number and float (float ** BcMath\Number|int)
133+
$x = $nInt + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number|int + float|int)
134+
$x = $intFloat + $nInt; // error: Using + over BcMath\Number and float (float|int + BcMath\Number|int)
135+
136+
$x = $nFloat + $float; // error: Using + over BcMath\Number and float (BcMath\Number|float + float)
137+
$x = $float + $nFloat; // error: Using + over BcMath\Number and float (float + BcMath\Number|float)
138+
$x = $nFloat - $float; // error: Using - over BcMath\Number and float (BcMath\Number|float - float)
139+
$x = $float - $nFloat; // error: Using - over BcMath\Number and float (float - BcMath\Number|float)
140+
$x = $nFloat * $float; // error: Using * over BcMath\Number and float (BcMath\Number|float * float)
141+
$x = $float * $nFloat; // error: Using * over BcMath\Number and float (float * BcMath\Number|float)
142+
$x = $nFloat / $float; // error: Using / over BcMath\Number and float (BcMath\Number|float / float)
143+
$x = $float / $nFloat; // error: Using / over BcMath\Number and float (float / BcMath\Number|float)
144+
$x = $nFloat % $float; // error: Using % over BcMath\Number and float (BcMath\Number|float % float)
145+
$x = $float % $nFloat; // error: Using % over BcMath\Number and float (float % BcMath\Number|float)
146+
$x = $nFloat ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number|float ** float)
147+
$x = $float ** $nFloat; // error: Using ** over BcMath\Number and float (float ** BcMath\Number|float)
148+
$x = $nFloat + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number|float + float|int)
149+
$x = $intFloat + $nFloat; // error: Using + over BcMath\Number and float (float|int + BcMath\Number|float)
150+
151+
$x = $nIntFloat + $float; // error: Using + over BcMath\Number and float (BcMath\Number|float|int + float)
152+
$x = $float + $nIntFloat; // error: Using + over BcMath\Number and float (float + BcMath\Number|float|int)
153+
$x = $nIntFloat - $float; // error: Using - over BcMath\Number and float (BcMath\Number|float|int - float)
154+
$x = $float - $nIntFloat; // error: Using - over BcMath\Number and float (float - BcMath\Number|float|int)
155+
$x = $nIntFloat * $float; // error: Using * over BcMath\Number and float (BcMath\Number|float|int * float)
156+
$x = $float * $nIntFloat; // error: Using * over BcMath\Number and float (float * BcMath\Number|float|int)
157+
$x = $nIntFloat / $float; // error: Using / over BcMath\Number and float (BcMath\Number|float|int / float)
158+
$x = $float / $nIntFloat; // error: Using / over BcMath\Number and float (float / BcMath\Number|float|int)
159+
$x = $nIntFloat % $float; // error: Using % over BcMath\Number and float (BcMath\Number|float|int % float)
160+
$x = $float % $nIntFloat; // error: Using % over BcMath\Number and float (float % BcMath\Number|float|int)
161+
$x = $nIntFloat ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number|float|int ** float)
162+
$x = $float ** $nIntFloat; // error: Using ** over BcMath\Number and float (float ** BcMath\Number|float|int)
163+
$x = $nIntFloat + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number|float|int + float|int)
164+
$x = $intFloat + $nIntFloat; // error: Using + over BcMath\Number and float (float|int + BcMath\Number|float|int)
165+
166+
$x = $nIntFloatBool + $float; // error: Using + over BcMath\Number and float (BcMath\Number|bool|float|int + float)
167+
$x = $float + $nIntFloatBool; // error: Using + over BcMath\Number and float (float + BcMath\Number|bool|float|int)
168+
$x = $nIntFloatBool - $float; // error: Using - over BcMath\Number and float (BcMath\Number|bool|float|int - float)
169+
$x = $float - $nIntFloatBool; // error: Using - over BcMath\Number and float (float - BcMath\Number|bool|float|int)
170+
$x = $nIntFloatBool * $float; // error: Using * over BcMath\Number and float (BcMath\Number|bool|float|int * float)
171+
$x = $float * $nIntFloatBool; // error: Using * over BcMath\Number and float (float * BcMath\Number|bool|float|int)
172+
$x = $nIntFloatBool / $float; // error: Using / over BcMath\Number and float (BcMath\Number|bool|float|int / float)
173+
$x = $float / $nIntFloatBool; // error: Using / over BcMath\Number and float (float / BcMath\Number|bool|float|int)
174+
$x = $nIntFloatBool % $float; // error: Using % over BcMath\Number and float (BcMath\Number|bool|float|int % float)
175+
$x = $float % $nIntFloatBool; // error: Using % over BcMath\Number and float (float % BcMath\Number|bool|float|int)
176+
$x = $nIntFloatBool ** $float; // error: Using ** over BcMath\Number and float (BcMath\Number|bool|float|int ** float)
177+
$x = $float ** $nIntFloatBool; // error: Using ** over BcMath\Number and float (float ** BcMath\Number|bool|float|int)
178+
$x = $nIntFloatBool + $intFloat; // error: Using + over BcMath\Number and float (BcMath\Number|bool|float|int + float|int)
179+
$x = $intFloat + $nIntFloatBool; // error: Using + over BcMath\Number and float (float|int + BcMath\Number|bool|float|int)
52180
}
53181
}
54182

0 commit comments

Comments
 (0)