diff --git a/README.md b/README.md index 21112d0..1d2f8bd 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,9 @@ These are the available attributes and their corresponding PHPDoc annotations: | [Template](https://github.com/php-static-analysis/attributes/blob/main/doc/Template.md) | `@template` | | [TemplateContravariant](https://github.com/php-static-analysis/attributes/blob/main/doc/TemplateContravariant.md) | `@template-contravariant` | | [TemplateCovariant](https://github.com/php-static-analysis/attributes/blob/main/doc/TemplateCovariant.md) | `@template-covariant` | +| [TemplateExtends](https://github.com/php-static-analysis/attributes/blob/main/doc/TemplateExtends.md) | `@extends` `@template-extends` | +| [TemplateImplements](https://github.com/php-static-analysis/attributes/blob/main/doc/TemplateImplements.md) | `@implements` `@template-implements` | +| [TemplateUse](https://github.com/php-static-analysis/attributes/blob/main/doc/TemplateUse.md) | `@use` `@template-use` | | [Type](https://github.com/php-static-analysis/attributes/blob/main/doc/Type.md) | `@var` `@return` | ### Location of Param attributes diff --git a/composer.json b/composer.json index 926b261..c960a5d 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "require": { "php": ">=8.0", "cweagans/composer-patches": "^1.7", - "php-static-analysis/attributes": "^0.1.10 || dev-main", + "php-static-analysis/attributes": "^0.1.11 || dev-main", "rector/rector": "^0.19 || ^1.0" }, "require-dev": { diff --git a/config/sets/php-static-analysis-annotations-to-attributes.php b/config/sets/php-static-analysis-annotations-to-attributes.php index e8e80ca..d58ddd8 100644 --- a/config/sets/php-static-analysis-annotations-to-attributes.php +++ b/config/sets/php-static-analysis-annotations-to-attributes.php @@ -10,6 +10,9 @@ use PhpStaticAnalysis\Attributes\PropertyWrite; use PhpStaticAnalysis\Attributes\TemplateContravariant; use PhpStaticAnalysis\Attributes\TemplateCovariant; +use PhpStaticAnalysis\Attributes\TemplateExtends; +use PhpStaticAnalysis\Attributes\TemplateImplements; +use PhpStaticAnalysis\Attributes\TemplateUse; use Rector\Config\RectorConfig; use Rector\Php80\ValueObject\AnnotationToAttribute; use PhpStaticAnalysis\Attributes\IsReadOnly; @@ -25,6 +28,8 @@ AnnotationsToAttributesRector::class, [ new AnnotationToAttribute('deprecated', Deprecated::class), + new AnnotationToAttribute('extends', TemplateExtends::class), + new AnnotationToAttribute('implements', TemplateImplements::class), new AnnotationToAttribute('internal', Internal::class), new AnnotationToAttribute('method', Method::class), new AnnotationToAttribute('mixin', Mixin::class), @@ -37,6 +42,10 @@ new AnnotationToAttribute('template', Template::class), new AnnotationToAttribute('template_contravariant', TemplateContravariant::class), new AnnotationToAttribute('template_covariant', TemplateCovariant::class), + new AnnotationToAttribute('template_extends', TemplateExtends::class), + new AnnotationToAttribute('template_implements', TemplateImplements::class), + new AnnotationToAttribute('template_use', TemplateUse::class), + new AnnotationToAttribute('use', TemplateUse::class), new AnnotationToAttribute('var', Type::class), 'addParamAttributeOnParameters' => false, 'useTypeAttributeForReturnAnnotation' => false, diff --git a/src/AnnotationsToAttributesRector.php b/src/AnnotationsToAttributesRector.php index 3b56d00..b284eca 100644 --- a/src/AnnotationsToAttributesRector.php +++ b/src/AnnotationsToAttributesRector.php @@ -12,7 +12,9 @@ use PhpParser\Node\Scalar; use PhpParser\Node\Stmt; use PHPStan\PhpDocParser\Ast\PhpDoc\DeprecatedTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; @@ -20,6 +22,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\UsesTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PhpStaticAnalysis\Attributes\Param; @@ -165,19 +168,17 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); - if (!$phpDocInfo instanceof PhpDocInfo) { - return null; - } - - $attributeGroups = $this->processAnnotations($phpDocInfo); - if ($attributeGroups === []) { - return null; + $attributeGroups = []; + if ($phpDocInfo instanceof PhpDocInfo) { + $attributeGroups = $this->processAnnotations($phpDocInfo); } - $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); + if ($attributeGroups !== []) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); - $this->attributeGroupNamedArgumentManipulator->decorate($attributeGroups); + $this->attributeGroupNamedArgumentManipulator->decorate($attributeGroups); + } if ($this->addParamAttributeOnParameters && ($node instanceof Stmt\ClassMethod || $node instanceof Stmt\Function_)) { @@ -207,7 +208,27 @@ public function refactor(Node $node): ?Node } } - $node->attrGroups = array_merge($node->attrGroups, $attributeGroups); + if ($node instanceof Stmt\Class_) { + foreach ($node->stmts as $stmt) { + if ($stmt instanceof Stmt\TraitUse) { + $phpDocInfo = $this->phpDocInfoFactory->createFromNode($stmt); + if ($phpDocInfo instanceof PhpDocInfo) { + $useAttributeGroups = $this->processAnnotations($phpDocInfo); + + if ($useAttributeGroups !== []) { + $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($stmt); + + $this->attributeGroupNamedArgumentManipulator->decorate($useAttributeGroups); + $attributeGroups = array_merge($attributeGroups, $useAttributeGroups); + } + } + } + } + } + + if ($attributeGroups !== []) { + $node->attrGroups = array_merge($node->attrGroups, $attributeGroups); + } return $node; } @@ -258,8 +279,11 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array ]; $attributeComment = $tagValueNode->description; break; + case $tagValueNode instanceof ExtendsTagValueNode: + case $tagValueNode instanceof ImplementsTagValueNode: case $tagValueNode instanceof MixinTagValueNode: case $tagValueNode instanceof ReturnTagValueNode: + case $tagValueNode instanceof UsesTagValueNode: case $tagValueNode instanceof VarTagValueNode: $args = [ new Node\Arg(new Scalar\String_((string)($tagValueNode->type))) diff --git a/tests/Fixture/TemplateExtendsAttributeTest.php.inc b/tests/Fixture/TemplateExtendsAttributeTest.php.inc new file mode 100644 index 0000000..063d13c --- /dev/null +++ b/tests/Fixture/TemplateExtendsAttributeTest.php.inc @@ -0,0 +1,69 @@ + this is the extended class + */ +#[Property(name:'string')] +class TemplateExtendsAttributeTest extends TemplateClass +{ +} + +/** + * @template-extends TemplateClass + */ +class TemplateExtendsAttributeTest2 extends TemplateClass +{ +} + +/** + * @phpstan-extends TemplateClass + */ +class TemplateExtendsAttributeTest3 extends TemplateClass +{ +} + +?> +----- +')] // this is the extended class +class TemplateExtendsAttributeTest extends TemplateClass +{ +} + +#[\PhpStaticAnalysis\Attributes\TemplateExtends('TemplateClass')] +class TemplateExtendsAttributeTest2 extends TemplateClass +{ +} + +#[\PhpStaticAnalysis\Attributes\TemplateExtends('TemplateClass')] +class TemplateExtendsAttributeTest3 extends TemplateClass +{ +} + +?> diff --git a/tests/Fixture/TemplateImplementsAttributeTest.php.inc b/tests/Fixture/TemplateImplementsAttributeTest.php.inc new file mode 100644 index 0000000..5a88555 --- /dev/null +++ b/tests/Fixture/TemplateImplementsAttributeTest.php.inc @@ -0,0 +1,69 @@ + this is the implemented interface + */ +#[Property(name:'string')] +class TemplateImplementsAttributeTest implements TemplateInterface +{ +} + +/** + * @template-implements TemplateInterface + */ +class TemplateImplementsAttributeTest2 implements TemplateInterface +{ +} + +/** + * @phpstan-implements TemplateInterface + */ +class TemplateImplementsAttributeTest3 implements TemplateInterface +{ +} + +?> +----- +')] // this is the implemented interface +class TemplateImplementsAttributeTest implements TemplateInterface +{ +} + +#[\PhpStaticAnalysis\Attributes\TemplateImplements('TemplateInterface')] +class TemplateImplementsAttributeTest2 implements TemplateInterface +{ +} + +#[\PhpStaticAnalysis\Attributes\TemplateImplements('TemplateInterface')] +class TemplateImplementsAttributeTest3 implements TemplateInterface +{ +} + +?> diff --git a/tests/Fixture/TemplateUseAttributeTest.php.inc b/tests/Fixture/TemplateUseAttributeTest.php.inc new file mode 100644 index 0000000..e6acb45 --- /dev/null +++ b/tests/Fixture/TemplateUseAttributeTest.php.inc @@ -0,0 +1,75 @@ + this is the used trait + */ + use TemplateTrait; +} + +class TemplateUseAttributeTest2 +{ + /** + * @template-use TemplateTrait + */ + use TemplateTrait; +} + +class TemplateUseAttributeTest3 +{ + /** + * @phpstan-use TemplateTrait + */ + use TemplateTrait; +} + +?> +----- +')] // this is the used trait +class TemplateUseAttributeTest +{ + /** + * @codeCoverageIgnore + */ + use TemplateTrait; +} + +#[\PhpStaticAnalysis\Attributes\TemplateUse('TemplateTrait')] +class TemplateUseAttributeTest2 +{ + use TemplateTrait; +} + +#[\PhpStaticAnalysis\Attributes\TemplateUse('TemplateTrait')] +class TemplateUseAttributeTest3 +{ + use TemplateTrait; +} + +?>