diff --git a/Makefile b/Makefile index 1122b4ab0f..11f34d26cd 100644 --- a/Makefile +++ b/Makefile @@ -107,6 +107,7 @@ lint: --exclude tests/PHPStan/Rules/Properties/data/final-property-hooks.php \ --exclude tests/PHPStan/Rules/Properties/data/final-properties.php \ --exclude tests/PHPStan/Rules/Properties/data/property-in-interface-explicit-abstract.php \ + --exclude tests/PHPStan/Rules/Constants/data/final-private-const.php \ src tests cs: diff --git a/conf/config.level0.neon b/conf/config.level0.neon index 9160281651..b5b1f2f793 100644 --- a/conf/config.level0.neon +++ b/conf/config.level0.neon @@ -46,6 +46,7 @@ rules: - PHPStan\Rules\Constants\FinalConstantRule - PHPStan\Rules\Constants\MagicConstantContextRule - PHPStan\Rules\Constants\NativeTypedClassConstantRule + - PHPStan\Rules\Constants\FinalPrivateConstantRule - PHPStan\Rules\EnumCases\EnumCaseAttributesRule - PHPStan\Rules\Exceptions\NoncapturingCatchRule - PHPStan\Rules\Exceptions\ThrowExpressionRule diff --git a/src/Rules/Constants/FinalPrivateConstantRule.php b/src/Rules/Constants/FinalPrivateConstantRule.php new file mode 100644 index 0000000000..2be7d51165 --- /dev/null +++ b/src/Rules/Constants/FinalPrivateConstantRule.php @@ -0,0 +1,49 @@ + */ +final class FinalPrivateConstantRule implements Rule +{ + + public function getNodeType(): string + { + return ClassConst::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$scope->isInClass()) { + throw new ShouldNotHappenException(); + } + $classReflection = $scope->getClassReflection(); + + if (!$node->isFinal()) { + return []; + } + + if (!$node->isPrivate()) { + return []; + } + + $errors = []; + foreach ($node->consts as $classConstNode) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Private constant %s::%s() cannot be final as it is never overridden by other classes.', + $classReflection->getDisplayName(), + $classConstNode->name->name, + ))->identifier('classConstant.finalPrivate')->nonIgnorable()->build(); + } + + return $errors; + } + +} diff --git a/tests/PHPStan/Rules/Constants/FinalPrivateConstantRuleTest.php b/tests/PHPStan/Rules/Constants/FinalPrivateConstantRuleTest.php new file mode 100644 index 0000000000..8e98e04565 --- /dev/null +++ b/tests/PHPStan/Rules/Constants/FinalPrivateConstantRuleTest.php @@ -0,0 +1,27 @@ + */ +class FinalPrivateConstantRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new FinalPrivateConstantRule(); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/final-private-const.php'], [ + [ + 'Private constant FinalPrivateConstants\User::FINAL_PRIVATE() cannot be final as it is never overridden by other classes.', + 8, + ], + ]); + } + +} diff --git a/tests/PHPStan/Rules/Constants/data/final-private-const.php b/tests/PHPStan/Rules/Constants/data/final-private-const.php new file mode 100644 index 0000000000..23c21b1271 --- /dev/null +++ b/tests/PHPStan/Rules/Constants/data/final-private-const.php @@ -0,0 +1,11 @@ +