From 503b48461158132face8980f422c780f4d9c3565 Mon Sep 17 00:00:00 2001 From: Ian Rosner Date: Sun, 16 Nov 2025 21:55:25 +0200 Subject: [PATCH 1/4] fix phpunit covers default class with short covers methods --- .../Fixture/covers_class_default.php.inc | 2 ++ ...oversAnnotationWithValueToAttributeRector.php | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class_default.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class_default.php.inc index 4b666909..58715b9b 100644 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class_default.php.inc +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class_default.php.inc @@ -36,7 +36,9 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnot use PHPUnit\Framework\TestCase; #[\PHPUnit\Framework\Attributes\CoversClass(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass::class, 'foo')] #[\PHPUnit\Framework\Attributes\CoversClass(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\HelperClass::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass::class, 'bar')] final class CoversClassDefault extends TestCase { public function testFoo() diff --git a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php index 25d79096..4483ad49 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php @@ -186,16 +186,18 @@ private function resolveClassAttributes(Class_ $class): array $coversGroups = []; $methodGroups = []; $hasCoversDefault = false; + $coversDefaultClass = ''; $phpDocInfo = $this->phpDocInfoFactory->createFromNode($class); if ($phpDocInfo instanceof PhpDocInfo) { $coversDefaultGroups = $this->handleCoversDefaultClass($phpDocInfo); // If there is a ::coversDefaultClass, @covers ::function will refer to class methods, otherwise it will refer to global functions. $hasCoversDefault = $coversDefaultGroups !== []; + $coversDefaultClass = $hasCoversDefault ? $coversDefaultGroups[0]->attrs[0]->args[0]->value->class->name : null; $coversGroups = $this->handleCovers($phpDocInfo, $hasCoversDefault); } foreach ($class->getMethods() as $classMethod) { - $methodGroups = [...$methodGroups, ...$this->resolveMethodAttributes($classMethod, $hasCoversDefault)]; + $methodGroups = [...$methodGroups, ...$this->resolveMethodAttributes($classMethod, $coversDefaultClass)]; } return [...$coversDefaultGroups, ...$coversGroups, ...$methodGroups]; @@ -260,8 +262,9 @@ private function handleCovers(PhpDocInfo $phpDocInfo, bool $hasCoversDefault): a /** * @return array */ - private function resolveMethodAttributes(ClassMethod $classMethod, bool $hasCoversDefault): array + private function resolveMethodAttributes(ClassMethod $classMethod, ?string $coversDefaultClass): array { + $hasCoversDefault = $coversDefaultClass !== null; $phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod); if (! $phpDocInfo instanceof PhpDocInfo) { return []; @@ -283,6 +286,15 @@ private function resolveMethodAttributes(ClassMethod $classMethod, bool $hasCove continue; } + $attributeGroups[$covers] = $attributeGroup; + } elseif ($hasCoversDefault && str_starts_with($covers, '::')) { + $attributeGroup = $this->createAttributeGroup($coversDefaultClass . $covers); + + // phpunit 10 may not fully support attribute + if (! $attributeGroup instanceof AttributeGroup) { + continue; + } + $attributeGroups[$covers] = $attributeGroup; } } From 59e112de1040f9d56aab7e0d18d9924c3bee7a6e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Nov 2025 09:25:42 +0700 Subject: [PATCH 2/4] Fix phpstan --- .../Class_/CoversAnnotationWithValueToAttributeRector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php index 4483ad49..f711fd9b 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php @@ -192,7 +192,7 @@ private function resolveClassAttributes(Class_ $class): array $coversDefaultGroups = $this->handleCoversDefaultClass($phpDocInfo); // If there is a ::coversDefaultClass, @covers ::function will refer to class methods, otherwise it will refer to global functions. $hasCoversDefault = $coversDefaultGroups !== []; - $coversDefaultClass = $hasCoversDefault ? $coversDefaultGroups[0]->attrs[0]->args[0]->value->class->name : null; + $coversDefaultClass = $hasCoversDefault ? $this->getName($coversDefaultGroups[0]->attrs[0]->args[0]->value) : null; $coversGroups = $this->handleCovers($phpDocInfo, $hasCoversDefault); } From 41e355ce2425375a4c2d27a5df490834ce2aed00 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Nov 2025 09:28:16 +0700 Subject: [PATCH 3/4] fix cs --- .../Class_/CoversAnnotationWithValueToAttributeRector.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php index f711fd9b..d4446d7a 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php @@ -192,7 +192,9 @@ private function resolveClassAttributes(Class_ $class): array $coversDefaultGroups = $this->handleCoversDefaultClass($phpDocInfo); // If there is a ::coversDefaultClass, @covers ::function will refer to class methods, otherwise it will refer to global functions. $hasCoversDefault = $coversDefaultGroups !== []; - $coversDefaultClass = $hasCoversDefault ? $this->getName($coversDefaultGroups[0]->attrs[0]->args[0]->value) : null; + $coversDefaultClass = $hasCoversDefault ? $this->getName( + $coversDefaultGroups[0]->attrs[0]->args[0]->value + ) : null; $coversGroups = $this->handleCovers($phpDocInfo, $hasCoversDefault); } From b4a1a1796d4c903ab44ed587302c747b5dd7355e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Mon, 17 Nov 2025 09:36:18 +0700 Subject: [PATCH 4/4] unify logic --- ...rsAnnotationWithValueToAttributeRector.php | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php index d4446d7a..1be0b56e 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php @@ -280,25 +280,20 @@ private function resolveMethodAttributes(ClassMethod $classMethod, ?string $cove } $covers = $desiredTagValueNode->value->value; - if (str_starts_with($covers, '\\') || (! $hasCoversDefault && str_starts_with($covers, '::'))) { - $attributeGroup = $this->createAttributeGroup($covers); - - // phpunit 10 may not fully support attribute - if (! $attributeGroup instanceof AttributeGroup) { - continue; - } - - $attributeGroups[$covers] = $attributeGroup; - } elseif ($hasCoversDefault && str_starts_with($covers, '::')) { - $attributeGroup = $this->createAttributeGroup($coversDefaultClass . $covers); + if (! str_starts_with($covers, '\\') && ! str_starts_with($covers, '::')) { + continue; + } - // phpunit 10 may not fully support attribute - if (! $attributeGroup instanceof AttributeGroup) { - continue; - } + if ($hasCoversDefault && str_starts_with($covers, '::')) { + $covers = $coversDefaultClass . $covers; + } - $attributeGroups[$covers] = $attributeGroup; + $attributeGroup = $this->createAttributeGroup($covers); + if (! $attributeGroup instanceof AttributeGroup) { + continue; } + + $attributeGroups[$covers] = $attributeGroup; } return $attributeGroups;