From 688804984677d51dcf0aa7b64c92081182efd0f2 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 13 Jun 2021 13:26:01 +0200 Subject: [PATCH] [PHP 8.0] Avoid duplicating attributed annotations (#212) --- .../skip_already_added.php.inc | 13 +++++++ .../NodeAnalyzer/PhpAttributeAnalyzer.php | 34 +++++++++++++++++++ ...ctrineAnnotationClassToAttributeRector.php | 25 +++++++++++--- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc create mode 100644 rules/Php80/NodeAnalyzer/PhpAttributeAnalyzer.php diff --git a/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc new file mode 100644 index 00000000000..873cc9eea95 --- /dev/null +++ b/rules-tests/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector/FixtureShouldNotRemoveAnnotation/skip_already_added.php.inc @@ -0,0 +1,13 @@ +attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attribute) { + if (! $this->nodeNameResolver->isName($attribute->name, $attributeClass)) { + continue; + } + + return true; + } + } + + return false; + } +} diff --git a/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php b/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php index d89be90e585..6af3acbce3d 100644 --- a/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php +++ b/rules/Php80/Rector/Class_/DoctrineAnnotationClassToAttributeRector.php @@ -16,6 +16,7 @@ use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode; use Rector\Core\Contract\Rector\ConfigurableRectorInterface; use Rector\Core\Rector\AbstractRector; +use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer; use Rector\Php80\NodeFactory\AttributeFlagFactory; use Rector\PhpAttribute\Printer\PhpAttributeGroupFactory; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; @@ -51,12 +52,18 @@ final class DoctrineAnnotationClassToAttributeRector extends AbstractRector impl 'ANNOTATION' => 'TARGET_CLASS', ]; + /** + * @var string + */ + private const ATTRIBUTE = 'Attribute'; + private bool $shouldRemoveAnnotations = true; public function __construct( private PhpDocTagRemover $phpDocTagRemover, private AttributeFlagFactory $attributeFlagFactory, - private PhpAttributeGroupFactory $phpAttributeGroupFactory + private PhpAttributeGroupFactory $phpAttributeGroupFactory, + private PhpAttributeAnalyzer $phpAttributeAnalyzer ) { } @@ -111,7 +118,7 @@ public function refactor(Node $node): ?Node return null; } - if (! $phpDocInfo->hasByNames(['Annotation', 'annotation'])) { + if ($this->shouldSkipClass($phpDocInfo, $node)) { return null; } @@ -120,7 +127,7 @@ public function refactor(Node $node): ?Node $this->phpDocTagRemover->removeByName($phpDocInfo, 'Annotation'); } - $attributeGroup = $this->phpAttributeGroupFactory->createFromClass('Attribute'); + $attributeGroup = $this->phpAttributeGroupFactory->createFromClass(self::ATTRIBUTE); $this->decorateTarget($phpDocInfo, $attributeGroup); foreach ($node->getProperties() as $property) { @@ -179,7 +186,7 @@ private function resolveFlags(array $targetValues): array continue; } - $flags[] = $this->nodeFactory->createClassConstFetch('Attribute', $constant); + $flags[] = $this->nodeFactory->createClassConstFetch(self::ATTRIBUTE, $constant); } return $flags; @@ -214,4 +221,14 @@ private function decorateTarget(PhpDocInfo $phpDocInfo, AttributeGroup $attribut $attributeGroup->attrs[0]->args[] = new Arg($flagCollection); } + + private function shouldSkipClass(PhpDocInfo $phpDocInfo, Class_ $class): bool + { + if (! $phpDocInfo->hasByNames(['Annotation', 'annotation'])) { + return true; + } + + // has attribute? skip it + return $this->phpAttributeAnalyzer->hasPhpAttribute($class, self::ATTRIBUTE); + } }