From b15dcfef8cce42fb07369dda998563ad57d75cec Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 6 Feb 2026 22:17:35 +0700 Subject: [PATCH 1/2] [DeadCode] Handle only remove 1 @var on multi vars on RemoveUselessVarTagRector --- .../on_multi_var_remove_one_var.php.inc | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc diff --git a/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc new file mode 100644 index 00000000000..addd937fb7d --- /dev/null +++ b/rules-tests/DeadCode/Rector/Property/RemoveUselessVarTagRector/Fixture/on_multi_var_remove_one_var.php.inc @@ -0,0 +1,40 @@ +bulkConsumerRegistration ?? "abc"; + +?> +----- +bulkConsumerRegistration ?? "abc"; + +?> From 45c7dfa33e9ff98728304a98db2de436b940ccc9 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 6 Feb 2026 22:35:30 +0700 Subject: [PATCH 2/2] Fix --- .../PhpDoc/TagRemover/VarTagRemover.php | 8 +++---- .../PhpDocInfo/PhpDocInfo.php | 4 ++++ src/Comments/NodeDocBlock/DocBlockUpdater.php | 21 ++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php index a33631c9d15..f8e7be2e4d7 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php @@ -32,14 +32,14 @@ public function __construct( ) { } - public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassConst|Expression $property): bool + public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassConst|Expression $node): bool { $varTagValueNode = $phpDocInfo->getVarTagValueNode(); if (! $varTagValueNode instanceof VarTagValueNode) { return false; } - $isVarTagValueDead = $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $property); + $isVarTagValueDead = $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $node); if (! $isVarTagValueDead) { return false; } @@ -48,8 +48,8 @@ public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property|ClassCons return false; } - $phpDocInfo->removeByType(VarTagValueNode::class); - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + $phpDocInfo->removeByType(VarTagValueNode::class, $varTagValueNode->variableName); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); return true; } diff --git a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index 750845c3473..a49e14f8841 100644 --- a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -254,6 +254,10 @@ public function removeByType(string $typeToRemove, ?string $name = null): bool { $hasChanged = false; + if ($name === '') { + $name = null; + } + $phpDocNodeTraverser = new PhpDocNodeTraverser(); $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( $typeToRemove, diff --git a/src/Comments/NodeDocBlock/DocBlockUpdater.php b/src/Comments/NodeDocBlock/DocBlockUpdater.php index 9802b8f30b1..1bf70ee1fd7 100644 --- a/src/Comments/NodeDocBlock/DocBlockUpdater.php +++ b/src/Comments/NodeDocBlock/DocBlockUpdater.php @@ -43,7 +43,26 @@ public function updateRefactoredNodeWithPhpDocInfo(Node $node): void private function setCommentsAttribute(Node $node): void { - $comments = array_filter($node->getComments(), static fn (Comment $comment): bool => ! $comment instanceof Doc); + $docComment = $node->getDocComment(); + $docCommentText = $docComment instanceof Doc ? $docComment->getText() : null; + + $comments = array_filter( + $node->getComments(), + static function (Comment $comment) use ($docCommentText): bool { + if (! $comment instanceof Doc) { + return true; + } + + // remove only the docblock that belongs to the node itself; + // keep other preceding docblocks (possible with multiple @var docblocks before a statement) + if ($docCommentText !== null && $comment->getText() === $docCommentText) { + return false; + } + + return true; + } + ); + $node->setAttribute(AttributeKey::COMMENTS, array_values($comments)); }