Skip to content

Commit b8d2ddc

Browse files
Majkl578ondrejmirtes
authored andcommitted
Add rules for operands in arithmetic operations
1 parent 9db6ecc commit b8d2ddc

16 files changed

+645
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[PHPStan](https://github.com/phpstan/phpstan) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming:
88

99
* Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`.
10+
* Require numeric operands or arrays in `+` and numeric operands in `-`/`*`/`/`/`**`/`%`.
1011
* These functions contain a `$strict` parameter for better type safety, it must be set to `true`:
1112
* `in_array` (3rd parameter)
1213
* `array_search` (3rd parameter)

rules.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ rules:
1717
- PHPStan\Rules\Methods\MissingMethodParameterTypehintRule
1818
- PHPStan\Rules\Methods\MissingMethodReturnTypehintRule
1919
- PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule
20+
- PHPStan\Rules\Operators\OperandsInArithmeticAddition
21+
- PHPStan\Rules\Operators\OperandsInArithmeticDivision
22+
- PHPStan\Rules\Operators\OperandsInArithmeticExponentiation
23+
- PHPStan\Rules\Operators\OperandsInArithmeticModulo
24+
- PHPStan\Rules\Operators\OperandsInArithmeticMultiplication
25+
- PHPStan\Rules\Operators\OperandsInArithmeticSubtraction
2026
- PHPStan\Rules\Properties\MissingPropertyTypehintRule
2127
- PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule
2228
- PHPStan\Rules\StrictCalls\StrictFunctionCallsRule
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Type\ArrayType;
6+
use PHPStan\Type\MixedType;
7+
8+
class OperandsInArithmeticAddition implements \PHPStan\Rules\Rule
9+
{
10+
11+
public function getNodeType(): string
12+
{
13+
return \PhpParser\Node\Expr\BinaryOp\Plus::class;
14+
}
15+
16+
/**
17+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
18+
* @param \PHPStan\Analyser\Scope $scope
19+
* @return string[] errors
20+
*/
21+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
22+
{
23+
$leftType = $scope->getType($node->left);
24+
$rightType = $scope->getType($node->right);
25+
26+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
27+
return [];
28+
}
29+
30+
$mixedArrayType = new ArrayType(new MixedType(), new MixedType());
31+
32+
if ($mixedArrayType->isSuperTypeOf($leftType)->yes() && $mixedArrayType->isSuperTypeOf($rightType)->yes()) {
33+
return [];
34+
}
35+
36+
return ['Only numeric types or arrays are allowed in arithmetic addition.'];
37+
}
38+
39+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticDivision implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Div::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic division.'];
28+
}
29+
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticExponentiation implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Pow::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic exponentiation.'];
28+
}
29+
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticModulo implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Mod::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic modulo.'];
28+
}
29+
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticMultiplication implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Mul::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic multiplication.'];
28+
}
29+
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandsInArithmeticSubtraction implements \PHPStan\Rules\Rule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\BinaryOp\Minus::class;
11+
}
12+
13+
/**
14+
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
15+
* @param \PHPStan\Analyser\Scope $scope
16+
* @return string[] errors
17+
*/
18+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
19+
{
20+
$leftType = $scope->getType($node->left);
21+
$rightType = $scope->getType($node->right);
22+
23+
if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
24+
return [];
25+
}
26+
27+
return ['Only numeric types are allowed in arithmetic subtraction.'];
28+
}
29+
30+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Type\FloatType;
6+
use PHPStan\Type\IntegerType;
7+
use PHPStan\Type\Type;
8+
use PHPStan\Type\UnionType;
9+
10+
class OperatorRuleHelper
11+
{
12+
13+
public static function isValidForArithmeticOperation(Type $leftType, Type $rightType): bool
14+
{
15+
$acceptedType = new UnionType([new IntegerType(), new FloatType()]);
16+
17+
return $acceptedType->isSuperTypeOf($leftType)->yes() && $acceptedType->isSuperTypeOf($rightType)->yes();
18+
}
19+
20+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandsInArithmeticAdditionTest extends \PHPStan\Testing\RuleTestCase
8+
{
9+
10+
protected function getRule(): Rule
11+
{
12+
return new OperandsInArithmeticAddition();
13+
}
14+
15+
public function testRule(): void
16+
{
17+
$this->analyse([__DIR__ . '/data/operators.php'], [
18+
[
19+
'Only numeric types or arrays are allowed in arithmetic addition.',
20+
25,
21+
],
22+
[
23+
'Only numeric types or arrays are allowed in arithmetic addition.',
24+
26,
25+
],
26+
[
27+
'Only numeric types or arrays are allowed in arithmetic addition.',
28+
27,
29+
],
30+
[
31+
'Only numeric types or arrays are allowed in arithmetic addition.',
32+
28,
33+
],
34+
[
35+
'Only numeric types or arrays are allowed in arithmetic addition.',
36+
29,
37+
],
38+
[
39+
'Only numeric types or arrays are allowed in arithmetic addition.',
40+
29,
41+
],
42+
[
43+
'Only numeric types or arrays are allowed in arithmetic addition.',
44+
30,
45+
],
46+
[
47+
'Only numeric types or arrays are allowed in arithmetic addition.',
48+
30,
49+
],
50+
[
51+
'Only numeric types or arrays are allowed in arithmetic addition.',
52+
30,
53+
],
54+
]);
55+
}
56+
57+
}

0 commit comments

Comments
 (0)