Skip to content

Commit

Permalink
Strict operator type check for assign operations
Browse files Browse the repository at this point in the history
  • Loading branch information
kamil-zacek authored and ondrejmirtes committed Feb 21, 2023
1 parent 66b378f commit b7dd96a
Show file tree
Hide file tree
Showing 15 changed files with 317 additions and 90 deletions.
30 changes: 0 additions & 30 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -107,61 +107,31 @@ parameters:
count: 1
path: src/Rules/Operators/OperandsInArithmeticAdditionRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticAdditionRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticAdditionRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticDivisionRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticDivisionRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticDivisionRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticDivisionRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticExponentiationRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticExponentiationRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticExponentiationRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticExponentiationRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticModuloRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticModuloRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticModuloRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticModuloRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticMultiplicationRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticMultiplicationRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticMultiplicationRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticSubtractionRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticSubtractionRule.php

-
message: "#^Parameter \\#1 \\$node \\(PhpParser\\\\Node\\\\Expr\\\\BinaryOp\\\\BooleanAnd\\) of method PHPStan\\\\Rules\\\\Operators\\\\OperandsInArithmeticSubtractionRule\\:\\:processNode\\(\\) should be contravariant with parameter \\$node \\(PhpParser\\\\Node\\) of method PHPStan\\\\Rules\\\\Rule\\<PhpParser\\\\Node\\>\\:\\:processNode\\(\\)$#"
count: 1
path: src/Rules/Operators/OperandsInArithmeticSubtractionRule.php

-
message: "#^Class PHPStan\\\\Rules\\\\StrictCalls\\\\DynamicCallOnStaticMethodsRule implements generic interface PHPStan\\\\Rules\\\\Rule but does not specify its types\\: TNodeType$#"
count: 1
Expand Down
12 changes: 12 additions & 0 deletions rules.neon
Original file line number Diff line number Diff line change
Expand Up @@ -204,21 +204,33 @@ services:

-
class: PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\Operators\OperandsInArithmeticExponentiationRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\Operators\OperandsInArithmeticModuloRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\Operators\OperandsInArithmeticMultiplicationRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\Operators\OperandsInArithmeticSubtractionRule
arguments:
bleedingEdge: %featureToggles.bleedingEdge%

-
class: PHPStan\Rules\StrictCalls\DynamicCallOnStaticMethodsRule
Expand Down
32 changes: 23 additions & 9 deletions src/Rules/Operators/OperandsInArithmeticAdditionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace PHPStan\Rules\Operators;

use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Plus;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp\Plus as AssignOpPlus;
use PhpParser\Node\Expr\BinaryOp\Plus as BinaryOpPlus;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -17,36 +18,49 @@ class OperandsInArithmeticAdditionRule implements Rule
/** @var OperatorRuleHelper */
private $helper;

public function __construct(OperatorRuleHelper $helper)
/** @var bool */
private $bleedingEdge;

public function __construct(OperatorRuleHelper $helper, bool $bleedingEdge)
{
$this->helper = $helper;
$this->bleedingEdge = $bleedingEdge;
}

public function getNodeType(): string
{
return Plus::class;
return Expr::class;
}

/**
* @param BooleanAnd $node
* @return string[] errors
*/
public function processNode(Node $node, Scope $scope): array
{
$leftType = $scope->getType($node->left);
$rightType = $scope->getType($node->right);
if ($node instanceof BinaryOpPlus) {
$left = $node->left;
$right = $node->right;
} elseif ($node instanceof AssignOpPlus && $this->bleedingEdge) {
$left = $node->var;
$right = $node->expr;
} else {
return [];
}

$leftType = $scope->getType($left);
$rightType = $scope->getType($right);
if (count($leftType->getArrays()) > 0 && count($rightType->getArrays()) > 0) {
return [];
}

$messages = [];
if (!$this->helper->isValidForArithmeticOperation($scope, $node->left)) {
if (!$this->helper->isValidForArithmeticOperation($scope, $left)) {
$messages[] = sprintf(
'Only numeric types are allowed in +, %s given on the left side.',
$leftType->describe(VerbosityLevel::typeOnly())
);
}
if (!$this->helper->isValidForArithmeticOperation($scope, $node->right)) {
if (!$this->helper->isValidForArithmeticOperation($scope, $right)) {
$messages[] = sprintf(
'Only numeric types are allowed in +, %s given on the right side.',
$rightType->describe(VerbosityLevel::typeOnly())
Expand Down
32 changes: 23 additions & 9 deletions src/Rules/Operators/OperandsInArithmeticDivisionRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace PHPStan\Rules\Operators;

use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Div;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp\Div as AssignOpDiv;
use PhpParser\Node\Expr\BinaryOp\Div as BinaryOpDiv;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -16,33 +17,46 @@ class OperandsInArithmeticDivisionRule implements Rule
/** @var OperatorRuleHelper */
private $helper;

public function __construct(OperatorRuleHelper $helper)
/** @var bool */
private $bleedingEdge;

public function __construct(OperatorRuleHelper $helper, bool $bleedingEdge)
{
$this->helper = $helper;
$this->bleedingEdge = $bleedingEdge;
}

public function getNodeType(): string
{
return Div::class;
return Expr::class;
}

/**
* @param BooleanAnd $node
* @return string[] errors
*/
public function processNode(Node $node, Scope $scope): array
{
if ($node instanceof BinaryOpDiv) {
$left = $node->left;
$right = $node->right;
} elseif ($node instanceof AssignOpDiv && $this->bleedingEdge) {
$left = $node->var;
$right = $node->expr;
} else {
return [];
}

$messages = [];
$leftType = $scope->getType($node->left);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->left)) {
$leftType = $scope->getType($left);
if (!$this->helper->isValidForArithmeticOperation($scope, $left)) {
$messages[] = sprintf(
'Only numeric types are allowed in /, %s given on the left side.',
$leftType->describe(VerbosityLevel::typeOnly())
);
}

$rightType = $scope->getType($node->right);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->right)) {
$rightType = $scope->getType($right);
if (!$this->helper->isValidForArithmeticOperation($scope, $right)) {
$messages[] = sprintf(
'Only numeric types are allowed in /, %s given on the right side.',
$rightType->describe(VerbosityLevel::typeOnly())
Expand Down
32 changes: 23 additions & 9 deletions src/Rules/Operators/OperandsInArithmeticExponentiationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace PHPStan\Rules\Operators;

use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Pow;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp\Pow as AssignOpPow;
use PhpParser\Node\Expr\BinaryOp\Pow as BinaryOpPow;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -16,33 +17,46 @@ class OperandsInArithmeticExponentiationRule implements Rule
/** @var OperatorRuleHelper */
private $helper;

public function __construct(OperatorRuleHelper $helper)
/** @var bool */
private $bleedingEdge;

public function __construct(OperatorRuleHelper $helper, bool $bleedingEdge)
{
$this->helper = $helper;
$this->bleedingEdge = $bleedingEdge;
}

public function getNodeType(): string
{
return Pow::class;
return Expr::class;
}

/**
* @param BooleanAnd $node
* @return string[] errors
*/
public function processNode(Node $node, Scope $scope): array
{
if ($node instanceof BinaryOpPow) {
$left = $node->left;
$right = $node->right;
} elseif ($node instanceof AssignOpPow && $this->bleedingEdge) {
$left = $node->var;
$right = $node->expr;
} else {
return [];
}

$messages = [];
$leftType = $scope->getType($node->left);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->left)) {
$leftType = $scope->getType($left);
if (!$this->helper->isValidForArithmeticOperation($scope, $left)) {
$messages[] = sprintf(
'Only numeric types are allowed in **, %s given on the left side.',
$leftType->describe(VerbosityLevel::typeOnly())
);
}

$rightType = $scope->getType($node->right);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->right)) {
$rightType = $scope->getType($right);
if (!$this->helper->isValidForArithmeticOperation($scope, $right)) {
$messages[] = sprintf(
'Only numeric types are allowed in **, %s given on the right side.',
$rightType->describe(VerbosityLevel::typeOnly())
Expand Down
32 changes: 23 additions & 9 deletions src/Rules/Operators/OperandsInArithmeticModuloRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
namespace PHPStan\Rules\Operators;

use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Mod;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\AssignOp\Mod as AssignOpMod;
use PhpParser\Node\Expr\BinaryOp\Mod as BinaryOpMod;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\VerbosityLevel;
Expand All @@ -16,33 +17,46 @@ class OperandsInArithmeticModuloRule implements Rule
/** @var OperatorRuleHelper */
private $helper;

public function __construct(OperatorRuleHelper $helper)
/** @var bool */
private $bleedingEdge;

public function __construct(OperatorRuleHelper $helper, bool $bleedingEdge)
{
$this->helper = $helper;
$this->bleedingEdge = $bleedingEdge;
}

public function getNodeType(): string
{
return Mod::class;
return Expr::class;
}

/**
* @param BooleanAnd $node
* @return string[] errors
*/
public function processNode(Node $node, Scope $scope): array
{
if ($node instanceof BinaryOpMod) {
$left = $node->left;
$right = $node->right;
} elseif ($node instanceof AssignOpMod && $this->bleedingEdge) {
$left = $node->var;
$right = $node->expr;
} else {
return [];
}

$messages = [];
$leftType = $scope->getType($node->left);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->left)) {
$leftType = $scope->getType($left);
if (!$this->helper->isValidForArithmeticOperation($scope, $left)) {
$messages[] = sprintf(
'Only numeric types are allowed in %%, %s given on the left side.',
$leftType->describe(VerbosityLevel::typeOnly())
);
}

$rightType = $scope->getType($node->right);
if (!$this->helper->isValidForArithmeticOperation($scope, $node->right)) {
$rightType = $scope->getType($right);
if (!$this->helper->isValidForArithmeticOperation($scope, $right)) {
$messages[] = sprintf(
'Only numeric types are allowed in %%, %s given on the right side.',
$rightType->describe(VerbosityLevel::typeOnly())
Expand Down
Loading

0 comments on commit b7dd96a

Please sign in to comment.