From 5e2ba1a2649681eaf2d878f77a7704b90c9754b3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 18 Jun 2020 13:38:17 +0100 Subject: [PATCH] implemented DuplicateDeclarationRule, fixes phpstan/phpstan#3475 --- build.xml | 2 + conf/config.level0.neon | 1 + .../Classes/DuplicateDeclarationRule.php | 79 +++++++++++++++++++ .../Classes/DuplicateDeclarationRuleTest.php | 58 ++++++++++++++ .../Classes/data/duplicate-declarations.php | 36 +++++++++ 5 files changed, 176 insertions(+) create mode 100644 src/Rules/Classes/DuplicateDeclarationRule.php create mode 100644 tests/PHPStan/Rules/Classes/DuplicateDeclarationRuleTest.php create mode 100644 tests/PHPStan/Rules/Classes/data/duplicate-declarations.php diff --git a/build.xml b/build.xml index 3fd9b25d4a..6a163cbbdd 100644 --- a/build.xml +++ b/build.xml @@ -134,6 +134,8 @@ + + diff --git a/conf/config.level0.neon b/conf/config.level0.neon index a9ec38d8af..e7396651e3 100644 --- a/conf/config.level0.neon +++ b/conf/config.level0.neon @@ -16,6 +16,7 @@ rules: - PHPStan\Rules\Arrays\OffsetAccessWithoutDimForReadingRule - PHPStan\Rules\Classes\ClassConstantDeclarationRule - PHPStan\Rules\Classes\ClassConstantRule + - PHPStan\Rules\Classes\DuplicateDeclarationRule - PHPStan\Rules\Classes\ExistingClassesInClassImplementsRule - PHPStan\Rules\Classes\ExistingClassesInInterfaceExtendsRule - PHPStan\Rules\Classes\ExistingClassInClassExtendsRule diff --git a/src/Rules/Classes/DuplicateDeclarationRule.php b/src/Rules/Classes/DuplicateDeclarationRule.php new file mode 100644 index 0000000000..3e84f48fdf --- /dev/null +++ b/src/Rules/Classes/DuplicateDeclarationRule.php @@ -0,0 +1,79 @@ + + */ +class DuplicateDeclarationRule implements \PHPStan\Rules\Rule +{ + + public function getNodeType(): string + { + return InClassNode::class; + } + + public function processNode(Node $node, Scope $scope): array + { + $classReflection = $scope->getClassReflection(); + if ($classReflection === null) { + throw new \PHPStan\ShouldNotHappenException(); + } + + $errors = []; + + $declaredClassConstants = []; + foreach ($node->getOriginalNode()->getConstants() as $constDecl) { + foreach ($constDecl->consts as $const) { + if (array_key_exists($const->name->name, $declaredClassConstants)) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Cannot redeclare constant %s::%s.', + $classReflection->getDisplayName(), + $const->name->name + ))->line($const->getLine())->nonIgnorable()->build(); + } else { + $declaredClassConstants[$const->name->name] = true; + } + } + } + + $declaredProperties = []; + foreach ($node->getOriginalNode()->getProperties() as $propertyDecl) { + foreach ($propertyDecl->props as $property) { + if (array_key_exists($property->name->name, $declaredProperties)) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Cannot redeclare property %s::$%s.', + $classReflection->getDisplayName(), + $property->name->name + ))->line($property->getLine())->nonIgnorable()->build(); + } else { + $declaredProperties[$property->name->name] = true; + } + } + } + + $declaredFunctions = []; + foreach ($node->getOriginalNode()->getMethods() as $method) { + if (array_key_exists(strtolower($method->name->name), $declaredFunctions)) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Cannot redeclare method %s::%s().', + $classReflection->getDisplayName(), + $method->name->name + ))->line($method->getStartLine())->nonIgnorable()->build(); + } else { + $declaredFunctions[strtolower($method->name->name)] = true; + } + } + + return $errors; + } + +} diff --git a/tests/PHPStan/Rules/Classes/DuplicateDeclarationRuleTest.php b/tests/PHPStan/Rules/Classes/DuplicateDeclarationRuleTest.php new file mode 100644 index 0000000000..85b0f532ec --- /dev/null +++ b/tests/PHPStan/Rules/Classes/DuplicateDeclarationRuleTest.php @@ -0,0 +1,58 @@ + + */ +class DuplicateDeclarationRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new DuplicateDeclarationRule(); + } + + public function testDuplicateDeclarations(): void + { + if (!self::$useStaticReflectionProvider) { + $this->markTestSkipped('This test needs static reflection'); + } + + $this->analyse( + [ + __DIR__ . '/data/duplicate-declarations.php', + ], + [ + [ + 'Cannot redeclare constant DuplicateDeclarations\Foo::CONST1.', + 8, + ], + [ + 'Cannot redeclare constant DuplicateDeclarations\Foo::CONST2.', + 10, + ], + [ + 'Cannot redeclare property DuplicateDeclarations\Foo::$prop1.', + 17, + ], + [ + 'Cannot redeclare property DuplicateDeclarations\Foo::$prop2.', + 20, + ], + [ + 'Cannot redeclare method DuplicateDeclarations\Foo::func1().', + 27, + ], + [ + 'Cannot redeclare method DuplicateDeclarations\Foo::Func1().', + 35, + ], + ] + ); + } + +} diff --git a/tests/PHPStan/Rules/Classes/data/duplicate-declarations.php b/tests/PHPStan/Rules/Classes/data/duplicate-declarations.php new file mode 100644 index 0000000000..7deff4c5e3 --- /dev/null +++ b/tests/PHPStan/Rules/Classes/data/duplicate-declarations.php @@ -0,0 +1,36 @@ +