[Attribute] Fix UseAliasNameMatcher for the last part of use import rename (#2402)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Tomas Votruba 2022-05-31 17:40:56 +02:00 committed by GitHub
parent 02803bf7d5
commit 73055d87fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 33 deletions

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Rector\Tests\PhpAttribute;
use Iterator;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
@ -11,21 +12,13 @@ use Rector\Php80\ValueObject\AnnotationToAttribute;
use Rector\PhpAttribute\UseAliasNameMatcher;
use Rector\PhpAttribute\ValueObject\UseAliasMetadata;
use Rector\Testing\PHPUnit\AbstractTestCase;
use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\Annotation\NestedPastAnnotation;
use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\PastAnnotation;
use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Attribute\NestedFutureAttribute;
use Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\FutureAttribute;
final class UseAliasNameMatcherTest extends AbstractTestCase
{
/**
* @var string
*/
private const USE_IMPORT_NAME = 'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi';
/**
* @var string
*/
private const USE_ALIAS = 'OA';
private UseAliasNameMatcher $useAliasNameMatcher;
protected function setUp(): void
@ -34,24 +27,66 @@ final class UseAliasNameMatcherTest extends AbstractTestCase
$this->useAliasNameMatcher = $this->getService(UseAliasNameMatcher::class);
}
public function test(): void
{
$annotationToAttribute = new AnnotationToAttribute(PastAnnotation::class, FutureAttribute::class);
$uses = [new Use_([new UseUse(new Name(self::USE_IMPORT_NAME), self::USE_ALIAS), self::USE_ALIAS])];
/**
* @dataProvider provideData()
*/
public function test(
AnnotationToAttribute $annotationToAttribute,
string $useImportName,
string $useAlias,
string $shortAnnotationName,
// attribute
string $expectedAttributeUseImportName,
string $expectedShortAttributeName,
): void {
$uses = [new Use_([new UseUse(new Name($useImportName), $useAlias), $useAlias])];
// uses
$useAliasMetadata = $this->useAliasNameMatcher->match($uses, '@OA\PastAnnotation', $annotationToAttribute);
$useAliasMetadata = $this->useAliasNameMatcher->match($uses, $shortAnnotationName, $annotationToAttribute);
$this->assertInstanceOf(UseAliasMetadata::class, $useAliasMetadata);
// test new use import
$this->assertSame('OA\FutureAttribute', $useAliasMetadata->getShortAttributeName());
$this->assertSame($expectedShortAttributeName, $useAliasMetadata->getShortAttributeName());
// test new short attribute name
$this->assertSame(
$this->assertSame($expectedAttributeUseImportName, $useAliasMetadata->getUseImportName());
}
public function provideData(): Iterator
{
yield [
// configuration
new AnnotationToAttribute(PastAnnotation::class, FutureAttribute::class),
// use import
'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi',
// use import alias
'OA',
// short attribute name
'@OA\PastAnnotation',
// expected attribute import
'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi',
$useAliasMetadata->getUseImportName()
);
// expected attribute short name
'OA\FutureAttribute',
];
yield [
// configuration
new AnnotationToAttribute(NestedPastAnnotation::class, NestedFutureAttribute::class),
// use import
'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Annotation\OpenApi\Annotation',
// use import alias
'OA',
// short attribute name
'@OA\NestedPastAnnotation',
// expected attribute import
'Rector\Tests\Php80\Rector\Class_\AnnotationToAttributeRector\Source\Attribute\OpenApi\Attribute',
// expected attribute short name
'OA\NestedFutureAttribute',
];
}
}

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\PhpAttribute;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt\Use_;
use Rector\Php80\ValueObject\AnnotationToAttribute;
use Rector\PhpAttribute\ValueObject\UseAliasMetadata;
@ -35,23 +34,31 @@ final class UseAliasNameMatcher
continue;
}
$importName = $useUse->name->toString();
// previous keyword
$lastImportKeyword = Strings::after($importName, '\\', -1);
if ($lastImportKeyword === null) {
$fullyQualifiedAnnotationName = $useUse->name->toString() . ltrim($shortAnnotationName, $alias);
if ($fullyQualifiedAnnotationName !== $annotationToAttribute->getTag()) {
continue;
}
// resolve new short name
$newShortname = Strings::after($annotationToAttribute->getAttributeClass(), $lastImportKeyword);
$annotationParts = explode('\\', $fullyQualifiedAnnotationName);
$attributeParts = explode('\\', $annotationToAttribute->getAttributeClass());
$beforeImportName = Strings::before(
$annotationToAttribute->getAttributeClass(),
$lastImportKeyword
) . $lastImportKeyword;
// requirement for matching single part rename
if (count($annotationParts) !== count($attributeParts)) {
continue;
}
return new UseAliasMetadata($alias . $newShortname, $beforeImportName, $useUse);
// now we now we are matching correct contanct and old and new have the same number of parts
$useImportPartCount = substr_count($useUse->name->toString(), '\\') + 1;
$newAttributeImportPart = array_slice($attributeParts, 0, $useImportPartCount);
$newAttributeImport = implode('\\', $newAttributeImportPart);
$shortNamePartCount = count($attributeParts) - $useImportPartCount;
// +1, to remove the alias part
$attributeParts = array_slice($attributeParts, -$shortNamePartCount);
$shortAttributeName = $alias . '\\' . implode('\\', $attributeParts);
return new UseAliasMetadata($shortAttributeName, $newAttributeImport, $useUse);
}
}