diff --git a/config/set/level/up-to-php83.php b/config/set/level/up-to-php83.php
new file mode 100644
index 00000000000..455663aa423
--- /dev/null
+++ b/config/set/level/up-to-php83.php
@@ -0,0 +1,14 @@
+sets([SetList::PHP_83, LevelSetList::UP_TO_PHP_82]);
+ // parameter must be defined after import, to override imported param version
+ $rectorConfig->phpVersion(PhpVersion::PHP_83);
+};
diff --git a/config/set/php83.php b/config/set/php83.php
new file mode 100644
index 00000000000..82dae290104
--- /dev/null
+++ b/config/set/php83.php
@@ -0,0 +1,10 @@
+rules([AddOverrideAttributeToOverriddenMethodsRector::class]);
+};
diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md
index 3f6fa376392..7b136074996 100644
--- a/docs/rector_rules_overview.md
+++ b/docs/rector_rules_overview.md
@@ -1,4 +1,4 @@
-# 355 Rules Overview
+# 356 Rules Overview
@@ -44,6 +44,8 @@
- [Php82](#php82) (4)
+- [Php83](#php83) (1)
+
- [Privatization](#privatization) (4)
- [Removing](#removing) (5)
@@ -5314,6 +5316,33 @@ Change deprecated utf8_decode and utf8_encode to mb_convert_encoding
+## Php83
+
+### AddOverrideAttributeToOverriddenMethodsRector
+
+Add override attribute to overridden methods
+
+- class: [`Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector`](../rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php)
+
+```diff
+ class ParentClass
+ {
+ public function foo()
+ {
+ }
+ }
+
+ class ChildClass extends ParentClass
+ {
++ #[\Override]
+ public function foo()
+ {
+ }
+ }
+```
+
+
+
## Privatization
### FinalizeClassesWithoutChildrenRector
diff --git a/packages/Set/ValueObject/LevelSetList.php b/packages/Set/ValueObject/LevelSetList.php
index 88341fc7d02..06b7ebae311 100644
--- a/packages/Set/ValueObject/LevelSetList.php
+++ b/packages/Set/ValueObject/LevelSetList.php
@@ -9,6 +9,10 @@ use Rector\Set\Contract\SetListInterface;
*/
final class LevelSetList implements SetListInterface
{
+ /**
+ * @var string
+ */
+ public const UP_TO_PHP_83 = __DIR__ . '/../../../config/set/level/up-to-php83.php';
/**
* @var string
*/
diff --git a/packages/Set/ValueObject/SetList.php b/packages/Set/ValueObject/SetList.php
index 0e3a9be33a0..cae7589229c 100644
--- a/packages/Set/ValueObject/SetList.php
+++ b/packages/Set/ValueObject/SetList.php
@@ -85,6 +85,10 @@ final class SetList implements SetListInterface
* @var string
*/
public const PHP_82 = __DIR__ . '/../../../config/set/php82.php';
+ /**
+ * @var string
+ */
+ public const PHP_83 = __DIR__ . '/../../../config/set/php83.php';
/**
* @var string
*/
diff --git a/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php
new file mode 100644
index 00000000000..c1d8a35402c
--- /dev/null
+++ b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php
@@ -0,0 +1,138 @@
+reflectionProvider = $reflectionProvider;
+ $this->classAnalyzer = $classAnalyzer;
+ $this->phpAttributeAnalyzer = $phpAttributeAnalyzer;
+ }
+ public function getRuleDefinition() : RuleDefinition
+ {
+ return new RuleDefinition('Add override attribute to overridden methods', [new CodeSample(<<<'CODE_SAMPLE'
+class ParentClass
+{
+ public function foo()
+ {
+ }
+}
+
+class ChildClass extends ParentClass
+{
+ public function foo()
+ {
+ }
+}
+CODE_SAMPLE
+, <<<'CODE_SAMPLE'
+class ParentClass
+{
+ public function foo()
+ {
+ }
+}
+
+class ChildClass extends ParentClass
+{
+ #[\Override]
+ public function foo()
+ {
+ }
+}
+CODE_SAMPLE
+)]);
+ }
+ /**
+ * @return array>
+ */
+ public function getNodeTypes() : array
+ {
+ return [Class_::class];
+ }
+ /**
+ * @param Class_ $node
+ */
+ public function refactor(Node $node) : ?Node
+ {
+ // Detect if class extends a parent class
+ if ($this->shouldSkipClass($node)) {
+ return null;
+ }
+ // Fetch the parent class reflection
+ $parentClassReflection = $this->reflectionProvider->getClass((string) $node->extends);
+ $hasChanged = \false;
+ foreach ($node->getMethods() as $method) {
+ if ($method->name->toString() === '__construct') {
+ continue;
+ }
+ // Private methods should be ignored
+ if ($parentClassReflection->hasNativeMethod($method->name->toString())) {
+ // ignore if it is a private method on the parent
+ $parentMethod = $parentClassReflection->getNativeMethod($method->name->toString());
+ if ($parentMethod->isPrivate()) {
+ continue;
+ }
+ // ignore if it already uses the attribute
+ if ($this->phpAttributeAnalyzer->hasPhpAttribute($method, 'Override')) {
+ continue;
+ }
+ $method->attrGroups[] = new AttributeGroup([new Attribute(new FullyQualified('Override'))]);
+ $hasChanged = \true;
+ }
+ }
+ if (!$hasChanged) {
+ return null;
+ }
+ return $node;
+ }
+ public function provideMinPhpVersion() : int
+ {
+ return PhpVersionFeature::OVERRIDE_ATTRIBUTE;
+ }
+ private function shouldSkipClass(Class_ $class) : bool
+ {
+ if ($this->classAnalyzer->isAnonymousClass($class)) {
+ return \true;
+ }
+ if (!$class->extends instanceof FullyQualified) {
+ return \true;
+ }
+ return !$this->reflectionProvider->hasClass($class->extends->toString());
+ }
+}
diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php
index 621e9c582dd..bb76e01dd2d 100644
--- a/src/Application/VersionResolver.php
+++ b/src/Application/VersionResolver.php
@@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
- public const PACKAGE_VERSION = 'cc97becc06dcf1b69c5de01aebf09b498c0d9006';
+ public const PACKAGE_VERSION = '2ad03db37f187d715c3a2f189dc181063126e596';
/**
* @api
* @var string
*/
- public const RELEASE_DATE = '2023-11-03 13:00:11';
+ public const RELEASE_DATE = '2023-11-03 13:11:18';
/**
* @var int
*/
diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php
index 18c35b44634..7a15d8c0201 100644
--- a/src/ValueObject/PhpVersionFeature.php
+++ b/src/ValueObject/PhpVersionFeature.php
@@ -522,4 +522,9 @@ final class PhpVersionFeature
* @var int
*/
public const SENSITIVE_PARAMETER_ATTRIBUTE = \Rector\Core\ValueObject\PhpVersion::PHP_82;
+ /**
+ * @see https://wiki.php.net/rfc/marking_overriden_methods
+ * @var int
+ */
+ public const OVERRIDE_ATTRIBUTE = \Rector\Core\ValueObject\PhpVersion::PHP_83;
}
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index fd4b7a48b3f..670f558a5f7 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -1902,6 +1902,7 @@ return array(
'Rector\\Php82\\Rector\\FuncCall\\Utf8DecodeEncodeToMbConvertEncodingRector' => $baseDir . '/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php',
'Rector\\Php82\\Rector\\New_\\FilesystemIteratorSkipDotsRector' => $baseDir . '/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php',
'Rector\\Php82\\Rector\\Param\\AddSensitiveParameterAttributeRector' => $baseDir . '/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php',
+ 'Rector\\Php83\\Rector\\ClassMethod\\AddOverrideAttributeToOverriddenMethodsRector' => $baseDir . '/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayItemNodeAnnotationToAttributeMapper' => $baseDir . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index 537119bfe81..922614f8564 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -2120,6 +2120,7 @@ class ComposerStaticInit18ad0e678efbbb500e116f7c54cccdd4
'Rector\\Php82\\Rector\\FuncCall\\Utf8DecodeEncodeToMbConvertEncodingRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php',
'Rector\\Php82\\Rector\\New_\\FilesystemIteratorSkipDotsRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php',
'Rector\\Php82\\Rector\\Param\\AddSensitiveParameterAttributeRector' => __DIR__ . '/../..' . '/rules/Php82/Rector/Param/AddSensitiveParameterAttributeRector.php',
+ 'Rector\\Php83\\Rector\\ClassMethod\\AddOverrideAttributeToOverriddenMethodsRector' => __DIR__ . '/../..' . '/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayAnnotationToAttributeMapper.php',
'Rector\\PhpAttribute\\AnnotationToAttributeMapper\\ArrayItemNodeAnnotationToAttributeMapper' => __DIR__ . '/../..' . '/packages/PhpAttribute/AnnotationToAttributeMapper/ArrayItemNodeAnnotationToAttributeMapper.php',