Skip to content

Commit

Permalink
Add RequireExtends and RequireImplements attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Feb 24, 2024
1 parent 1028781 commit 1eaf5e0
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 2 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"require": {
"php": ">=8.0",
"nikic/php-parser": "^4 || ^5",
"php-static-analysis/attributes": "^0.1.13 || dev-main"
"php-static-analysis/attributes": "^0.1.14 || dev-main"
},
"require-dev": {
"php-static-analysis/phpstan-extension": "dev-main",
Expand Down
25 changes: 25 additions & 0 deletions src/AttributeNodeVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use PhpStaticAnalysis\Attributes\Property;
use PhpStaticAnalysis\Attributes\PropertyRead;
use PhpStaticAnalysis\Attributes\PropertyWrite;
use PhpStaticAnalysis\Attributes\RequireExtends;
use PhpStaticAnalysis\Attributes\RequireImplements;
use PhpStaticAnalysis\Attributes\Returns;
use PhpStaticAnalysis\Attributes\SelfOut;
use PhpStaticAnalysis\Attributes\Template;
Expand All @@ -41,6 +43,7 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
private const ARGS_MANY_IN_USE = "many in use";
private const ARGS_MANY_WITH_NAME = "many with name";
private const ARGS_MANY_WITHOUT_NAME = "many without name";
private const ARGS_MANY_WITHOUT_NAME_AND_PREFIX = "many without name and prexif";

private const ALLOWED_NODE_TYPES = [
Stmt\Class_::class,
Expand Down Expand Up @@ -119,6 +122,8 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
Property::class,
PropertyRead::class,
PropertyWrite::class,
RequireExtends::class,
RequireImplements::class,
Template::class,
TemplateContravariant::class,
TemplateCovariant::class,
Expand All @@ -136,6 +141,8 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
'Property' => Property::class,
'PropertyRead' => PropertyRead::class,
'PropertyWrite' => PropertyWrite::class,
'RequireExtends' => RequireExtends::class,
'RequireImplements' => RequireImplements::class,
'Returns' => Returns::class,
'SelfOut' => SelfOut::class,
'Template' => Template::class,
Expand Down Expand Up @@ -179,6 +186,12 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
PropertyWrite::class => [
'all' => 'property-write',
],
RequireExtends::class => [
'all' => 'require-extends',
],
RequireImplements::class => [
'all' => 'require-implements',
],
Returns::class => [
'all' => 'return',
],
Expand Down Expand Up @@ -243,6 +256,12 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
PropertyWrite::class => [
'all' => self::ARGS_MANY_WITH_NAME,
],
RequireExtends::class => [
'all' => self::ARGS_ONE_WITH_PREFIX,
],
RequireImplements::class => [
'all' => self::ARGS_MANY_WITHOUT_NAME_AND_PREFIX,
],
Returns::class => [
'all' => self::ARGS_ONE,
],
Expand Down Expand Up @@ -365,6 +384,12 @@ public function enterNode(Node $node)
$tagCreated = true;
}
break;
case self::ARGS_MANY_WITHOUT_NAME_AND_PREFIX:
foreach ($args as $arg) {
$tagsToAdd[] = $this->createTag($nodeType, $attributeName, $arg, prefix: $this->toolType);
$tagCreated = true;
}
break;
case self::ARGS_MANY_IN_USE:
foreach ($args as $arg) {
if ($arg->value instanceof String_) {
Expand Down
31 changes: 31 additions & 0 deletions tests/RequireExtendsAttributeNodeVisitorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace test\PhpStaticAnalysis\NodeVisitor;

use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Name\FullyQualified;
use PhpStaticAnalysis\Attributes\RequireExtends;

class RequireExtendsAttributeNodeVisitorTest extends AttributeNodeVisitorTestBase
{
public function testAddsRequireExtendsPHPDoc(): void
{
$node = new Node\Stmt\Trait_('Test');
$this->addRequireExtendsAttributeToNode($node);
$this->nodeVisitor->enterNode($node);
$docText = $this->getDocText($node);
$this->assertEquals("/**\n * @require-extends RequireClass\n */", $docText);
}

private function addRequireExtendsAttributeToNode(Node\Stmt\Trait_ $node): void
{
$args = [
new Node\Arg(new Node\Scalar\String_('RequireClass'))
];
$attributeName = new FullyQualified(RequireExtends::class);
$attribute = new Attribute($attributeName, $args);
$node->attrGroups = array_merge($node->attrGroups, [new AttributeGroup([$attribute])]);
}
}
52 changes: 52 additions & 0 deletions tests/RequireImplementsAttributeNodeVisitorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace test\PhpStaticAnalysis\NodeVisitor;

use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Name\FullyQualified;
use PhpStaticAnalysis\Attributes\RequireImplements;

class RequireImplementsAttributeNodeVisitorTest extends AttributeNodeVisitorTestBase
{
public function testAddsRequireImplementsPHPDoc(): void
{
$node = new Node\Stmt\Trait_('Test');
$this->addRequireImplementsAttributesToNode($node);
$this->nodeVisitor->enterNode($node);
$docText = $this->getDocText($node);
$this->assertEquals("/**\n * @require-implements RequireInterface\n */", $docText);
}

public function testAddsSeveralRequireImplementsPHPDocs(): void
{
$node = new Node\Stmt\Trait_('Test');
$this->addRequireImplementsAttributesToNode($node, 2);
$this->nodeVisitor->enterNode($node);
$docText = $this->getDocText($node);
$this->assertEquals("/**\n * @require-implements RequireInterface\n * @require-implements RequireInterface\n */", $docText);
}

public function testAddsMultipleRequireImplementsPHPDocs(): void
{
$node = new Node\Stmt\Trait_('Test');
$this->addRequireImplementsAttributesToNode($node);
$this->addRequireImplementsAttributesToNode($node);
$this->nodeVisitor->enterNode($node);
$docText = $this->getDocText($node);
$this->assertEquals("/**\n * @require-implements RequireInterface\n * @require-implements RequireInterface\n */", $docText);
}

private function addRequireImplementsAttributesToNode(Node\Stmt\Trait_ $node, int $num = 1): void
{
$value = new Node\Scalar\String_('RequireInterface');
$args = [];
for ($i = 0; $i < $num; $i++) {
$args[] = new Node\Arg($value);
}
$attributeName = new FullyQualified(RequireImplements::class);
$attribute = new Attribute($attributeName, $args);
$node->attrGroups = array_merge($node->attrGroups, [new AttributeGroup([$attribute])]);
}
}
1 change: 0 additions & 1 deletion tests/TemplateExtendsAttributeNodeVisitorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace test\PhpStaticAnalysis\NodeVisitor;

use Exception;
use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
Expand Down

0 comments on commit 1eaf5e0

Please sign in to comment.