Skip to content

Commit

Permalink
Updated Rector to commit f83d744
Browse files Browse the repository at this point in the history
rectorphp/rector-src@f83d744 [PHP 8.1] Add nested attributes support - part #1 (#1266)
  • Loading branch information
TomasVotruba committed Nov 20, 2021
1 parent cd31960 commit 1292707
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function hasClassName(string $className) : bool
if ($annotationName === $className) {
return \true;
}
// the name is not fully qualified in the original name, look for resolvd class attirubte
// the name is not fully qualified in the original name, look for resolved class attribute
$resolvedClass = $this->identifierTypeNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::RESOLVED_CLASS);
return $resolvedClass === $className;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use RectorPrefix20211120\Nette\Utils\Strings;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
Expand All @@ -32,6 +33,11 @@ final class DoctrineAnnotationDecorator
* @var string
*/
private const LONG_ANNOTATION_REGEX = '#@\\\\(?<class_name>.*?)(?<annotation_content>\\(.*?\\))#';
/**
* @see https://regex101.com/r/xWaLOz/1
* @var string
*/
private const NESTED_ANNOTATION_END_REGEX = '#(\\s+)?\\}\\)(\\s+)?#';
/**
* @var \Rector\Core\Configuration\CurrentNodeProvider
*/
Expand Down Expand Up @@ -94,6 +100,17 @@ private function mergeNestedDoctrineAnnotations(\PHPStan\PhpDocParser\Ast\PhpDoc
break;
}
$nextPhpDocChildNode = $phpDocNode->children[$key];
if ($nextPhpDocChildNode instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode && \RectorPrefix20211120\Nette\Utils\Strings::match($nextPhpDocChildNode->text, self::NESTED_ANNOTATION_END_REGEX)) {
// @todo how to detect previously opened brackets?
// probably local property with holding count of opened brackets
$composedContent = $genericTagValueNode->value . \PHP_EOL . $nextPhpDocChildNode->text;
$genericTagValueNode->value = $composedContent;
$startAndEnd = $this->combineStartAndEnd($phpDocChildNode, $nextPhpDocChildNode);
$phpDocChildNode->setAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END, $startAndEnd);
$removedKeys[] = $key;
$removedKeys[] = $key + 1;
continue;
}
if (!$nextPhpDocChildNode instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode) {
continue;
}
Expand All @@ -103,13 +120,10 @@ private function mergeNestedDoctrineAnnotations(\PHPStan\PhpDocParser\Ast\PhpDoc
if ($this->isClosedContent($genericTagValueNode->value)) {
break;
}
$composedContent = $genericTagValueNode->value . \PHP_EOL . $nextPhpDocChildNode->name . $nextPhpDocChildNode->value;
$composedContent = $genericTagValueNode->value . \PHP_EOL . $nextPhpDocChildNode->name . $nextPhpDocChildNode->value->value;
// cleanup the next from closing
$genericTagValueNode->value = $composedContent;
/** @var StartAndEnd $currentStartAndEnd */
$currentStartAndEnd = $phpDocChildNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
/** @var StartAndEnd $nextStartAndEnd */
$nextStartAndEnd = $nextPhpDocChildNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
$startAndEnd = new \Rector\BetterPhpDocParser\ValueObject\StartAndEnd($currentStartAndEnd->getStart(), $nextStartAndEnd->getEnd());
$startAndEnd = $this->combineStartAndEnd($phpDocChildNode, $nextPhpDocChildNode);
$phpDocChildNode->setAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END, $startAndEnd);
$currentChildValueNode = $phpDocNode->children[$key];
if (!$currentChildValueNode instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode) {
Expand All @@ -134,15 +148,10 @@ private function transformGenericTagValueNodesToDoctrineAnnotationTagValueNodes(
foreach ($phpDocNode->children as $key => $phpDocChildNode) {
// the @\FQN use case
if ($phpDocChildNode instanceof \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode) {
$match = \RectorPrefix20211120\Nette\Utils\Strings::match($phpDocChildNode->text, self::LONG_ANNOTATION_REGEX);
$fullyQualifiedAnnotationClass = $match['class_name'] ?? null;
if ($fullyQualifiedAnnotationClass === null) {
$spacelessPhpDocTagNode = $this->resolveFqnAnnotationSpacelessPhpDocTagNode($phpDocChildNode);
if (!$spacelessPhpDocTagNode instanceof \Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode) {
continue;
}
$annotationContent = $match['annotation_content'] ?? null;
$tagName = '@\\' . $fullyQualifiedAnnotationClass;
$formerStartEnd = $phpDocChildNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
$spacelessPhpDocTagNode = $this->createDoctrineSpacelessPhpDocTagNode($annotationContent, $tagName, $fullyQualifiedAnnotationClass, $formerStartEnd);
$phpDocNode->children[$key] = $spacelessPhpDocTagNode;
continue;
}
Expand Down Expand Up @@ -205,4 +214,24 @@ private function createDoctrineSpacelessPhpDocTagNode(string $annotationContent,
$doctrineAnnotationTagValueNode->setAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END, $startAndEnd);
return new \Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode($tagName, $doctrineAnnotationTagValueNode);
}
private function combineStartAndEnd(\PHPStan\PhpDocParser\Ast\Node $startPhpDocChildNode, \PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode $endPhpDocChildNode) : \Rector\BetterPhpDocParser\ValueObject\StartAndEnd
{
/** @var StartAndEnd $currentStartAndEnd */
$currentStartAndEnd = $startPhpDocChildNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
/** @var StartAndEnd $nextStartAndEnd */
$nextStartAndEnd = $endPhpDocChildNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
return new \Rector\BetterPhpDocParser\ValueObject\StartAndEnd($currentStartAndEnd->getStart(), $nextStartAndEnd->getEnd());
}
private function resolveFqnAnnotationSpacelessPhpDocTagNode(\PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode $phpDocTextNode) : ?\Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode
{
$match = \RectorPrefix20211120\Nette\Utils\Strings::match($phpDocTextNode->text, self::LONG_ANNOTATION_REGEX);
$fullyQualifiedAnnotationClass = $match['class_name'] ?? null;
if ($fullyQualifiedAnnotationClass === null) {
return null;
}
$annotationContent = $match['annotation_content'] ?? null;
$tagName = '@\\' . $fullyQualifiedAnnotationClass;
$formerStartEnd = $phpDocTextNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::START_AND_END);
return $this->createDoctrineSpacelessPhpDocTagNode($annotationContent, $tagName, $fullyQualifiedAnnotationClass, $formerStartEnd);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare (strict_types=1);
namespace Rector\PhpAttribute\Exception;

use Exception;
final class InvalidNestedAttributeException extends \Exception
{
}
17 changes: 12 additions & 5 deletions packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,12 @@ public function createArgsFromItems(array $items, ?string $silentKey = null) : a
{
$args = [];
if ($silentKey !== null && isset($items[$silentKey])) {
$silentValue = \PhpParser\BuilderHelpers::normalizeValue($items[$silentKey]);
$this->normalizeStringDoubleQuote($silentValue);
$silentValue = $this->mapAnnotationValueToAttribute($items[$silentKey]);
$args[] = new \PhpParser\Node\Arg($silentValue);
unset($items[$silentKey]);
}
foreach ($items as $key => $value) {
$value = $this->valueNormalizer->normalize($value);
$value = \PhpParser\BuilderHelpers::normalizeValue($value);
$this->normalizeStringDoubleQuote($value);
$value = $this->mapAnnotationValueToAttribute($value);
$name = null;
if (\is_string($key)) {
$name = new \PhpParser\Node\Identifier($key);
Expand Down Expand Up @@ -136,4 +133,14 @@ private function completeNamedArguments(array $args, array $argumentNames) : voi
$arg->name = new \PhpParser\Node\Identifier($argumentName);
}
}
/**
* @param mixed $annotationValue
*/
private function mapAnnotationValueToAttribute($annotationValue) : \PhpParser\Node\Expr
{
$value = $this->valueNormalizer->normalize($annotationValue);
$value = \PhpParser\BuilderHelpers::normalizeValue($value);
$this->normalizeStringDoubleQuote($value);
return $value;
}
}
59 changes: 49 additions & 10 deletions packages/PhpAttribute/Value/ValueNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,44 @@

use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\Type\Constant\ConstantBooleanType;
use PHPStan\Type\Constant\ConstantFloatType;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\PhpAttribute\Exception\InvalidNestedAttributeException;
final class ValueNormalizer
{
/**
* @var \Rector\Core\Php\PhpVersionProvider
*/
private $phpVersionProvider;
public function __construct(\Rector\Core\Php\PhpVersionProvider $phpVersionProvider)
{
$this->phpVersionProvider = $phpVersionProvider;
}
/**
* @param mixed $value
* @return mixed[]|bool|float|int|\PhpParser\Node\Expr|string
*/
public function normalize($value)
{
if ($value instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode) {
return (int) $value->value;
}
if ($value instanceof \PHPStan\Type\Constant\ConstantFloatType || $value instanceof \PHPStan\Type\Constant\ConstantBooleanType) {
return $value->getValue();
if ($value instanceof \Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode) {
return $this->normalizeDoctrineAnnotationTagValueNode($value);
}
if ($value instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode) {
return \true;
}
if ($value instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode) {
return \false;
if ($value instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode) {
return $this->normalizeConstrExprNode($value);
}
if ($value instanceof \Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\CurlyListNode) {
return \array_map(function ($node) {
Expand All @@ -53,4 +64,32 @@ public function normalize($value)
}
return $value;
}
private function normalizeDoctrineAnnotationTagValueNode(\Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : \PhpParser\Node\Expr\New_
{
// if PHP 8.0- throw exception
if (!$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::NEW_INITIALIZERS)) {
throw new \Rector\PhpAttribute\Exception\InvalidNestedAttributeException();
}
$resolveClass = $doctrineAnnotationTagValueNode->identifierTypeNode->getAttribute(\Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey::RESOLVED_CLASS);
return new \PhpParser\Node\Expr\New_(new \PhpParser\Node\Name\FullyQualified($resolveClass));
}
/**
* @return bool|float|int
*/
private function normalizeConstrExprNode(\PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNode $constExprNode)
{
if ($constExprNode instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode) {
return (int) $constExprNode->value;
}
if ($constExprNode instanceof \PHPStan\Type\Constant\ConstantFloatType || $constExprNode instanceof \PHPStan\Type\Constant\ConstantBooleanType) {
return $constExprNode->getValue();
}
if ($constExprNode instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode) {
return \true;
}
if ($constExprNode instanceof \PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode) {
return \false;
}
throw new \Rector\Core\Exception\ShouldNotHappenException();
}
}
32 changes: 22 additions & 10 deletions rules/Php80/Rector/Class_/AnnotationToAttributeRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,20 +185,32 @@ private function processDoctrineAnnotationClasses(\Rector\BetterPhpDocParser\Php
if (!$phpDocChildNode->value instanceof \Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode) {
continue;
}
$nestedDoctrineAnnotationTagValueNodes = $this->phpDocNodeFinder->findByType($phpDocChildNode->value, \Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode::class);
// depends on PHP 8.1+ - nested values, skip for now
if ($nestedDoctrineAnnotationTagValueNodes !== []) {
$doctrineTagValueNode = $phpDocChildNode->value;
$annotationToAttribute = $this->matchAnnotationToAttribute($doctrineTagValueNode);
if (!$annotationToAttribute instanceof \Rector\Php80\ValueObject\AnnotationToAttribute) {
continue;
}
$doctrineTagValueNode = $phpDocChildNode->value;
foreach ($this->annotationsToAttributes as $annotationToAttribute) {
if (!$doctrineTagValueNode->hasClassName($annotationToAttribute->getTag())) {
continue;
}
$doctrineTagAndAnnotationToAttributes[] = new \Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute($phpDocChildNode->value, $annotationToAttribute);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode);
$nestedDoctrineAnnotationTagValueNodes = $this->phpDocNodeFinder->findByType($doctrineTagValueNode, \Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode::class);
// depends on PHP 8.1+ - nested values, skip for now
if ($nestedDoctrineAnnotationTagValueNodes !== [] && !$this->phpVersionProvider->isAtLeastPhpVersion(\Rector\Core\ValueObject\PhpVersionFeature::NEW_INITIALIZERS)) {
continue;
}
$doctrineTagAndAnnotationToAttributes[] = new \Rector\Php80\ValueObject\DoctrineTagAndAnnotationToAttribute($doctrineTagValueNode, $annotationToAttribute);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineTagValueNode);
}
return $this->attrGroupsFactory->create($doctrineTagAndAnnotationToAttributes);
}
/**
* @return \Rector\Php80\ValueObject\AnnotationToAttribute|null
*/
private function matchAnnotationToAttribute(\Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode)
{
foreach ($this->annotationsToAttributes as $annotationToAttribute) {
if (!$doctrineAnnotationTagValueNode->hasClassName($annotationToAttribute->getTag())) {
continue;
}
return $annotationToAttribute;
}
return null;
}
}
4 changes: 2 additions & 2 deletions src/Application/VersionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ final class VersionResolver
/**
* @var string
*/
public const PACKAGE_VERSION = '60c06a7fbf77a7ad89e1c7a52114abd6ccc47883';
public const PACKAGE_VERSION = 'f83d7441580e6175556328ac94dc7f128d2344aa';
/**
* @var string
*/
public const RELEASE_DATE = '2021-11-20 13:28:26';
public const RELEASE_DATE = '2021-11-20 11:28:47';
public static function resolvePackageVersion() : string
{
$process = new \RectorPrefix20211120\Symfony\Component\Process\Process(['git', 'log', '--pretty="%H"', '-n1', 'HEAD'], __DIR__);
Expand Down
2 changes: 1 addition & 1 deletion vendor/autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitfb0bded2fb82879630a4e95c496b376e::getLoader();
return ComposerAutoloaderInit0063f93cd2df74c27fe1b0639d1d2de1::getLoader();
1 change: 1 addition & 0 deletions vendor/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -2511,6 +2511,7 @@
'Rector\\Php81\\Rector\\FunctionLike\\IntersectionTypesRector' => $baseDir . '/rules/Php81/Rector/FunctionLike/IntersectionTypesRector.php',
'Rector\\Php81\\Rector\\MethodCall\\MyCLabsMethodCallToEnumConstRector' => $baseDir . '/rules/Php81/Rector/MethodCall/MyCLabsMethodCallToEnumConstRector.php',
'Rector\\Php81\\Rector\\Property\\ReadOnlyPropertyRector' => $baseDir . '/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php',
'Rector\\PhpAttribute\\Exception\\InvalidNestedAttributeException' => $baseDir . '/packages/PhpAttribute/Exception/InvalidNestedAttributeException.php',
'Rector\\PhpAttribute\\NodeAnalyzer\\NamedArgumentsResolver' => $baseDir . '/packages/PhpAttribute/NodeAnalyzer/NamedArgumentsResolver.php',
'Rector\\PhpAttribute\\Printer\\DoctrineAnnotationFactory' => $baseDir . '/packages/PhpAttribute/Printer/DoctrineAnnotationFactory.php',
'Rector\\PhpAttribute\\Printer\\PhpAttributeGroupFactory' => $baseDir . '/packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php',
Expand Down

0 comments on commit 1292707

Please sign in to comment.