From de0132c9e31939def0d1eb9a8d1d689fade15193 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 30 Jun 2026 17:29:41 +0200 Subject: [PATCH] [DeadCode] Add RemoveUselessUnionReturnDocblockRector Remove @return union docblock when a more specific, non-array native return type already covers it, e.g. @return bool|mixed|string over false|string. --- .../Fixture/skip_array_native.php.inc | 13 ++ .../Fixture/skip_description.php.inc | 13 ++ .../skip_more_specific_docblock.php.inc | 13 ++ .../Fixture/skip_no_native_type.php.inc | 13 ++ .../Fixture/union_over_single_native.php.inc | 28 ++++ .../Fixture/union_with_mixed.php.inc | 28 ++++ ...veUselessUnionReturnDocblockRectorTest.php | 28 ++++ .../config/configured_rule.php | 11 ++ ...RemoveUselessUnionReturnDocblockRector.php | 134 ++++++++++++++++++ src/Config/Level/DeadCodeLevel.php | 2 + 10 files changed, 283 insertions(+) create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_array_native.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_description.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_more_specific_docblock.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_no_native_type.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/union_over_single_native.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/union_with_mixed.php.inc create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/RemoveUselessUnionReturnDocblockRectorTest.php create mode 100644 rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/config/configured_rule.php create mode 100644 rules/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector.php diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_array_native.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_array_native.php.inc new file mode 100644 index 00000000000..0f84345531f --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/skip_array_native.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/union_with_mixed.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/union_with_mixed.php.inc new file mode 100644 index 00000000000..920809d8e51 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/Fixture/union_with_mixed.php.inc @@ -0,0 +1,28 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/RemoveUselessUnionReturnDocblockRectorTest.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/RemoveUselessUnionReturnDocblockRectorTest.php new file mode 100644 index 00000000000..0aa834d1411 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/RemoveUselessUnionReturnDocblockRectorTest.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/ClassMethod/RemoveUselessUnionReturnDocblockRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/config/configured_rule.php new file mode 100644 index 00000000000..04139295eb0 --- /dev/null +++ b/rules-tests/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector/config/configured_rule.php @@ -0,0 +1,11 @@ +withPhpVersion(PhpVersionFeature::UNION_TYPES) + ->withRules([RemoveUselessUnionReturnDocblockRector::class]); diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector.php new file mode 100644 index 00000000000..d6d713e751a --- /dev/null +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessUnionReturnDocblockRector.php @@ -0,0 +1,134 @@ +> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class, Function_::class]; + } + + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->returnType === null) { + return null; + } + + // skip as no comments + if ($node->getComments() === []) { + return null; + } + + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $phpDocInfo instanceof PhpDocInfo) { + return null; + } + + $returnTagValueNode = $phpDocInfo->getReturnTagValue(); + if (! $returnTagValueNode instanceof ReturnTagValueNode) { + return null; + } + + // keep description, it carries info the native type cannot + if ($returnTagValueNode->description !== '') { + return null; + } + + // only union docblock types are handled here + if (! $returnTagValueNode->type instanceof UnionTypeNode) { + return null; + } + + $nativeReturnType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($node->returnType); + + // keep array native type, the docblock may carry element types + if ($nativeReturnType->isArray()->yes()) { + return null; + } + + $docblockReturnType = $phpDocInfo->getReturnType(); + + // the docblock adds nothing when it is broader than the native type + if (! $docblockReturnType->isSuperTypeOf($nativeReturnType)->yes()) { + return null; + } + + if (! $phpDocInfo->removeByType(ReturnTagValueNode::class)) { + return null; + } + + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::UNION_TYPES; + } +} diff --git a/src/Config/Level/DeadCodeLevel.php b/src/Config/Level/DeadCodeLevel.php index e1062f82deb..e16dc63841c 100644 --- a/src/Config/Level/DeadCodeLevel.php +++ b/src/Config/Level/DeadCodeLevel.php @@ -28,6 +28,7 @@ use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector; use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnExprInConstructRector; use Rector\DeadCode\Rector\ClassMethod\RemoveUselessReturnTagRector; +use Rector\DeadCode\Rector\ClassMethod\RemoveUselessUnionReturnDocblockRector; use Rector\DeadCode\Rector\ClassMethod\RemoveVoidDocblockFromMagicMethodRector; use Rector\DeadCode\Rector\Closure\RemoveUnusedClosureVariableUseRector; use Rector\DeadCode\Rector\Concat\RemoveConcatAutocastRector; @@ -119,6 +120,7 @@ final class DeadCodeLevel RemoveUselessReturnTagRector::class, RemoveDuplicatedReturnSelfDocblockRector::class, RemoveMixedDocblockOverruledByNativeTypeRector::class, + RemoveUselessUnionReturnDocblockRector::class, RemoveUselessReadOnlyTagRector::class, RemoveNonExistingVarAnnotationRector::class, RemoveUselessVarTagRector::class,