From f8560bc381cd2f00c61f010350bf171c4c3bdd88 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 11 Sep 2023 14:10:22 +0200 Subject: [PATCH 1/3] almost --- .../PhpDocInfo/PhpDocInfo.php | 2 -- .../PhpDocManipulator/PhpDocTypeChanger.php | 21 +++++++------------ .../Comments/NodeDocBlock/DocBlockUpdater.php | 1 - phpstan.neon | 3 +++ .../PhpDoc/TagRemover/ParamTagRemover.php | 10 ++++++--- .../PhpDoc/TagRemover/VarTagRemover.php | 6 +++++- .../RemoveUselessReturnTagRector.php | 6 +++++- .../RemoveNonExistingVarAnnotationRector.php | 5 ++++- ...ertyAssignToConstructorPromotionRector.php | 16 ++++++-------- 9 files changed, 38 insertions(+), 32 deletions(-) diff --git a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index c524b44f887..ac9592b724f 100644 --- a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -269,7 +269,6 @@ public function removeByType(string $typeToRemove): bool return null; } - $this->markAsChanged(); $hasChanged = true; return PhpDocNodeTraverser::NODE_REMOVE; } @@ -278,7 +277,6 @@ public function removeByType(string $typeToRemove): bool return null; } - $this->markAsChanged(); $hasChanged = true; return PhpDocNodeTraverser::NODE_REMOVE; }); diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php index 34b78054566..d719e0055af 100644 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php +++ b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php @@ -177,7 +177,6 @@ public function changeParamType( } $paramTagValueNode->type = $phpDocTypeNode; - $phpDocInfo->markAsChanged(); } else { $paramTagValueNode = $this->paramPhpDocNodeFactory->create($phpDocTypeNode, $param); $phpDocInfo->addTagValueNode($paramTagValueNode); @@ -211,7 +210,7 @@ public function isAllowed(TypeNode $typeNode): bool return in_array((string) $typeNode, self::ALLOWED_IDENTIFIER_TYPENODE_TYPES, true); } - public function copyPropertyDocToParam(ClassMethod $classMethod, Property $property, Param $param): void + public function convertPropertyVarTagToParamTag(ClassMethod $classMethod, Property $property, Param $param): void { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); if (! $phpDocInfo instanceof PhpDocInfo) { @@ -220,7 +219,7 @@ public function copyPropertyDocToParam(ClassMethod $classMethod, Property $prope $varTagValueNode = $phpDocInfo->getVarTagValueNode(); if (! $varTagValueNode instanceof VarTagValueNode) { - $this->processKeepComments($classMethod, $property, $param); + $this->processKeepComments($property, $param); return; } @@ -238,7 +237,6 @@ public function copyPropertyDocToParam(ClassMethod $classMethod, Property $prope } $phpDocInfo->removeByType(VarTagValueNode::class); - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); $param->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); @@ -246,7 +244,7 @@ public function copyPropertyDocToParam(ClassMethod $classMethod, Property $prope $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); $this->changeParamType($classMethod, $phpDocInfo, $paramType, $param, $paramVarName); - $this->processKeepComments($classMethod, $property, $param); + $this->processKeepComments($property, $param); } /** @@ -261,7 +259,7 @@ public function changeVarTypeNode(Stmt $stmt, PhpDocInfo $phpDocInfo, TypeNode $ $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); } - private function processKeepComments(ClassMethod $classMethod, Property $property, Param $param): void + private function processKeepComments(Property $property, Param $param): void { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); $varTagValueNode = $phpDocInfo->getVarTagValueNode(); @@ -269,10 +267,8 @@ private function processKeepComments(ClassMethod $classMethod, Property $propert $toBeRemoved = ! $varTagValueNode instanceof VarTagValueNode; $this->commentsMerger->keepComments($param, [$property]); - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); - - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + $paramPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); + $varTagValueNode = $paramPhpDocInfo->getVarTagValueNode(); if (! $toBeRemoved) { return; } @@ -285,8 +281,7 @@ private function processKeepComments(ClassMethod $classMethod, Property $propert return; } - $phpDocInfo->removeByType(VarTagValueNode::class); - - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod); + $paramPhpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); } } diff --git a/packages/Comments/NodeDocBlock/DocBlockUpdater.php b/packages/Comments/NodeDocBlock/DocBlockUpdater.php index 526d27d9faf..64c1acd85e0 100644 --- a/packages/Comments/NodeDocBlock/DocBlockUpdater.php +++ b/packages/Comments/NodeDocBlock/DocBlockUpdater.php @@ -55,7 +55,6 @@ public function updateRefactoredNodeWithPhpDocInfo(Node $node): void $printedPhpDoc = $this->printPhpDocInfoToString($phpDocInfo); $node->setDocComment(new Doc($printedPhpDoc)); - // $node->setDocComment(new Doc((string) $phpDocNode)); } private function setCommentsAttribute(Node $node): void diff --git a/phpstan.neon b/phpstan.neon index 48f6e29c5ea..5a3814be05d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -574,3 +574,6 @@ parameters: - message: '#Parameters should use "PhpParser\\Node\\Identifier\|string" types as the only types passed to this method#' path: packages/NodeNameResolver/NodeNameResolver.php + + # remove in next step + - '#Call to deprecated method markAsChanged\(\) of class Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo#' diff --git a/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php index f9e58bd54ae..3daa3b3d678 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/ParamTagRemover.php @@ -10,13 +10,15 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\PhpDoc\DeadParamTagValueNodeAnalyzer; use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; final class ParamTagRemover { public function __construct( - private readonly DeadParamTagValueNodeAnalyzer $deadParamTagValueNodeAnalyzer + private readonly DeadParamTagValueNodeAnalyzer $deadParamTagValueNodeAnalyzer, + private readonly DocBlockUpdater $docBlockUpdater, ) { } @@ -27,7 +29,6 @@ public function removeParamTagsIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $f $phpDocNodeTraverser = new PhpDocNodeTraverser(); $phpDocNodeTraverser->traverseWithCallable($phpDocInfo->getPhpDocNode(), '', function (Node $docNode) use ( $functionLike, - $phpDocInfo, &$hasChanged ): ?int { if (! $docNode instanceof PhpDocTagNode) { @@ -52,11 +53,14 @@ public function removeParamTagsIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $f return null; } - $phpDocInfo->markAsChanged(); $hasChanged = true; return PhpDocNodeTraverser::NODE_REMOVE; }); + if ($hasChanged) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($functionLike); + } + return $hasChanged; } } diff --git a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php index cc122883a1c..fa07fc01de5 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php @@ -13,6 +13,7 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\DeadCode\PhpDoc\DeadVarTagValueNodeAnalyzer; use Rector\PHPStanStaticTypeMapper\DoctrineTypeAnalyzer; @@ -22,7 +23,8 @@ public function __construct( private readonly DoctrineTypeAnalyzer $doctrineTypeAnalyzer, private readonly PhpDocInfoFactory $phpDocInfoFactory, private readonly DeadVarTagValueNodeAnalyzer $deadVarTagValueNodeAnalyzer, - private readonly PhpDocTypeChanger $phpDocTypeChanger + private readonly PhpDocTypeChanger $phpDocTypeChanger, + private readonly DocBlockUpdater $docBlockUpdater, ) { } @@ -43,6 +45,7 @@ public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property $property } $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); } public function removeVarPhpTagValueNodeIfNotComment(Expression | Property | Param $node, Type $type): void @@ -74,5 +77,6 @@ public function removeVarPhpTagValueNodeIfNotComment(Expression | Property | Par } $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); } } diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php index b8210c62e55..0543d6faca1 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUselessReturnTagRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\ClassMethod; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Core\Rector\AbstractRector; use Rector\DeadCode\PhpDoc\TagRemover\ReturnTagRemover; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -17,7 +18,8 @@ final class RemoveUselessReturnTagRector extends AbstractRector { public function __construct( - private readonly ReturnTagRemover $returnTagRemover + private readonly ReturnTagRemover $returnTagRemover, + private readonly DocBlockUpdater $docBlockUpdater, ) { } @@ -75,6 +77,8 @@ public function refactor(Node $node): ?Node return null; } + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + return $node; } } diff --git a/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php b/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php index eef7369df2f..675a0affdfd 100644 --- a/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php +++ b/rules/DeadCode/Rector/Node/RemoveNonExistingVarAnnotationRector.php @@ -22,6 +22,7 @@ use PhpParser\Node\Stmt\While_; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Core\NodeManipulator\StmtsManipulator; use Rector\Core\Rector\AbstractRector; @@ -52,7 +53,8 @@ final class RemoveNonExistingVarAnnotationRector extends AbstractRector ]; public function __construct( - private readonly StmtsManipulator $stmtsManipulator + private readonly StmtsManipulator $stmtsManipulator, + private readonly DocBlockUpdater $docBlockUpdater, ) { } @@ -154,6 +156,7 @@ public function refactor(Node $node): ?Node } $phpDocInfo->removeByType(VarTagValueNode::class); + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); $hasChanged = true; } diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index 38234cfe9ef..ef48d0c007d 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -139,7 +139,11 @@ public function refactor(Node $node): ?Node } $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod); - $classReflection = null; + + $classReflection = $this->reflectionResolver->resolveClassReflection($node); + if (! $classReflection instanceof ClassReflection) { + return null; + } foreach ($promotionCandidates as $promotionCandidate) { // does property have some useful annotations? @@ -150,14 +154,6 @@ public function refactor(Node $node): ?Node continue; } - if (! $classReflection instanceof ClassReflection) { - $classReflection = $this->reflectionResolver->resolveClassReflection($node); - } - - if (! $classReflection instanceof ClassReflection) { - return null; - } - if (! $this->makePropertyPromotionGuard->isLegal( $node, $classReflection, @@ -203,7 +199,7 @@ public function refactor(Node $node): ?Node $param->attrGroups = array_merge($param->attrGroups, $property->attrGroups); $this->processUnionType($property, $param); - $this->phpDocTypeChanger->copyPropertyDocToParam($constructClassMethod, $property, $param); + $this->phpDocTypeChanger->convertPropertyVarTagToParamTag($constructClassMethod, $property, $param); } return $node; From 10069d755de3a5e3c1511485bcb4fa51d892e630 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 11 Sep 2023 14:25:34 +0200 Subject: [PATCH 2/3] simplify fixtures --- .../PhpDocInfo/PhpDocInfo.php | 4 +- .../PhpDocManipulator/PhpDocTypeChanger.php | 130 +++++++++--------- .../Printer/PhpDocInfoPrinter.php | 10 ++ .../Fixture/copy_array_generic_doc.php.inc | 4 +- .../Fixture/copy_array_string_doc.php.inc | 40 ------ .../Fixture/copy_doc.php.inc | 20 +-- .../Source/ValidatedElement.php | 8 ++ ...ertyAssignToConstructorPromotionRector.php | 42 ++++-- 8 files changed, 124 insertions(+), 134 deletions(-) delete mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_string_doc.php.inc create mode 100644 rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php diff --git a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index ac9592b724f..9c2a4c25086 100644 --- a/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -255,9 +255,9 @@ public function removeByType(string $typeToRemove): bool $hasChanged = false; $phpDocNodeTraverser = new PhpDocNodeTraverser(); - $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', function (Node $node) use ( + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( $typeToRemove, - &$hasChanged, + &$hasChanged ): ?int { if ($node instanceof PhpDocTagNode && $node->value instanceof $typeToRemove) { // keep special annotation for tools diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php index d719e0055af..bebc4dab84e 100644 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php +++ b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php @@ -59,9 +59,9 @@ public function __construct( private readonly StaticTypeMapper $staticTypeMapper, private readonly TypeComparator $typeComparator, private readonly ParamPhpDocNodeFactory $paramPhpDocNodeFactory, - private readonly NodeNameResolver $nodeNameResolver, - private readonly CommentsMerger $commentsMerger, - private readonly PhpDocInfoFactory $phpDocInfoFactory, + // private readonly NodeNameResolver $nodeNameResolver, + // private readonly CommentsMerger $commentsMerger, + // private readonly PhpDocInfoFactory $phpDocInfoFactory, private readonly NewPhpDocFromPHPStanTypeGuard $newPhpDocFromPHPStanTypeGuard, private readonly DocBlockUpdater $docBlockUpdater ) { @@ -210,45 +210,45 @@ public function isAllowed(TypeNode $typeNode): bool return in_array((string) $typeNode, self::ALLOWED_IDENTIFIER_TYPENODE_TYPES, true); } - public function convertPropertyVarTagToParamTag(ClassMethod $classMethod, Property $property, Param $param): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); - if (! $phpDocInfo instanceof PhpDocInfo) { - return; - } - - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - if (! $varTagValueNode instanceof VarTagValueNode) { - $this->processKeepComments($property, $param); - return; - } - - if ($varTagValueNode->description !== '') { - return; - } - - $paramVarName = $this->nodeNameResolver->getName($param->var); - if (! $this->isAllowed($varTagValueNode->type)) { - return; - } - - if (! is_string($paramVarName)) { - return; - } - - $phpDocInfo->removeByType(VarTagValueNode::class); - - $param->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - - $phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO); - $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); - - $this->changeParamType($classMethod, $phpDocInfo, $paramType, $param, $paramVarName); - $this->processKeepComments($property, $param); - } + // public function convertPropertyVarTagToParamTag(ClassMethod $classMethod, Property $property, Param $param): void + // { + // $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); + // if (! $phpDocInfo instanceof PhpDocInfo) { + // return; + // } + // + // $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + // if (! $varTagValueNode instanceof VarTagValueNode) { + // $this->processKeepComments($property, $param); + // return; + // } + // + // if ($varTagValueNode->description !== '') { + // return; + // } + // + // $paramVarName = $this->nodeNameResolver->getName($param->var); + // if (! $this->isAllowed($varTagValueNode->type)) { + // return; + // } + // + // if (! is_string($paramVarName)) { + // return; + // } + // + // $phpDocInfo->removeByType(VarTagValueNode::class); + // + // $param->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); + // + // $phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO); + // $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); + // + // $this->changeParamType($classMethod, $phpDocInfo, $paramType, $param, $paramVarName); + // $this->processKeepComments($property, $param); + // } /** - * @api doctrine + * @api downgrade */ public function changeVarTypeNode(Stmt $stmt, PhpDocInfo $phpDocInfo, TypeNode $typeNode): void { @@ -259,29 +259,29 @@ public function changeVarTypeNode(Stmt $stmt, PhpDocInfo $phpDocInfo, TypeNode $ $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); } - private function processKeepComments(Property $property, Param $param): void - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); - $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - - $toBeRemoved = ! $varTagValueNode instanceof VarTagValueNode; - $this->commentsMerger->keepComments($param, [$property]); - - $paramPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); - $varTagValueNode = $paramPhpDocInfo->getVarTagValueNode(); - if (! $toBeRemoved) { - return; - } - - if (! $varTagValueNode instanceof VarTagValueNode) { - return; - } - - if ($varTagValueNode->description !== '') { - return; - } - - $paramPhpDocInfo->removeByType(VarTagValueNode::class); - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); - } + // private function processKeepComments(Property $property, Param $param): void + // { + // $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); + // $varTagValueNode = $phpDocInfo->getVarTagValueNode(); + // + // $toBeRemoved = ! $varTagValueNode instanceof VarTagValueNode; + // $this->commentsMerger->keepComments($param, [$property]); + // + // $paramPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); + // $varTagValueNode = $paramPhpDocInfo->getVarTagValueNode(); + // if (! $toBeRemoved) { + // return; + // } + // + // if (! $varTagValueNode instanceof VarTagValueNode) { + // return; + // } + // + // if ($varTagValueNode->description !== '') { + // return; + // } + // + // $paramPhpDocInfo->removeByType(VarTagValueNode::class); + // $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); + // } } diff --git a/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php b/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php index 6abcf31ecd0..954f5803a68 100644 --- a/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php +++ b/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php @@ -22,6 +22,7 @@ use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\Util\StringUtils; use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; +use Rector\Tests\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture\Comment; /** * @see \Rector\Tests\BetterPhpDocParser\PhpDocInfo\PhpDocInfoPrinter\PhpDocInfoPrinterTest @@ -128,6 +129,15 @@ public function printFormatPreserving(PhpDocInfo $phpDocInfo): string return Strings::replace($phpDocString, self::CALLABLE_REGEX, 'callable('); } + /** + * @return Comment[] + */ + public function printToComments(PhpDocInfo $phpDocInfo): array + { + $printedPhpDocContents = $this->printFormatPreserving($phpDocInfo); + return [new \PhpParser\Comment($printedPhpDocContents)]; + } + private function getCurrentPhpDocInfo(): PhpDocInfo { if (! $this->phpDocInfo instanceof PhpDocInfo) { diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc index 60bac01f883..11d807c955b 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_generic_doc.php.inc @@ -24,12 +24,10 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromo final class CopyArrayGenericDoc { - /** - * @param array $map - */ public function __construct( /** * Comment + * @var array */ private array $map ) diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_string_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_string_doc.php.inc deleted file mode 100644 index dd1ebab5dab..00000000000 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_array_string_doc.php.inc +++ /dev/null @@ -1,40 +0,0 @@ -map = $map; - } -} - -?> ------ - diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc index ff4d1f2b818..ea2a1c0c942 100644 --- a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Fixture/copy_doc.php.inc @@ -2,28 +2,23 @@ namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\ValidatedElement; use Symfony\Component\Validator\Constraints as Assert; -class Y {} -class Z {} - final class CopyDoc { - public Y $y; - /** - * @var Z[] + * @var ValidatedElement[] * @Assert\Valid() * @Assert\NotBlank() */ public array $z = []; /** - * @param Z[] $z + * @param ValidatedElement[] $z */ - public function __construct(Y $y, array $z = []) + public function __construct(array $z = []) { - $this->y = $y; $this->z = $z; } } @@ -33,18 +28,15 @@ final class CopyDoc namespace Rector\Tests\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Fixture; +use Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector\Source\ValidatedElement; use Symfony\Component\Validator\Constraints as Assert; -class Y {} -class Z {} - final class CopyDoc { /** - * @param Z[] $z + * @param ValidatedElement[] $z */ public function __construct( - public Y $y, /** * @Assert\Valid() * @Assert\NotBlank() diff --git a/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php new file mode 100644 index 00000000000..b757d8fbd87 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector/Source/ValidatedElement.php @@ -0,0 +1,8 @@ +phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod); + $constructorPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod); $classReflection = $this->reflectionResolver->resolveClassReflection($node); if (! $classReflection instanceof ClassReflection) { @@ -146,14 +149,13 @@ public function refactor(Node $node): ?Node } foreach ($promotionCandidates as $promotionCandidate) { - // does property have some useful annotations? - $property = $promotionCandidate->getProperty(); $param = $promotionCandidate->getParam(); if ($this->shouldSkipParam($param)) { continue; } + $property = $promotionCandidate->getProperty(); if (! $this->makePropertyPromotionGuard->isLegal( $node, $classReflection, @@ -164,14 +166,14 @@ public function refactor(Node $node): ?Node continue; } + // remove property from class $propertyStmtKey = $property->getAttribute(AttributeKey::STMT_KEY); unset($node->stmts[$propertyStmtKey]); - // remove assign + // remove assign in constructor $assignStmtPosition = $promotionCandidate->getStmtPosition(); unset($constructClassMethod->stmts[$assignStmtPosition]); - $property = $promotionCandidate->getProperty(); $paramName = $this->getName($param); // rename also following calls @@ -181,7 +183,7 @@ public function refactor(Node $node): ?Node $oldName = $this->getName($param->var); $this->variableRenamer->renameVariableInFunctionLike($constructClassMethod, $oldName, $propertyName, null); - $paramTagValueNode = $classMethodPhpDocInfo->getParamTagValueByName($paramName); + $paramTagValueNode = $constructorPhpDocInfo->getParamTagValueByName($paramName); if (! $paramTagValueNode instanceof ParamTagValueNode) { $this->decorateParamWithPropertyPhpDocInfo($constructClassMethod, $property, $param, $paramName); @@ -193,13 +195,33 @@ public function refactor(Node $node): ?Node // property name has higher priority $paramName = $this->getName($property); $param->var = new Variable($paramName); - $param->flags = $property->flags; - // Copy over attributes of the "old" property + + // copy attributes of the old property $param->attrGroups = array_merge($param->attrGroups, $property->attrGroups); $this->processUnionType($property, $param); - $this->phpDocTypeChanger->convertPropertyVarTagToParamTag($constructClassMethod, $property, $param); + $paramComments = $param->getComments(); + + // already has @param tag → give it priority over @var and remove @var + if ($paramTagValueNode instanceof ParamTagValueNode) { + $propertyDocInfo = $this->phpDocInfoFactory->createFromNode($property); + + if ($propertyDocInfo->hasByType(VarTagValueNode::class)) { + $propertyDocInfo->removeByType(VarTagValueNode::class); + + $propertyComments = $this->phpDocInfoPrinter->printToComments($propertyDocInfo); + $mergedComments = array_merge($paramComments, $propertyComments); + + $mergedComments = array_filter($mergedComments, function (Comment $comment) { + return $comment->getText() !== ''; + }); + + $param->setAttribute(AttributeKey::COMMENTS, $mergedComments); + } + } + + // $this->phpDocTypeChanger->convertPropertyVarTagToParamTag($constructClassMethod, $property, $param); } return $node; From 979bc0481d40c055ce2359a5bd8992dabc120df4 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 11 Sep 2023 14:54:26 +0200 Subject: [PATCH 3/3] extract PropertyPromotionDocBlockMerger service --- .../PhpDocManipulator/PhpDocTypeChanger.php | 72 -------------- .../Printer/PhpDocInfoPrinter.php | 4 +- .../{comment.php.inc => with_comment.php.inc} | 4 +- .../PhpDoc/TagRemover/VarTagRemover.php | 10 +- .../Property/RemoveUselessVarTagRector.php | 10 +- .../PropertyPromotionDocBlockMerger.php | 99 +++++++++++++++++++ ...ertyAssignToConstructorPromotionRector.php | 77 +++------------ 7 files changed, 127 insertions(+), 149 deletions(-) rename rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/{comment.php.inc => with_comment.php.inc} (94%) create mode 100644 rules/Php80/DocBlock/PropertyPromotionDocBlockMerger.php diff --git a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php index bebc4dab84e..2909301d6e4 100644 --- a/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php +++ b/packages/BetterPhpDocParser/PhpDocManipulator/PhpDocTypeChanger.php @@ -7,8 +7,6 @@ use PhpParser\Node\FunctionLike; use PhpParser\Node\Param; use PhpParser\Node\Stmt; -use PhpParser\Node\Stmt\ClassMethod; -use PhpParser\Node\Stmt\Property; use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode; use PHPStan\PhpDocParser\Ast\Node; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; @@ -23,17 +21,13 @@ use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; use PHPStan\Type\Type; -use Rector\BetterPhpDocParser\Comment\CommentsMerger; use Rector\BetterPhpDocParser\Guard\NewPhpDocFromPHPStanTypeGuard; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; -use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareIntersectionTypeNode; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareArrayTypeNode; use Rector\BetterPhpDocParser\ValueObject\Type\SpacingAwareCallableTypeNode; use Rector\Comments\NodeDocBlock\DocBlockUpdater; -use Rector\NodeNameResolver\NodeNameResolver; -use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory; @@ -59,9 +53,6 @@ public function __construct( private readonly StaticTypeMapper $staticTypeMapper, private readonly TypeComparator $typeComparator, private readonly ParamPhpDocNodeFactory $paramPhpDocNodeFactory, - // private readonly NodeNameResolver $nodeNameResolver, - // private readonly CommentsMerger $commentsMerger, - // private readonly PhpDocInfoFactory $phpDocInfoFactory, private readonly NewPhpDocFromPHPStanTypeGuard $newPhpDocFromPHPStanTypeGuard, private readonly DocBlockUpdater $docBlockUpdater ) { @@ -210,43 +201,6 @@ public function isAllowed(TypeNode $typeNode): bool return in_array((string) $typeNode, self::ALLOWED_IDENTIFIER_TYPENODE_TYPES, true); } - // public function convertPropertyVarTagToParamTag(ClassMethod $classMethod, Property $property, Param $param): void - // { - // $phpDocInfo = $this->phpDocInfoFactory->createFromNode($property); - // if (! $phpDocInfo instanceof PhpDocInfo) { - // return; - // } - // - // $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - // if (! $varTagValueNode instanceof VarTagValueNode) { - // $this->processKeepComments($property, $param); - // return; - // } - // - // if ($varTagValueNode->description !== '') { - // return; - // } - // - // $paramVarName = $this->nodeNameResolver->getName($param->var); - // if (! $this->isAllowed($varTagValueNode->type)) { - // return; - // } - // - // if (! is_string($paramVarName)) { - // return; - // } - // - // $phpDocInfo->removeByType(VarTagValueNode::class); - // - // $param->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); - // - // $phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO); - // $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); - // - // $this->changeParamType($classMethod, $phpDocInfo, $paramType, $param, $paramVarName); - // $this->processKeepComments($property, $param); - // } - /** * @api downgrade */ @@ -258,30 +212,4 @@ public function changeVarTypeNode(Stmt $stmt, PhpDocInfo $phpDocInfo, TypeNode $ $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); } - - // private function processKeepComments(Property $property, Param $param): void - // { - // $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); - // $varTagValueNode = $phpDocInfo->getVarTagValueNode(); - // - // $toBeRemoved = ! $varTagValueNode instanceof VarTagValueNode; - // $this->commentsMerger->keepComments($param, [$property]); - // - // $paramPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($param); - // $varTagValueNode = $paramPhpDocInfo->getVarTagValueNode(); - // if (! $toBeRemoved) { - // return; - // } - // - // if (! $varTagValueNode instanceof VarTagValueNode) { - // return; - // } - // - // if ($varTagValueNode->description !== '') { - // return; - // } - // - // $paramPhpDocInfo->removeByType(VarTagValueNode::class); - // $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($param); - // } } diff --git a/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php b/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php index 954f5803a68..6a60eb3462e 100644 --- a/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php +++ b/packages/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php @@ -5,6 +5,7 @@ namespace Rector\BetterPhpDocParser\Printer; use Nette\Utils\Strings; +use PhpParser\Comment; use PhpParser\Node\Stmt\InlineHTML; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode; @@ -22,7 +23,6 @@ use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\Util\StringUtils; use Rector\PhpDocParser\PhpDocParser\PhpDocNodeTraverser; -use Rector\Tests\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture\Comment; /** * @see \Rector\Tests\BetterPhpDocParser\PhpDocInfo\PhpDocInfoPrinter\PhpDocInfoPrinterTest @@ -135,7 +135,7 @@ public function printFormatPreserving(PhpDocInfo $phpDocInfo): string public function printToComments(PhpDocInfo $phpDocInfo): array { $printedPhpDocContents = $this->printFormatPreserving($phpDocInfo); - return [new \PhpParser\Comment($printedPhpDocContents)]; + return [new Comment($printedPhpDocContents)]; } private function getCurrentPhpDocInfo(): PhpDocInfo diff --git a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc similarity index 94% rename from rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc rename to rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc index 46be10ba8cf..527f8032a06 100644 --- a/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/comment.php.inc +++ b/rules-tests/EarlyReturn/Rector/Return_/ReturnBinaryOrToEarlyReturnRector/Fixture/with_comment.php.inc @@ -2,7 +2,7 @@ namespace Rector\Tests\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture; -class Comment +final class WithComment { public function accept() { @@ -23,7 +23,7 @@ class Comment namespace Rector\Tests\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector\Fixture; -class Comment +final class WithComment { public function accept() { diff --git a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php index fa07fc01de5..24ebf9c11bb 100644 --- a/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php +++ b/rules/DeadCode/PhpDoc/TagRemover/VarTagRemover.php @@ -28,24 +28,26 @@ public function __construct( ) { } - public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property $property): void + public function removeVarTagIfUseless(PhpDocInfo $phpDocInfo, Property $property): bool { $varTagValueNode = $phpDocInfo->getVarTagValueNode(); if (! $varTagValueNode instanceof VarTagValueNode) { - return; + return false; } $isVarTagValueDead = $this->deadVarTagValueNodeAnalyzer->isDead($varTagValueNode, $property); if (! $isVarTagValueDead) { - return; + return false; } if ($this->phpDocTypeChanger->isAllowed($varTagValueNode->type)) { - return; + return false; } $phpDocInfo->removeByType(VarTagValueNode::class); $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($property); + + return true; } public function removeVarPhpTagValueNodeIfNotComment(Expression | Property | Param $node, Type $type): void diff --git a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php index a5dc89f9d86..4ab95c60aa0 100644 --- a/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php +++ b/rules/DeadCode/Rector/Property/RemoveUselessVarTagRector.php @@ -17,7 +17,7 @@ final class RemoveUselessVarTagRector extends AbstractRector { public function __construct( - private readonly VarTagRemover $varTagRemover + private readonly VarTagRemover $varTagRemover, ) { } @@ -60,12 +60,12 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); - if ($phpDocInfo->hasChanged()) { - return $node; + $hasChanged = $this->varTagRemover->removeVarTagIfUseless($phpDocInfo, $node); + if (! $hasChanged) { + return null; } - return null; + return $node; } } diff --git a/rules/Php80/DocBlock/PropertyPromotionDocBlockMerger.php b/rules/Php80/DocBlock/PropertyPromotionDocBlockMerger.php new file mode 100644 index 00000000000..2e97780042a --- /dev/null +++ b/rules/Php80/DocBlock/PropertyPromotionDocBlockMerger.php @@ -0,0 +1,99 @@ +getComments(); + + // already has @param tag → give it priority over @var and remove @var + if ($paramTagValueNode instanceof ParamTagValueNode) { + $propertyDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + + if ($propertyDocInfo->hasByType(VarTagValueNode::class)) { + $propertyDocInfo->removeByType(VarTagValueNode::class); + + $propertyComments = $this->phpDocInfoPrinter->printToComments($propertyDocInfo); + + /** @var Comment[] $mergedComments */ + $mergedComments = array_merge($paramComments, $propertyComments); + + $mergedComments = $this->removeEmptyComments($mergedComments); + + $param->setAttribute(AttributeKey::COMMENTS, $mergedComments); + } + } + } + + public function decorateParamWithPropertyPhpDocInfo( + ClassMethod $classMethod, + Property $property, + Param $param, + string $paramName + ): void { + $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); + $propertyPhpDocInfo->markAsChanged(); + + $param->setAttribute(AttributeKey::PHP_DOC_INFO, $propertyPhpDocInfo); + + // make sure the docblock is useful + if ($param->type === null) { + $varTagValueNode = $propertyPhpDocInfo->getVarTagValueNode(); + if (! $varTagValueNode instanceof VarTagValueNode) { + return; + } + + $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); + $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + $this->phpDocTypeChanger->changeParamType( + $classMethod, + $classMethodPhpDocInfo, + $paramType, + $param, + $paramName + ); + } else { + $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); + } + + $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($param, $paramType); + } + + /** + * @param Comment[] $mergedComments + * @return Comment[] + */ + private function removeEmptyComments(array $mergedComments): array + { + return array_filter($mergedComments, static fn (Comment $comment): bool => $comment->getText() !== ''); + } +} diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index bb66f80c0fe..3cc3db340fd 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -4,7 +4,6 @@ namespace Rector\Php80\Rector\Class_; -use PhpParser\Comment; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\Variable; @@ -16,12 +15,9 @@ use PhpParser\Node\Stmt\Property; use PhpParser\Node\UnionType; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; -use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\Reflection\ClassReflection; use PHPStan\Type\MixedType; use PHPStan\Type\TypeCombinator; -use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; -use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter; use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey; use Rector\Core\Contract\Rector\ConfigurableRectorInterface; use Rector\Core\NodeAnalyzer\ParamAnalyzer; @@ -29,10 +25,10 @@ use Rector\Core\Reflection\ReflectionResolver; use Rector\Core\ValueObject\MethodName; use Rector\Core\ValueObject\PhpVersionFeature; -use Rector\DeadCode\PhpDoc\TagRemover\VarTagRemover; use Rector\Naming\VariableRenamer; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\TypeComparator\TypeComparator; +use Rector\Php80\DocBlock\PropertyPromotionDocBlockMerger; use Rector\Php80\Guard\MakePropertyPromotionGuard; use Rector\Php80\NodeAnalyzer\PromotedPropertyCandidateResolver; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; @@ -66,13 +62,11 @@ final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRect public function __construct( private readonly PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver, private readonly VariableRenamer $variableRenamer, - private readonly VarTagRemover $varTagRemover, private readonly ParamAnalyzer $paramAnalyzer, - private readonly PhpDocTypeChanger $phpDocTypeChanger, + private readonly PropertyPromotionDocBlockMerger $propertyPromotionDocBlockMerger, private readonly MakePropertyPromotionGuard $makePropertyPromotionGuard, private readonly TypeComparator $typeComparator, private readonly ReflectionResolver $reflectionResolver, - private readonly PhpDocInfoPrinter $phpDocInfoPrinter, ) { } @@ -186,7 +180,12 @@ public function refactor(Node $node): ?Node $paramTagValueNode = $constructorPhpDocInfo->getParamTagValueByName($paramName); if (! $paramTagValueNode instanceof ParamTagValueNode) { - $this->decorateParamWithPropertyPhpDocInfo($constructClassMethod, $property, $param, $paramName); + $this->propertyPromotionDocBlockMerger->decorateParamWithPropertyPhpDocInfo( + $constructClassMethod, + $property, + $param, + $paramName + ); } elseif ($paramTagValueNode->parameterName !== '$' . $propertyName) { $paramTagValueNode->parameterName = '$' . $propertyName; $paramTagValueNode->setAttribute(PhpDocAttributeKey::ORIG_NODE, null); @@ -201,27 +200,11 @@ public function refactor(Node $node): ?Node $param->attrGroups = array_merge($param->attrGroups, $property->attrGroups); $this->processUnionType($property, $param); - $paramComments = $param->getComments(); - - // already has @param tag → give it priority over @var and remove @var - if ($paramTagValueNode instanceof ParamTagValueNode) { - $propertyDocInfo = $this->phpDocInfoFactory->createFromNode($property); - - if ($propertyDocInfo->hasByType(VarTagValueNode::class)) { - $propertyDocInfo->removeByType(VarTagValueNode::class); - - $propertyComments = $this->phpDocInfoPrinter->printToComments($propertyDocInfo); - $mergedComments = array_merge($paramComments, $propertyComments); - - $mergedComments = array_filter($mergedComments, function (Comment $comment) { - return $comment->getText() !== ''; - }); - - $param->setAttribute(AttributeKey::COMMENTS, $mergedComments); - } - } - - // $this->phpDocTypeChanger->convertPropertyVarTagToParamTag($constructClassMethod, $property, $param); + $this->propertyPromotionDocBlockMerger->mergePropertyAndParamDocBlocks( + $property, + $param, + $paramTagValueNode + ); } return $node; @@ -267,40 +250,6 @@ private function processUnionType(Property $property, Param $param): void $param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, TypeKind::PARAM); } - private function decorateParamWithPropertyPhpDocInfo( - ClassMethod $classMethod, - Property $property, - Param $param, - string $paramName - ): void { - $propertyPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property); - $propertyPhpDocInfo->markAsChanged(); - - $param->setAttribute(AttributeKey::PHP_DOC_INFO, $propertyPhpDocInfo); - - // make sure the docblock is useful - if ($param->type === null) { - $varTagValueNode = $propertyPhpDocInfo->getVarTagValueNode(); - if (! $varTagValueNode instanceof VarTagValueNode) { - return; - } - - $paramType = $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($varTagValueNode, $property); - $classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - $this->phpDocTypeChanger->changeParamType( - $classMethod, - $classMethodPhpDocInfo, - $paramType, - $param, - $paramName - ); - } else { - $paramType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type); - } - - $this->varTagRemover->removeVarPhpTagValueNodeIfNotComment($param, $paramType); - } - private function shouldSkipParam(Param $param): bool { if ($param->variadic) {