diff --git a/README.md b/README.md index 115745ba..882d6b1b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [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: * Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`. +* Require numeric operands or arrays in `+` and numeric operands in `-`/`*`/`/`/`**`/`%`. * These functions contain a `$strict` parameter for better type safety, it must be set to `true`: * `in_array` (3rd parameter) * `array_search` (3rd parameter) diff --git a/rules.neon b/rules.neon index 3e540e67..9236a92e 100644 --- a/rules.neon +++ b/rules.neon @@ -17,6 +17,12 @@ rules: - PHPStan\Rules\Methods\MissingMethodParameterTypehintRule - PHPStan\Rules\Methods\MissingMethodReturnTypehintRule - PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule + - PHPStan\Rules\Operators\OperandsInArithmeticAddition + - PHPStan\Rules\Operators\OperandsInArithmeticDivision + - PHPStan\Rules\Operators\OperandsInArithmeticExponentiation + - PHPStan\Rules\Operators\OperandsInArithmeticModulo + - PHPStan\Rules\Operators\OperandsInArithmeticMultiplication + - PHPStan\Rules\Operators\OperandsInArithmeticSubtraction - PHPStan\Rules\Properties\MissingPropertyTypehintRule - PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule - PHPStan\Rules\StrictCalls\StrictFunctionCallsRule diff --git a/src/Rules/Operators/OperandsInArithmeticAddition.php b/src/Rules/Operators/OperandsInArithmeticAddition.php new file mode 100644 index 00000000..9ad57c47 --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticAddition.php @@ -0,0 +1,39 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + $mixedArrayType = new ArrayType(new MixedType(), new MixedType()); + + if ($mixedArrayType->isSuperTypeOf($leftType)->yes() && $mixedArrayType->isSuperTypeOf($rightType)->yes()) { + return []; + } + + return ['Only numeric types or arrays are allowed in arithmetic addition.']; + } + +} diff --git a/src/Rules/Operators/OperandsInArithmeticDivision.php b/src/Rules/Operators/OperandsInArithmeticDivision.php new file mode 100644 index 00000000..30a118a5 --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticDivision.php @@ -0,0 +1,30 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + return ['Only numeric types are allowed in arithmetic division.']; + } + +} diff --git a/src/Rules/Operators/OperandsInArithmeticExponentiation.php b/src/Rules/Operators/OperandsInArithmeticExponentiation.php new file mode 100644 index 00000000..6a098ae2 --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticExponentiation.php @@ -0,0 +1,30 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + return ['Only numeric types are allowed in arithmetic exponentiation.']; + } + +} diff --git a/src/Rules/Operators/OperandsInArithmeticModulo.php b/src/Rules/Operators/OperandsInArithmeticModulo.php new file mode 100644 index 00000000..89991c4c --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticModulo.php @@ -0,0 +1,30 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + return ['Only numeric types are allowed in arithmetic modulo.']; + } + +} diff --git a/src/Rules/Operators/OperandsInArithmeticMultiplication.php b/src/Rules/Operators/OperandsInArithmeticMultiplication.php new file mode 100644 index 00000000..03547163 --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticMultiplication.php @@ -0,0 +1,30 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + return ['Only numeric types are allowed in arithmetic multiplication.']; + } + +} diff --git a/src/Rules/Operators/OperandsInArithmeticSubtraction.php b/src/Rules/Operators/OperandsInArithmeticSubtraction.php new file mode 100644 index 00000000..7301eb39 --- /dev/null +++ b/src/Rules/Operators/OperandsInArithmeticSubtraction.php @@ -0,0 +1,30 @@ +getType($node->left); + $rightType = $scope->getType($node->right); + + if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) { + return []; + } + + return ['Only numeric types are allowed in arithmetic subtraction.']; + } + +} diff --git a/src/Rules/Operators/OperatorRuleHelper.php b/src/Rules/Operators/OperatorRuleHelper.php new file mode 100644 index 00000000..de7cc950 --- /dev/null +++ b/src/Rules/Operators/OperatorRuleHelper.php @@ -0,0 +1,20 @@ +isSuperTypeOf($leftType)->yes() && $acceptedType->isSuperTypeOf($rightType)->yes(); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticAdditionTest.php b/tests/Rules/Operators/OperandsInArithmeticAdditionTest.php new file mode 100644 index 00000000..ccfa2eec --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticAdditionTest.php @@ -0,0 +1,57 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 25, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 26, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 27, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 28, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 29, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 29, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 30, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 30, + ], + [ + 'Only numeric types or arrays are allowed in arithmetic addition.', + 30, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticDivisionTest.php b/tests/Rules/Operators/OperandsInArithmeticDivisionTest.php new file mode 100644 index 00000000..67520244 --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticDivisionTest.php @@ -0,0 +1,57 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types are allowed in arithmetic division.', + 64, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 65, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 66, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 67, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 68, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 68, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 69, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 69, + ], + [ + 'Only numeric types are allowed in arithmetic division.', + 69, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticExponentiationTest.php b/tests/Rules/Operators/OperandsInArithmeticExponentiationTest.php new file mode 100644 index 00000000..18a6e98a --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticExponentiationTest.php @@ -0,0 +1,53 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 77, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 78, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 79, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 80, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 81, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 82, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 82, + ], + [ + 'Only numeric types are allowed in arithmetic exponentiation.', + 82, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticModuloTest.php b/tests/Rules/Operators/OperandsInArithmeticModuloTest.php new file mode 100644 index 00000000..39b279b1 --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticModuloTest.php @@ -0,0 +1,53 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types are allowed in arithmetic modulo.', + 90, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 91, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 92, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 93, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 94, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 94, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 95, + ], + [ + 'Only numeric types are allowed in arithmetic modulo.', + 95, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticMultiplicationTest.php b/tests/Rules/Operators/OperandsInArithmeticMultiplicationTest.php new file mode 100644 index 00000000..562dc640 --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticMultiplicationTest.php @@ -0,0 +1,57 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 51, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 52, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 53, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 54, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 55, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 55, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 56, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 56, + ], + [ + 'Only numeric types are allowed in arithmetic multiplication.', + 56, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/OperandsInArithmeticSubtractionTest.php b/tests/Rules/Operators/OperandsInArithmeticSubtractionTest.php new file mode 100644 index 00000000..a70e743f --- /dev/null +++ b/tests/Rules/Operators/OperandsInArithmeticSubtractionTest.php @@ -0,0 +1,57 @@ +analyse([__DIR__ . '/data/operators.php'], [ + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 38, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 39, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 40, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 41, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 42, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 42, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 43, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 43, + ], + [ + 'Only numeric types are allowed in arithmetic subtraction.', + 43, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/data/operators.php b/tests/Rules/Operators/data/operators.php new file mode 100644 index 00000000..e5da5ff0 --- /dev/null +++ b/tests/Rules/Operators/data/operators.php @@ -0,0 +1,95 @@ +