diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index a39279ad1d8..fdacc54a1a0 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 369 Rules Overview +# 370 Rules Overview
@@ -10,7 +10,7 @@ - [CodingStyle](#codingstyle) (28) -- [DeadCode](#deadcode) (43) +- [DeadCode](#deadcode) (44) - [EarlyReturn](#earlyreturn) (9) @@ -2886,6 +2886,29 @@ Remove `@param` docblock with same type as parameter type
+### RemoveUselessReadOnlyTagRector + +Remove useless `@readonly` annotation on native readonly type + +- class: [`Rector\DeadCode\Rector\Property\RemoveUselessReadOnlyTagRector`](../rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php) + +```diff + final class SomeClass + { +- /** +- * @readonly +- */ + private readonly string $name; + + public function __construct(string $name) + { + $this->name = $name; + } + } +``` + +
+ ### RemoveUselessReturnExprInConstructRector Remove useless return Expr in `__construct()` diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc new file mode 100644 index 00000000000..df38cce3edb --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_class.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc new file mode 100644 index 00000000000..1b9b8366114 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_param_construct.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc new file mode 100644 index 00000000000..6d7f7b3d7ab --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/remove_on_property.php.inc @@ -0,0 +1,34 @@ +name = $name; + } +} + +?> +----- +name = $name; + } +} + +?> diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc new file mode 100644 index 00000000000..f2047395390 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_no_readonly_doc.php.inc @@ -0,0 +1,13 @@ +name = $name; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc new file mode 100644 index 00000000000..eb9dd784d86 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/Fixture/skip_with_description.php.inc @@ -0,0 +1,16 @@ +name = $name; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php new file mode 100644 index 00000000000..90b3a2dc676 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/RemoveUselessReadOnlyTagRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php new file mode 100644 index 00000000000..862a435388c --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector/config/configured_rule.php @@ -0,0 +1,9 @@ +withRules([RemoveUselessReadOnlyTagRector::class]); diff --git a/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php b/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php new file mode 100644 index 00000000000..664a30ea752 --- /dev/null +++ b/rules/DeadCode/Rector/Property/RemoveUselessReadOnlyTagRector.php @@ -0,0 +1,108 @@ +name = $name; + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + private readonly string $name; + + public function __construct(string $name) + { + $this->name = $name; + } +} +CODE_SAMPLE + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [Class_::class, Property::class, Param::class]; + } + + /** + * @param Class_|Property|Param $node + */ + public function refactor(Node $node): ?Node + { + // for param, only on property promotion + if ($node instanceof Param && $node->flags === 0) { + return null; + } + + if (! $this->visibilityManipulator->isReadonly($node)) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + $readonlyDoc = $phpDocInfo->getByName('readonly'); + if (! $readonlyDoc instanceof PhpDocTagNode) { + return null; + } + + if (! $readonlyDoc->value instanceof GenericTagValueNode) { + return null; + } + + if ($readonlyDoc->value->value !== '') { + return null; + } + + $phpDocInfo->removeByName('readonly'); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } +} diff --git a/src/Config/Level/DeadCodeLevel.php b/src/Config/Level/DeadCodeLevel.php index 7e3ca367ec2..7c92251f1ee 100644 --- a/src/Config/Level/DeadCodeLevel.php +++ b/src/Config/Level/DeadCodeLevel.php @@ -39,6 +39,7 @@ use Rector\DeadCode\Rector\Node\RemoveNonExistingVarAnnotationRector; use Rector\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector; use Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector; +use Rector\DeadCode\Rector\Property\RemoveUselessReadOnlyTagRector; use Rector\DeadCode\Rector\Property\RemoveUselessVarTagRector; use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; use Rector\DeadCode\Rector\Return_\RemoveDeadConditionAboveReturnRector; @@ -90,6 +91,7 @@ final class DeadCodeLevel // docblock RemoveUselessParamTagRector::class, RemoveUselessReturnTagRector::class, + RemoveUselessReadOnlyTagRector::class, RemoveNonExistingVarAnnotationRector::class, RemoveUselessVarTagRector::class, RemovePhpVersionIdCheckRector::class,