Skip to content

Commit

Permalink
Preserve comments from annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-granados committed Feb 21, 2024
1 parent 83ce0f0 commit dbf405d
Show file tree
Hide file tree
Showing 15 changed files with 169 additions and 11 deletions.
22 changes: 19 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"name": "php-static-analysis/rector-rule",
"description": "RectorPHP rule to convert PHPDoc annotations for static analysis to PHP attributes",
"type": "rector-extension",
"keywords": ["dev", "static analysis"],
"keywords": [
"dev",
"static analysis"
],
"license": "MIT",
"autoload": {
"psr-4": {
Expand All @@ -24,7 +27,8 @@
"prefer-stable": true,
"require": {
"php": ">=8.0",
"php-static-analysis/attributes": "^0.1.6 || dev-main",
"cweagans/composer-patches": "^1.7",
"php-static-analysis/attributes": "^0.1.7 || dev-main",
"rector/rector": "^0.19 || ^1.0"
},
"require-dev": {
Expand All @@ -34,6 +38,7 @@
"phpstan/phpstan": "^1.8",
"phpunit/phpunit": "^9.0",
"symplify/easy-coding-standard": "^12.1",
"symplify/vendor-patches": "^11.3",
"vimeo/psalm": "^5",
"webmozart/assert": "^1.11"
},
Expand All @@ -56,8 +61,19 @@
},
"config": {
"allow-plugins": {
"phpstan/extension-installer": true
"phpstan/extension-installer": true,
"cweagans/composer-patches": true
},
"sort-packages": true
},
"extra": {
"patches": {
"rector/rector": [
"patches/rector-rector-src-phpparser-printer-betterstandardprinter-php.patch",
"patches/rector-rector-src-nodetyperesolver-node-attributekey-php.patch"
]
},
"composer-exit-on-patch-failure": true,
"enable-patching": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- /dev/null
+++ ../src/NodeTypeResolver/Node/AttributeKey.php
@@ -249,4 +249,8 @@
* @var string
*/
public const IS_USED_AS_ARG_BY_REF_VALUE = 'is_used_as_arg_by_ref_value';
+ /**
+ * @var string
+ */
+ public const ATTRIBUTE_COMMENT = 'attribute_comment';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
--- /dev/null
+++ ../src/PhpParser/Printer/BetterStandardPrinter.php
@@ -113,6 +113,15 @@
$content = parent::p($node, $parentFormatPreserved);
return $node->getAttribute(AttributeKey::WRAPPED_IN_PARENTHESES) === \true ? '(' . $content . ')' : $content;
}
+ protected function pAttributeGroup(Node\AttributeGroup $node)
+ {
+ $ret = parent::pAttributeGroup($node);
+ $comment = $node->getAttribute(AttributeKey::ATTRIBUTE_COMMENT);
+ if (! in_array($comment, ['', null], true)) {
+ $ret .= ' // ' . $comment;
+ }
+ return $ret;
+ }
protected function pExpr_ArrowFunction(ArrowFunction $arrowFunction) : string
{
if (!$arrowFunction->hasAttribute(AttributeKey::COMMENT_CLOSURE_RETURN_MIRRORED)) {
21 changes: 19 additions & 2 deletions src/AnnotationsToAttributesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PhpStaticAnalysis\RectorRule;

use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
Expand All @@ -29,6 +30,7 @@
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php80\NodeManipulator\AttributeGroupNamedArgumentManipulator;
use Rector\Php80\ValueObject\AnnotationToAttribute;
use Rector\Rector\AbstractRector;
Expand Down Expand Up @@ -225,10 +227,16 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
}

$tagValueNode = $phpDocChildNode->value;
$attributeComment = null;
switch (true) {
case $tagValueNode instanceof MethodTagValueNode:
$methodSignature = (string)($tagValueNode);
$attributeComment = $tagValueNode->description;
if ($attributeComment) {
$methodSignature = substr($methodSignature, 0, -(strlen($attributeComment) + 1));
}
$args = [
new Node\Arg(new Scalar\String_((string)($tagValueNode)))
new Node\Arg(new Scalar\String_($methodSignature))
];
break;
case $tagValueNode instanceof ParamTagValueNode:
Expand All @@ -238,6 +246,7 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
name: new Node\Identifier(substr($tagValueNode->parameterName, 1))
)
];
$attributeComment = $tagValueNode->description;
break;
case $tagValueNode instanceof PropertyTagValueNode:
$args = [
Expand All @@ -246,12 +255,14 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
name: new Node\Identifier(substr($tagValueNode->propertyName, 1))
)
];
$attributeComment = $tagValueNode->description;
break;
case $tagValueNode instanceof ReturnTagValueNode:
case $tagValueNode instanceof VarTagValueNode:
$args = [
new Node\Arg(new Scalar\String_((string)($tagValueNode->type)))
];
$attributeComment = $tagValueNode->description;
break;
case $tagValueNode instanceof TemplateTagValueNode:
$args = [
Expand All @@ -260,9 +271,11 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array
if ($tagValueNode->bound instanceof IdentifierTypeNode) {
$args[] = new Node\Arg(new Scalar\String_($tagValueNode->bound->name));
}
$attributeComment = $tagValueNode->description;
break;
case $tagValueNode instanceof GenericTagValueNode:
$args = [];
$attributeComment = (string)$tagValueNode;
break;
default:
continue 2;
Expand All @@ -277,7 +290,11 @@ private function processAnnotations(PhpDocInfo $phpDocInfo): array

$attributeName = new FullyQualified($annotationToAttribute->getAttributeClass());
$attribute = new Attribute($attributeName, $args);
$attributeGroups[] = new AttributeGroup([$attribute]);
$attributeGroup = new AttributeGroup([$attribute]);
if ($attributeComment && defined('Rector\NodeTypeResolver\Node\AttributeKey::ATTRIBUTE_COMMENT')) {
$attributeGroup->setAttribute(AttributeKey::ATTRIBUTE_COMMENT, $attributeComment);
}
$attributeGroups[] = $attributeGroup;
}

foreach ($tagValueNodes as $tagValueNode) {
Expand Down
8 changes: 8 additions & 0 deletions tests/Fixture/IsReadOnlyAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ class IsReadOnlyAttributeTest
#[Type('string')]
public $otherName;

/**
* @readonly please do not write here
*/
public $nameWithComment;

/**
* @psalm-readonly
*/
Expand Down Expand Up @@ -57,6 +62,9 @@ class IsReadOnlyAttributeTest
#[\PhpStaticAnalysis\Attributes\IsReadOnly]
public $otherName;

#[\PhpStaticAnalysis\Attributes\IsReadOnly] // please do not write here
public $nameWithComment;

#[\PhpStaticAnalysis\Attributes\IsReadOnly]
public $psalmName;

Expand Down
2 changes: 2 additions & 0 deletions tests/Fixture/MethodAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use PhpStaticAnalysis\Attributes\Template;
/**
* @deprecated
* @method string getString()
* @method string getName() used to get the name
* @psalm-method void setString(string $text)
* @phpstan-method static string staticGetter()
*/
Expand All @@ -28,6 +29,7 @@ use PhpStaticAnalysis\Attributes\Template;
*/
#[Template('T')]
#[\PhpStaticAnalysis\Attributes\Method('string getString()')]
#[\PhpStaticAnalysis\Attributes\Method('string getName()')] // used to get the name
#[\PhpStaticAnalysis\Attributes\Method('void setString(string $text)')]
#[\PhpStaticAnalysis\Attributes\Method('static string staticGetter()')]
class MethodAttributeTest
Expand Down
14 changes: 14 additions & 0 deletions tests/Fixture/ParamAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ class ParamAttributeTest
return $name1 . $name2;
}

/**
* @param string $name the name of the user
*/
public function getUserName($name)
{
return $name;
}

/**
* @psalm-param string $name
*/
Expand Down Expand Up @@ -123,6 +131,12 @@ class ParamAttributeTest
return $name1 . $name2;
}

#[\PhpStaticAnalysis\Attributes\Param(name: 'string')] // the name of the user
public function getUserName($name)
{
return $name;
}

#[\PhpStaticAnalysis\Attributes\Param(name: 'string')]
public function getPsalmName($name)
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixture/PropertyAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use PhpStaticAnalysis\Attributes\Template;
/**
* @deprecated
* @property string $name
* @property string $userName the name of the user
* @psalm-property int $num
* @phpstan-property string[] $index
*/
Expand All @@ -28,6 +29,7 @@ use PhpStaticAnalysis\Attributes\Template;
*/
#[Template('T')]
#[\PhpStaticAnalysis\Attributes\Property(name: 'string')]
#[\PhpStaticAnalysis\Attributes\Property(userName: 'string')] // the name of the user
#[\PhpStaticAnalysis\Attributes\Property(num: 'int')]
#[\PhpStaticAnalysis\Attributes\Property(index: 'string[]')]
class PropertyAttributeTest
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixture/PropertyReadAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use PhpStaticAnalysis\Attributes\Template;
/**
* @deprecated
* @property-read string $name
* @property-read string $userName the name of the user
* @psalm-property-read int $num
* @phpstan-property-read string[] $index
*/
Expand All @@ -28,6 +29,7 @@ use PhpStaticAnalysis\Attributes\Template;
*/
#[Template('T')]
#[\PhpStaticAnalysis\Attributes\PropertyRead(name: 'string')]
#[\PhpStaticAnalysis\Attributes\PropertyRead(userName: 'string')] // the name of the user
#[\PhpStaticAnalysis\Attributes\PropertyRead(num: 'int')]
#[\PhpStaticAnalysis\Attributes\PropertyRead(index: 'string[]')]
class PropertyReadAttributeTest
Expand Down
2 changes: 2 additions & 0 deletions tests/Fixture/PropertyWriteAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use PhpStaticAnalysis\Attributes\Template;
/**
* @deprecated
* @property-write string $name
* @property-write string $userName the name of the user
* @psalm-property-write int $num
* @phpstan-property-write string[] $index
*/
Expand All @@ -28,6 +29,7 @@ use PhpStaticAnalysis\Attributes\Template;
*/
#[Template('T')]
#[\PhpStaticAnalysis\Attributes\PropertyWrite(name: 'string')]
#[\PhpStaticAnalysis\Attributes\PropertyWrite(userName: 'string')] // the name of the user
#[\PhpStaticAnalysis\Attributes\PropertyWrite(num: 'int')]
#[\PhpStaticAnalysis\Attributes\PropertyWrite(index: 'string[]')]
class PropertyWriteAttributeTest
Expand Down
14 changes: 14 additions & 0 deletions tests/Fixture/ReturnsAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ class ReturnsAttributeTest
return "Hello " . $name;
}

/**
* @return string the name of the user
*/
public function getUserName()
{
return 'John';
}

/**
* @psalm-return string
*/
Expand Down Expand Up @@ -89,6 +97,12 @@ class ReturnsAttributeTest
return "Hello " . $name;
}

#[\PhpStaticAnalysis\Attributes\Returns('string')] // the name of the user
public function getUserName()
{
return 'John';
}

#[\PhpStaticAnalysis\Attributes\Returns('string')]
public function getPsalmName()
{
Expand Down
28 changes: 28 additions & 0 deletions tests/Fixture/TemplateAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ class TemplateAttributeTest
return "Hello " . $name;
}

/**
* @template T the type of object
*/
public function getUserName()
{
return 'John';
}

/**
* @template T of object the type of object
*/
public function getAnotherUserName()
{
return 'John';
}

/**
* @psalm-template T
*/
Expand Down Expand Up @@ -107,6 +123,18 @@ class TemplateAttributeTest
return "Hello " . $name;
}

#[\PhpStaticAnalysis\Attributes\Template('T')] // the type of object
public function getUserName()
{
return 'John';
}

#[\PhpStaticAnalysis\Attributes\Template('T', 'object')] // the type of object
public function getAnotherUserName()
{
return 'John';
}

#[\PhpStaticAnalysis\Attributes\Template('T')]
public function getPsalmName()
{
Expand Down
8 changes: 6 additions & 2 deletions tests/Fixture/TemplateContravariantAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use PhpStaticAnalysis\Attributes\Property;
* @deprecated
* @template-contravariant T1
* @template-contravariant T2 of Exception
* @phpstan-template-contravariant T3
* @template-contravariant T3 this is contravariant
* @template-contravariant T4 of Exception this is also contravariant
* @phpstan-template-contravariant T5
*/
#[Property(name:'string')]
class TemplateContravariantAttributeTest
Expand All @@ -31,7 +33,9 @@ use PhpStaticAnalysis\Attributes\Property;
#[Property(name:'string')]
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T1')]
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T2', 'Exception')]
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T3')]
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T3')] // this is contravariant
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T4', 'Exception')] // this is also contravariant
#[\PhpStaticAnalysis\Attributes\TemplateContravariant('T5')]
class TemplateContravariantAttributeTest
{
}
Expand Down
12 changes: 8 additions & 4 deletions tests/Fixture/TemplateCovariantAttributeTest.php.inc
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use PhpStaticAnalysis\Attributes\Property;
* @deprecated
* @template-covariant T1
* @template-covariant T2 of Exception
* @psalm-template-covariant T3
* @phpstan-template-covariant T4
* @template-covariant T3 this is covariant
* @template-covariant T4 of Exception this is also covariant
* @psalm-template-covariant T5
* @phpstan-template-covariant T6
*/
#[Property(name:'string')]
class TemplateCovariantAttributeTest
Expand All @@ -32,8 +34,10 @@ use PhpStaticAnalysis\Attributes\Property;
#[Property(name:'string')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T1')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T2', 'Exception')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T3')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T4')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T3')] // this is covariant
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T4', 'Exception')] // this is also covariant
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T5')]
#[\PhpStaticAnalysis\Attributes\TemplateCovariant('T6')]
class TemplateCovariantAttributeTest
{
}
Expand Down
Loading

0 comments on commit dbf405d

Please sign in to comment.