diff --git a/rules.neon b/rules.neon index 4a326a62..5ad15508 100644 --- a/rules.neon +++ b/rules.neon @@ -41,7 +41,14 @@ rules: - PHPStan\Rules\VariableVariables\VariableStaticPropertyFetchRule - PHPStan\Rules\VariableVariables\VariableVariablesRule +conditionalTags: + PHPStan\Rules\Operators\OperandsInTernaryOperatorRule: + phpstan.rules.rule: %featureToggles.bleedingEdge% + services: + - + class: PHPStan\Rules\Operators\OperandsInTernaryOperatorRule + - class: PHPStan\Rules\BooleansInConditions\BooleanRuleHelper diff --git a/src/Rules/Operators/OperandsInTernaryOperatorRule.php b/src/Rules/Operators/OperandsInTernaryOperatorRule.php new file mode 100644 index 00000000..d5b9a66f --- /dev/null +++ b/src/Rules/Operators/OperandsInTernaryOperatorRule.php @@ -0,0 +1,39 @@ + + */ +class OperandsInTernaryOperatorRule implements Rule +{ + + public function getNodeType(): string + { + return Ternary::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $ifType = $node->if === null ? $scope->getType($node->cond) : $scope->getType($node->if); + $elseType = $scope->getType($node->else); + if ($ifType instanceof ConstantType && $elseType instanceof ConstantType) { + if ($ifType->equals($elseType)) { + return [sprintf( + 'If and else parts of ternary operator are equal (%s).', + $ifType->describe(VerbosityLevel::value()) + )]; + } + } + + return []; + } + +} diff --git a/tests/Rules/Operators/OperandsInTernaryOperatorRuleTest.php b/tests/Rules/Operators/OperandsInTernaryOperatorRuleTest.php new file mode 100644 index 00000000..8c365f22 --- /dev/null +++ b/tests/Rules/Operators/OperandsInTernaryOperatorRuleTest.php @@ -0,0 +1,44 @@ + + */ +class OperandsInTernaryOperatorRuleTest extends \PHPStan\Testing\RuleTestCase +{ + + protected function getRule(): Rule + { + return new OperandsInTernaryOperatorRule(); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/operators.php'], [ + [ + 'If and else parts of ternary operator are equal (true).', + 113, + ], + [ + 'If and else parts of ternary operator are equal (array()).', + 118, + ], + [ + 'If and else parts of ternary operator are equal (\'string_val\').', + 119, + ], + [ + 'If and else parts of ternary operator are equal (array(23, 24)).', + 121, + ], + [ + 'If and else parts of ternary operator are equal (array(1 => 1)).', + 122, + ], + ]); + } + +} diff --git a/tests/Rules/Operators/data/operators.php b/tests/Rules/Operators/data/operators.php index 4a4bf9e2..0a69f1b6 100644 --- a/tests/Rules/Operators/data/operators.php +++ b/tests/Rules/Operators/data/operators.php @@ -109,3 +109,18 @@ function (array $array, int $int, $mixed) { explode($mixed, $mixed) + $int; }; + +foo() ? true : true; +foo() ? true : null; +foo() ? $object : true; +foo() ? $object : false; +foo() ? $object : null; +foo() ? [] : []; +foo() ? 'string_val' : 'string_val'; +foo() ? [23, 24] : [23, 25]; +foo() ? [23, 24] : [23, 24]; +foo() ? [1 => 1] : ['1' => 1]; +foo() ? [1 => 1] : ['asd' => 1]; +$object ?: $object; +foo() ? false : true; +foo() ? true : false;