Skip to content

Add rules for operands in arithmetic operations #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 39 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticAddition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;

class OperandsInArithmeticAddition implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Plus::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->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.'];
}

}
30 changes: 30 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticDivision.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

class OperandsInArithmeticDivision implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Div::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);

if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
return [];
}

return ['Only numeric types are allowed in arithmetic division.'];
}

}
30 changes: 30 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticExponentiation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

class OperandsInArithmeticExponentiation implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Pow::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);

if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
return [];
}

return ['Only numeric types are allowed in arithmetic exponentiation.'];
}

}
30 changes: 30 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticModulo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

class OperandsInArithmeticModulo implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Mod::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);

if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
return [];
}

return ['Only numeric types are allowed in arithmetic modulo.'];
}

}
30 changes: 30 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticMultiplication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

class OperandsInArithmeticMultiplication implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Mul::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);

if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
return [];
}

return ['Only numeric types are allowed in arithmetic multiplication.'];
}

}
30 changes: 30 additions & 0 deletions src/Rules/Operators/OperandsInArithmeticSubtraction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

class OperandsInArithmeticSubtraction implements \PHPStan\Rules\Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\BinaryOp\Minus::class;
}

/**
* @param \PhpParser\Node\Expr\BinaryOp\BooleanAnd $node
* @param \PHPStan\Analyser\Scope $scope
* @return string[] errors
*/
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);

if (OperatorRuleHelper::isValidForArithmeticOperation($leftType, $rightType)) {
return [];
}

return ['Only numeric types are allowed in arithmetic subtraction.'];
}

}
20 changes: 20 additions & 0 deletions src/Rules/Operators/OperatorRuleHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;

class OperatorRuleHelper
{

public static function isValidForArithmeticOperation(Type $leftType, Type $rightType): bool
{
$acceptedType = new UnionType([new IntegerType(), new FloatType()]);

return $acceptedType->isSuperTypeOf($leftType)->yes() && $acceptedType->isSuperTypeOf($rightType)->yes();
}

}
57 changes: 57 additions & 0 deletions tests/Rules/Operators/OperandsInArithmeticAdditionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

use PHPStan\Rules\Rule;

class OperandsInArithmeticAdditionTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): Rule
{
return new OperandsInArithmeticAddition();
}

public function testRule(): void
{
$this->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,
],
]);
}

}
57 changes: 57 additions & 0 deletions tests/Rules/Operators/OperandsInArithmeticDivisionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Operators;

use PHPStan\Rules\Rule;

class OperandsInArithmeticDivisionTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): Rule
{
return new OperandsInArithmeticDivision();
}

public function testRule(): void
{
$this->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,
],
]);
}

}
Loading