Skip to content

Commit b6edd89

Browse files
samsonasiksimonschaufiactions-user
authored
[Renaming] Handle namespaced annotation on RenameClassRector (#7333)
* Add example for issue #9388 * Closes #7330 * [ci-review] Rector Rectify * Fix phpstan * clean up fixture * [ci-review] Rector Rectify --------- Co-authored-by: Simon Schaufelberger <simonschaufi@users.noreply.github.com> Co-authored-by: GitHub Action <actions@github.com>
1 parent fe64244 commit b6edd89

File tree

11 files changed

+375
-7
lines changed

11 files changed

+375
-7
lines changed

rules/CodingStyle/Node/NameImporter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ private function addUseImport(
138138
FullyQualified $fullyQualified,
139139
FullyQualifiedObjectType $fullyQualifiedObjectType
140140
): void {
141-
if ($this->useNodesToAddCollector->hasImport($file, $fullyQualified, $fullyQualifiedObjectType)) {
141+
if ($this->useNodesToAddCollector->hasImport($file, $fullyQualifiedObjectType)) {
142142
return;
143143
}
144144

src/NodeTypeResolver/PhpDocNodeVisitor/NameImportingPhpDocNodeVisitor.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ private function processFqnNameImport(
146146
return null;
147147
}
148148

149-
if ($this->shouldImport($newNode, $identifierTypeNode, $fullyQualifiedObjectType)) {
149+
if ($this->shouldImport($file, $newNode, $identifierTypeNode, $fullyQualifiedObjectType)) {
150150
$this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType);
151151
$this->hasChanged = true;
152152

@@ -157,6 +157,7 @@ private function processFqnNameImport(
157157
}
158158

159159
private function shouldImport(
160+
File $file,
160161
IdentifierTypeNode $newNode,
161162
IdentifierTypeNode $identifierTypeNode,
162163
FullyQualifiedObjectType $fullyQualifiedObjectType
@@ -181,7 +182,7 @@ private function shouldImport(
181182

182183
$firstPath = Strings::before($identifierTypeNode->name, '\\' . $newNode->name);
183184
if ($firstPath === null) {
184-
return true;
185+
return ! $this->useNodesToAddCollector->hasImport($file, $fullyQualifiedObjectType);
185186
}
186187

187188
if ($firstPath === '') {

src/PostRector/Collector/UseNodesToAddCollector.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Rector\PostRector\Collector;
66

77
use PhpParser\Node\Identifier;
8-
use PhpParser\Node\Name\FullyQualified;
98
use Rector\Application\Provider\CurrentFileProvider;
109
use Rector\Naming\Naming\UseImportsResolver;
1110
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
@@ -86,7 +85,6 @@ public function getUseImportTypesByNode(File $file): array
8685

8786
public function hasImport(
8887
File $file,
89-
FullyQualified $fullyQualified,
9088
FullyQualifiedObjectType $fullyQualifiedObjectType
9189
): bool {
9290
$useImports = $this->getUseImportTypesByNode($file);

src/StaticTypeMapper/PhpDocParser/IdentifierPhpDocTypeMapper.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ public function mapIdentifierTypeNode(IdentifierTypeNode $identifierTypeNode, No
9797
return new UnionType($scalarTypes);
9898
}
9999

100-
$identifierTypeNode->name = ltrim($identifierTypeNode->name, '@');
101-
$objectType = new ObjectType($identifierTypeNode->name);
100+
$objectType = new ObjectType(ltrim($identifierTypeNode->name, '@'));
102101
}
103102

104103
$scope = $node->getAttribute(AttributeKey::SCOPE);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Rector\Tests\Issues\Issue9388\Fixture;
4+
5+
final class MyClass
6+
{
7+
/**
8+
* @var string
9+
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
10+
*/
11+
protected $name = '';
12+
13+
/**
14+
* @TYPO3\CMS\Extbase\Annotation\Validate("NotEmpty")
15+
*/
16+
protected $thisWorks = '';
17+
}
18+
-----
19+
<?php
20+
21+
namespace Rector\Tests\Issues\Issue9388\Fixture;
22+
23+
final class MyClass
24+
{
25+
/**
26+
* @var string
27+
*/
28+
#[\TYPO3\CMS\Extbase\Annotation\Validate(['validator' => 'NotEmpty'])]
29+
protected $name = '';
30+
31+
#[\TYPO3\CMS\Extbase\Annotation\Validate(['validator' => 'NotEmpty'])]
32+
protected $thisWorks = '';
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\Issue9388;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
/**
12+
* @see https://github.com/rectorphp/rector/issues/9388
13+
*/
14+
final class Issue9388Test extends AbstractRectorTestCase
15+
{
16+
#[DataProvider('provideData')]
17+
public function test(string $filePath): void
18+
{
19+
$this->doTestFile($filePath);
20+
}
21+
22+
public static function provideData(): Iterator
23+
{
24+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
25+
}
26+
27+
public function provideConfigFilePath(): string
28+
{
29+
return __DIR__ . '/config/configured_rule.php';
30+
}
31+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute;
6+
7+
use PhpParser\Node\Attribute;
8+
9+
final readonly class AttributeDecorator
10+
{
11+
/**
12+
* @param AttributeDecoratorInterface[] $decorators
13+
*/
14+
public function __construct(private array $decorators)
15+
{
16+
}
17+
18+
public function decorate(string $phpAttributeName, Attribute $attribute): void
19+
{
20+
foreach ($this->decorators as $decorator) {
21+
if ($decorator->supports($phpAttributeName)) {
22+
$decorator->decorate($attribute);
23+
}
24+
}
25+
}
26+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute;
6+
7+
use PhpParser\Node\Attribute;
8+
9+
interface AttributeDecoratorInterface
10+
{
11+
public function supports(string $phpAttributeName): bool;
12+
13+
public function decorate(Attribute $attribute): void;
14+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute;
6+
7+
use PhpParser\Node\Arg;
8+
use PhpParser\Node\ArrayItem;
9+
use PhpParser\Node\Attribute;
10+
use PhpParser\Node\Expr\Array_;
11+
use PhpParser\Node\Expr\ClassConstFetch;
12+
use PhpParser\Node\Identifier;
13+
use PhpParser\Node\Scalar\String_;
14+
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
15+
use Rector\PhpParser\Node\Value\ValueResolver;
16+
17+
final readonly class ValidateAttributeDecorator implements AttributeDecoratorInterface
18+
{
19+
public function __construct(private ValueResolver $valueResolver, private StringClassNameToClassConstantRector $stringClassNameToClassConstantRector)
20+
{
21+
}
22+
23+
public function supports(string $phpAttributeName): bool
24+
{
25+
return $phpAttributeName === 'TYPO3\CMS\Extbase\Annotation\Validate';
26+
}
27+
28+
public function decorate(Attribute $attribute): void
29+
{
30+
$array = new Array_();
31+
32+
foreach ($attribute->args as $arg) {
33+
$key = $arg->name instanceof Identifier ? new String_($arg->name->toString()) : new String_('validator');
34+
35+
if ($this->valueResolver->isValue($key, 'validator')) {
36+
$classNameString = $this->valueResolver->getValue($arg->value);
37+
if (! is_string($classNameString)) {
38+
continue;
39+
}
40+
41+
$className = ltrim($classNameString, '\\');
42+
$classConstant = $this->stringClassNameToClassConstantRector->refactor(new String_($className));
43+
$value = $classConstant instanceof ClassConstFetch ? $classConstant : $arg->value;
44+
} else {
45+
$value = $arg->value;
46+
}
47+
48+
$array->items[] = new ArrayItem($value, $key);
49+
}
50+
51+
$attribute->args = [new Arg($array)];
52+
}
53+
}

0 commit comments

Comments
 (0)