Skip to content

Commit 586314f

Browse files
Majkl578ondrejmirtes
authored andcommitted
Add rules for operand in pre/post-inc/decrement
1 parent 142d99d commit 586314f

14 files changed

+406
-3
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

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

rules.neon

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ rules:
1717
- PHPStan\Rules\Methods\MissingMethodParameterTypehintRule
1818
- PHPStan\Rules\Methods\MissingMethodReturnTypehintRule
1919
- PHPStan\Rules\Methods\WrongCaseOfInheritedMethodRule
20+
- PHPStan\Rules\Operators\OperandInArithmeticPostDecrementRule
21+
- PHPStan\Rules\Operators\OperandInArithmeticPostIncrementRule
22+
- PHPStan\Rules\Operators\OperandInArithmeticPreDecrementRule
23+
- PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule
2024
- PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule
2125
- PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule
2226
- PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Type\VerbosityLevel;
7+
8+
abstract class OperandInArithmeticIncrementOrDecrementRule implements Rule
9+
{
10+
11+
/** @var OperatorRuleHelper */
12+
private $helper;
13+
14+
public function __construct(OperatorRuleHelper $helper)
15+
{
16+
$this->helper = $helper;
17+
}
18+
19+
/**
20+
* @param \PhpParser\Node\Expr\PreInc|\PhpParser\Node\Expr\PreDec|\PhpParser\Node\Expr\PostInc|\PhpParser\Node\Expr\PostDec $node
21+
* @param \PHPStan\Analyser\Scope $scope
22+
* @return string[] errors
23+
*/
24+
public function processNode(\PhpParser\Node $node, \PHPStan\Analyser\Scope $scope): array
25+
{
26+
$messages = [];
27+
$varType = $scope->getType($node->var);
28+
29+
if (!$this->helper->isValidForIncrementOrDecrement($scope, $node->var)) {
30+
$messages[] = sprintf(
31+
'Only numeric types are allowed in %s, %s given.',
32+
$this->describeOperation(),
33+
$varType->describe(VerbosityLevel::typeOnly())
34+
);
35+
}
36+
37+
return $messages;
38+
}
39+
40+
abstract protected function describeOperation(): string;
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandInArithmeticPostDecrementRule extends OperandInArithmeticIncrementOrDecrementRule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\PostDec::class;
11+
}
12+
13+
protected function describeOperation(): string
14+
{
15+
return 'post-decrement';
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandInArithmeticPostIncrementRule extends OperandInArithmeticIncrementOrDecrementRule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\PostInc::class;
11+
}
12+
13+
protected function describeOperation(): string
14+
{
15+
return 'post-increment';
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandInArithmeticPreDecrementRule extends OperandInArithmeticIncrementOrDecrementRule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\PreDec::class;
11+
}
12+
13+
protected function describeOperation(): string
14+
{
15+
return 'pre-decrement';
16+
}
17+
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
class OperandInArithmeticPreIncrementRule extends OperandInArithmeticIncrementOrDecrementRule
6+
{
7+
8+
public function getNodeType(): string
9+
{
10+
return \PhpParser\Node\Expr\PreInc::class;
11+
}
12+
13+
protected function describeOperation(): string
14+
{
15+
return 'pre-increment';
16+
}
17+
18+
}

src/Rules/Operators/OperatorRuleHelper.php

+19-3
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,37 @@ public function isValidForArithmeticOperation(Scope $scope, Expr $expr): bool
3030
return true;
3131
}
3232

33+
// already reported by PHPStan core
3334
if ($type->toNumber() instanceof ErrorType) {
3435
return true;
3536
}
3637

38+
return $this->isSubtypeOfNumber($scope, $expr);
39+
}
40+
41+
public function isValidForIncrementOrDecrement(Scope $scope, Expr $expr): bool
42+
{
43+
$type = $scope->getType($expr);
44+
if ($type instanceof MixedType) {
45+
return true;
46+
}
47+
48+
return $this->isSubtypeOfNumber($scope, $expr);
49+
}
50+
51+
private function isSubtypeOfNumber(Scope $scope, Expr $expr): bool
52+
{
3753
$acceptedType = new UnionType([new IntegerType(), new FloatType()]);
3854

39-
$typeToCheck = $this->ruleLevelHelper->findTypeToCheck(
55+
$type = $this->ruleLevelHelper->findTypeToCheck(
4056
$scope,
4157
$expr,
4258
'',
4359
function (Type $type) use ($acceptedType): bool {
4460
return $acceptedType->isSuperTypeOf($type)->yes();
4561
}
46-
);
47-
$type = $typeToCheck->getType();
62+
)->getType();
63+
4864
if ($type instanceof ErrorType) {
4965
return true;
5066
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Rules\RuleLevelHelper;
7+
8+
abstract class OperandInArithmeticIncrementOrDecrementRuleTest extends \PHPStan\Testing\RuleTestCase
9+
{
10+
11+
protected function getRule(): Rule
12+
{
13+
return $this->createRule(
14+
new OperatorRuleHelper(
15+
new RuleLevelHelper($this->createBroker(), true, false, true)
16+
)
17+
);
18+
}
19+
20+
public function testRule(): void
21+
{
22+
$this->analyse([__DIR__ . '/data/increment-decrement.php'], $this->getExpectedErrors());
23+
}
24+
25+
abstract protected function createRule(OperatorRuleHelper $helper): Rule;
26+
27+
/**
28+
* @return mixed[][]
29+
*/
30+
abstract protected function getExpectedErrors(): array;
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandInArithmeticPostDecrementRuleTest extends OperandInArithmeticIncrementOrDecrementRuleTest
8+
{
9+
10+
protected function createRule(OperatorRuleHelper $helper): Rule
11+
{
12+
return new OperandInArithmeticPostDecrementRule($helper);
13+
}
14+
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
protected function getExpectedErrors(): array
19+
{
20+
return [
21+
[
22+
'Only numeric types are allowed in post-decrement, false given.',
23+
21,
24+
],
25+
[
26+
'Only numeric types are allowed in post-decrement, string given.',
27+
22,
28+
],
29+
[
30+
'Only numeric types are allowed in post-decrement, null given.',
31+
23,
32+
],
33+
[
34+
'Only numeric types are allowed in post-decrement, stdClass given.',
35+
24,
36+
],
37+
[
38+
'Only numeric types are allowed in post-decrement, int|stdClass|string given.',
39+
26,
40+
],
41+
];
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandInArithmeticPostIncrementRuleTest extends OperandInArithmeticIncrementOrDecrementRuleTest
8+
{
9+
10+
protected function createRule(OperatorRuleHelper $helper): Rule
11+
{
12+
return new OperandInArithmeticPostIncrementRule($helper);
13+
}
14+
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
protected function getExpectedErrors(): array
19+
{
20+
return [
21+
[
22+
'Only numeric types are allowed in post-increment, false given.',
23+
32,
24+
],
25+
[
26+
'Only numeric types are allowed in post-increment, string given.',
27+
33,
28+
],
29+
[
30+
'Only numeric types are allowed in post-increment, null given.',
31+
34,
32+
],
33+
[
34+
'Only numeric types are allowed in post-increment, stdClass given.',
35+
35,
36+
],
37+
[
38+
'Only numeric types are allowed in post-increment, int|stdClass|string given.',
39+
37,
40+
],
41+
];
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandInArithmeticPreDecrementRuleTest extends OperandInArithmeticIncrementOrDecrementRuleTest
8+
{
9+
10+
protected function createRule(OperatorRuleHelper $helper): Rule
11+
{
12+
return new OperandInArithmeticPreDecrementRule($helper);
13+
}
14+
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
protected function getExpectedErrors(): array
19+
{
20+
return [
21+
[
22+
'Only numeric types are allowed in pre-decrement, false given.',
23+
43,
24+
],
25+
[
26+
'Only numeric types are allowed in pre-decrement, string given.',
27+
44,
28+
],
29+
[
30+
'Only numeric types are allowed in pre-decrement, null given.',
31+
45,
32+
],
33+
[
34+
'Only numeric types are allowed in pre-decrement, stdClass given.',
35+
46,
36+
],
37+
[
38+
'Only numeric types are allowed in pre-decrement, int|stdClass|string given.',
39+
48,
40+
],
41+
];
42+
}
43+
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Operators;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
class OperandInArithmeticPreIncrementRuleTest extends OperandInArithmeticIncrementOrDecrementRuleTest
8+
{
9+
10+
protected function createRule(OperatorRuleHelper $helper): Rule
11+
{
12+
return new OperandInArithmeticPreIncrementRule($helper);
13+
}
14+
15+
/**
16+
* {@inheritdoc}
17+
*/
18+
protected function getExpectedErrors(): array
19+
{
20+
return [
21+
[
22+
'Only numeric types are allowed in pre-increment, false given.',
23+
54,
24+
],
25+
[
26+
'Only numeric types are allowed in pre-increment, string given.',
27+
55,
28+
],
29+
[
30+
'Only numeric types are allowed in pre-increment, null given.',
31+
56,
32+
],
33+
[
34+
'Only numeric types are allowed in pre-increment, stdClass given.',
35+
57,
36+
],
37+
[
38+
'Only numeric types are allowed in pre-increment, int|stdClass|string given.',
39+
59,
40+
],
41+
];
42+
}
43+
44+
}

0 commit comments

Comments
 (0)