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.
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
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);
}
}
@@ -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
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
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
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
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
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
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.