From e92203ca72ac6136f473c8cbc2b14a05b25f2d64 Mon Sep 17 00:00:00 2001 From: BackEndTea Date: Mon, 30 Dec 2019 19:02:03 +0100 Subject: [PATCH] Add InvalidNullCoalesce as level 4 rule --- conf/config.level4.neon | 1 + src/Rules/Comparison/InvalidNullCoalesce.php | 49 +++++++++++++++++ .../Comparison/InvalidNullCoalesceTest.php | 52 +++++++++++++++++++ .../Comparison/data/invalid-null-coalesce.php | 40 ++++++++++++++ 4 files changed, 142 insertions(+) create mode 100644 src/Rules/Comparison/InvalidNullCoalesce.php create mode 100644 tests/PHPStan/Rules/Comparison/InvalidNullCoalesceTest.php create mode 100644 tests/PHPStan/Rules/Comparison/data/invalid-null-coalesce.php diff --git a/conf/config.level4.neon b/conf/config.level4.neon index a01448efb8..3e646f2f75 100644 --- a/conf/config.level4.neon +++ b/conf/config.level4.neon @@ -8,6 +8,7 @@ rules: - PHPStan\Rules\Comparison\BooleanOrConstantConditionRule - PHPStan\Rules\Comparison\ElseIfConstantConditionRule - PHPStan\Rules\Comparison\IfConstantConditionRule + - PHPStan\Rules\Comparison\InvalidNullCoalesce - PHPStan\Rules\Comparison\TernaryOperatorConstantConditionRule - PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule - PHPStan\Rules\Comparison\UnreachableIfBranchesRule diff --git a/src/Rules/Comparison/InvalidNullCoalesce.php b/src/Rules/Comparison/InvalidNullCoalesce.php new file mode 100644 index 0000000000..79a89bb17d --- /dev/null +++ b/src/Rules/Comparison/InvalidNullCoalesce.php @@ -0,0 +1,49 @@ + + */ +class InvalidNullCoalesce implements Rule +{ + + /** + * @phpstan-return class-string<\PhpParser\Node\Expr\BinaryOp\Coalesce> + * @return string + */ + public function getNodeType(): string + { + return Node\Expr\BinaryOp\Coalesce::class; + } + + /** + * @phpstan-param \PhpParser\Node\Expr\BinaryOp\Coalesce $node + * @param \PhpParser\Node $node + * @param \PHPStan\Analyser\Scope $scope + * @return RuleError[] errors + */ + public function processNode(Node $node, Scope $scope): array + { + $type = $scope->getType($node->left); + $onlyNull = $type instanceof NullType; + if($onlyNull || $type->accepts(new NullType(), $scope->isDeclareStrictTypes())->no()) { + return [ + RuleErrorBuilder::message(sprintf( + 'Null coalesce on type %s is always %s.', + $type->describe(VerbosityLevel::value()), + $onlyNull ? 'true' : 'false' + ))->line($node->left->getLine())->build(), + ]; + } + return []; + } +} diff --git a/tests/PHPStan/Rules/Comparison/InvalidNullCoalesceTest.php b/tests/PHPStan/Rules/Comparison/InvalidNullCoalesceTest.php new file mode 100644 index 0000000000..1c05d5faba --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/InvalidNullCoalesceTest.php @@ -0,0 +1,52 @@ + + */ +class InvalidNullCoalesceTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new InvalidNullCoalesce(); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/invalid-null-coalesce.php'], [ + [ + 'Null coalesce on type array is always false.', + 10, + ], + [ + 'Null coalesce on type int is always false.', + 11, + ], + [ + 'Null coalesce on type bool is always false.', + 12, + ], + [ + 'Null coalesce on type string is always false.', + 16, + ], + [ + 'Null coalesce on type int|string is always false.', + 27, + ], + [ + 'Null coalesce on type int|false is always false.', + 36, + ], + [ + 'Null coalesce on type null is always true.', + 38, + ], + ]); + } +} diff --git a/tests/PHPStan/Rules/Comparison/data/invalid-null-coalesce.php b/tests/PHPStan/Rules/Comparison/data/invalid-null-coalesce.php new file mode 100644 index 0000000000..116335d871 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/invalid-null-coalesce.php @@ -0,0 +1,40 @@ +