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 @@
+