diff --git a/config.xsd b/config.xsd
index de492fcb1da..211e6e151ab 100644
--- a/config.xsd
+++ b/config.xsd
@@ -230,6 +230,7 @@
+
diff --git a/docs/running_psalm/error_levels.md b/docs/running_psalm/error_levels.md
index 35c4821d63b..9bb001277c3 100644
--- a/docs/running_psalm/error_levels.md
+++ b/docs/running_psalm/error_levels.md
@@ -29,6 +29,7 @@ Level 5 and above allows a more non-verifiable code, and higher levels are even
- [DuplicateFunction](issues/DuplicateFunction.md)
- [DuplicateMethod](issues/DuplicateMethod.md)
- [DuplicateParam](issues/DuplicateParam.md)
+ - [DuplicateProperty](issues/DuplicateProperty.md)
- [EmptyArrayAccess](issues/EmptyArrayAccess.md)
- [ExtensionRequirementViolation](issues/ExtensionRequirementViolation.md)
- [ImplementationRequirementViolation](issues/ImplementationRequirementViolation.md)
diff --git a/docs/running_psalm/issues.md b/docs/running_psalm/issues.md
index c14cecd1451..f2655635cf1 100644
--- a/docs/running_psalm/issues.md
+++ b/docs/running_psalm/issues.md
@@ -31,6 +31,7 @@
- [DuplicateFunction](issues/DuplicateFunction.md)
- [DuplicateMethod](issues/DuplicateMethod.md)
- [DuplicateParam](issues/DuplicateParam.md)
+ - [DuplicateProperty](issues/DuplicateProperty.md)
- [EmptyArrayAccess](issues/EmptyArrayAccess.md)
- [ExtensionRequirementViolation](issues/ExtensionRequirementViolation.md)
- [FalsableReturnStatement](issues/FalsableReturnStatement.md)
diff --git a/docs/running_psalm/issues/DuplicateProperty.md b/docs/running_psalm/issues/DuplicateProperty.md
new file mode 100644
index 00000000000..1a7cf0e6e59
--- /dev/null
+++ b/docs/running_psalm/issues/DuplicateProperty.md
@@ -0,0 +1,19 @@
+# DuplicateProperty
+
+Emitted when a class property is defined twice
+
+```php
+props as $property) {
$doc_var_location = null;
+ if (isset($storage->properties[$property->name->name])) {
+ IssueBuffer::maybeAdd(
+ new DuplicateProperty(
+ 'Property ' . $fq_classlike_name . '::$' . $property->name->name . ' has already been defined',
+ new CodeLocation($this->file_scanner, $stmt, null, true),
+ $fq_classlike_name . '::$' . $property->name->name,
+ ),
+ );
+ }
+
$property_storage = $storage->properties[$property->name->name] = new PropertyStorage();
$property_storage->is_static = $stmt->isStatic();
$property_storage->type = $signature_type;
diff --git a/src/Psalm/Issue/DuplicateProperty.php b/src/Psalm/Issue/DuplicateProperty.php
new file mode 100644
index 00000000000..89538730a7d
--- /dev/null
+++ b/src/Psalm/Issue/DuplicateProperty.php
@@ -0,0 +1,9 @@
+ 'InheritorViolation',
'ignored_issues' => [],
],
+ 'duplicateInstanceProperties' => [
+ 'code' => <<<'PHP'
+ 'DuplicateProperty',
+ 'ignored_issues' => [],
+ ],
+ 'duplicateStaticProperties' => [
+ 'code' => <<<'PHP'
+ 'DuplicateProperty',
+ 'ignored_issues' => [],
+ ],
+ 'duplicateMixedProperties' => [
+ 'code' => <<<'PHP'
+ 'DuplicateProperty',
+ 'ignored_issues' => [],
+ ],
+ 'duplicatePropertiesDifferentVisibility' => [
+ 'code' => <<<'PHP'
+ 'DuplicateProperty',
+ 'ignored_issues' => [],
+ ],
];
}
}
diff --git a/tests/TraitTest.php b/tests/TraitTest.php
index bd53bd603c8..4b04b7c4e31 100644
--- a/tests/TraitTest.php
+++ b/tests/TraitTest.php
@@ -1236,6 +1236,15 @@ trait A { const B = 0; }
'ignored_issues' => [],
'php_version' => '8.1',
],
+ 'duplicateTraitProperty' => [
+ 'code' => ' 'DuplicateProperty',
+ ],
];
}
}