[PHP 8.0] Make Attribute silent keys explicit, with named args (#4834)

* decouple TagName class

* remove attribute aware from data provider tag value node, as unique class

* add RequiredTagValueNode

* make Attributes explicit, to avoid confusiong
This commit is contained in:
Tomas Votruba 2020-12-09 18:16:36 +01:00 committed by GitHub
parent 5e609f50fb
commit eac130e705
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 85 additions and 44 deletions

View File

@ -8,7 +8,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
final class AttributeAwareDataProviderTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface
final class DataProviderTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface
{
use AttributeTrait;

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Rector\AttributeAwarePhpDoc\Ast\PhpDoc;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
final class RequiredTagValueNode implements PhpDocTagValueNode, AttributeAwareNodeInterface, PhpAttributableTagNodeInterface
{
use AttributeTrait;
public function __toString(): string
{
return '';
}
public function getShortName(): string
{
return 'Required';
}
public function getAttributeClassName(): string
{
return 'Symfony\Contracts\Service\Attribute\Required';
}
/**
* @return mixed[]
*/
public function getAttributableItems(): array
{
return [];
}
}

View File

@ -9,7 +9,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Parser\ParserException;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareDataProviderTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
final class PHPUnitDataProviderDocNodeFactory
@ -57,7 +57,7 @@ final class PHPUnitDataProviderDocNodeFactory
/**
* Override of parent private method to allow reference: https://github.com/rectorphp/rector/pull/1735
*/
private function parseDataProviderTagValue(TokenIterator $tokenIterator): AttributeAwareDataProviderTagValueNode
private function parseDataProviderTagValue(TokenIterator $tokenIterator): DataProviderTagValueNode
{
$method = $this->privatesCaller->callPrivateMethod(
$this->phpDocParser,
@ -65,6 +65,6 @@ final class PHPUnitDataProviderDocNodeFactory
$tokenIterator
);
return new AttributeAwareDataProviderTagValueNode($method);
return new DataProviderTagValueNode($method);
}
}

View File

@ -15,6 +15,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\RequiredTagValueNode;
use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\GenericPhpDocNodeFactoryInterface;
@ -26,6 +27,7 @@ use Rector\BetterPhpDocParser\Printer\MultilineSpaceFormatPreserver;
use Rector\BetterPhpDocParser\ValueObject\StartAndEnd;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
@ -186,13 +188,16 @@ final class BetterPhpDocParser extends PhpDocParser
throw new ShouldNotHappenException();
}
if (strtolower($tag) === '@param') {
$lowercasedTag = strtolower($tag);
if ($lowercasedTag === '@param') {
// to prevent circular reference of this service
$this->paramPhpDocNodeFactory->setPhpDocParser($this);
$tagValueNode = $this->paramPhpDocNodeFactory->createFromTokens($tokenIterator);
} elseif (strtolower($tag) === '@dataprovider') {
} elseif ($lowercasedTag === '@dataprovider') {
$this->phpUnitDataProviderDocNodeFactory->setPhpDocParser($this);
$tagValueNode = $this->phpUnitDataProviderDocNodeFactory->createFromTokens($tokenIterator);
} elseif ($lowercasedTag === '@' . TagName::REQUIRED) {
$tagValueNode = new RequiredTagValueNode();
} else {
// class-annotation
$phpDocNodeFactory = $this->matchTagToPhpDocNodeFactory($tag);

View File

@ -13,5 +13,5 @@ return static function (ContainerConfigurator $containerConfigurator): void {
->autoconfigure();
$services->load('Rector\PhpAttribute\\', __DIR__ . '/../src')
->exclude([__DIR__ . '/../src/Contract']);
->exclude([__DIR__ . '/../src/Contract', __DIR__ . '/../src/ValueObject']);
};

View File

@ -6,10 +6,8 @@ namespace Rector\PhpAttribute;
use PhpParser\Node;
use PhpParser\Node\Attribute;
use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
@ -44,15 +42,7 @@ final class AnnotationToAttributeConverter
return null;
}
// special cases without tag value node
$hasNewAttrGroups = false;
if ($phpDocInfo->hasByName('required')) {
$phpDocInfo->removeByName('required');
$node->attrGroups[] = new AttributeGroup([
new Attribute(new FullyQualified('Symfony\Contracts\Service\Attribute\Required')),
]);
$hasNewAttrGroups = true;
}
// 0. has 0 nodes, nothing to change
/** @var PhpAttributableTagNodeInterface[] $phpAttributableTagNodes */

View File

@ -11,7 +11,6 @@ use PhpParser\Node\AttributeGroup;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\SilentKeyNodeInterface;
use Rector\PhpAttribute\Contract\ManyPhpAttributableTagNodeInterface;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
@ -43,13 +42,7 @@ final class PhpAttributteGroupFactory
public function printItemsToAttributeArgs(PhpAttributableTagNodeInterface $phpAttributableTagNode): array
{
$items = $phpAttributableTagNode->getAttributableItems();
$silentKey = null;
if ($phpAttributableTagNode instanceof SilentKeyNodeInterface) {
$silentKey = $phpAttributableTagNode->getSilentKey();
}
return $this->createArgsFromItems($items, $silentKey);
return $this->createArgsFromItems($items);
}
/**

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Rector\PhpAttribute\ValueObject;
final class TagName
{
/**
* Use by Symfony to autowire dependencies outside constructor,
* @see https://symfony.com/doc/current/service_container/autowiring.html#autowiring-other-methods-e-g-setters-and-public-typed-properties
* @var string
*/
public const REQUIRED = 'required';
}

View File

@ -637,7 +637,7 @@ parameters:
-
message: '#Do not use scalar or array as constructor parameter\. Use "Symplify\\PackageBuilder\\Parameter\\ParameterProvider" service instead#'
paths:
- packages/attribute-aware-php-doc/src/Ast/PhpDoc/AttributeAwareDataProviderTagValueNode.php # 22
- packages/attribute-aware-php-doc/src/Ast/PhpDoc/DataProviderTagValueNode.php # 22
- packages/attribute-aware-php-doc/src/Ast/PhpDoc/AttributeAwareParamTagValueNode.php # 33
- packages/attribute-aware-php-doc/src/Ast/PhpDoc/AttributeAwareParamTagValueNode.php # 33
- packages/attribute-aware-php-doc/src/Ast/PhpDoc/AttributeAwareParamTagValueNode.php # 33

View File

@ -16,6 +16,7 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\Comparator\CurrentAndParentClassMethodComparator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -164,6 +165,6 @@ CODE_SAMPLE
return false;
}
return $phpDocInfo->hasByName('required');
return $phpDocInfo->hasByName(TagName::REQUIRED);
}
}

View File

@ -24,7 +24,7 @@ use Symfony\Component\Routing\Annotation\Route;
class SymfonyRoute
{
#[\Symfony\Component\Routing\Annotation\Route('/path', name: 'action')]
#[\Symfony\Component\Routing\Annotation\Route(path: '/path', name: 'action')]
public function action()
{
}

View File

@ -24,7 +24,7 @@ use Symfony\Component\Routing\Annotation\Route;
class SymfonyRoute
{
#[Route('/path', name: 'action')]
#[Route(path: '/path', name: 'action')]
public function action()
{
}

View File

@ -8,7 +8,7 @@ use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Class_;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareDataProviderTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractPHPUnitRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -107,8 +107,8 @@ CODE_SAMPLE
continue;
}
/** @var AttributeAwareDataProviderTagValueNode[] $dataProviderTagValueNodes */
$dataProviderTagValueNodes = $phpDocInfo->findAllByType(AttributeAwareDataProviderTagValueNode::class);
/** @var DataProviderTagValueNode[] $dataProviderTagValueNodes */
$dataProviderTagValueNodes = $phpDocInfo->findAllByType(DataProviderTagValueNode::class);
if ($dataProviderTagValueNodes === []) {
continue;
}

View File

@ -12,6 +12,7 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Caching\Contract\Rector\ZeroCacheRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Rector\Privatization\NodeAnalyzer\ClassMethodExternalCallNodeAnalyzer;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodVisibilityVendorLockResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -161,7 +162,7 @@ CODE_SAMPLE
return false;
}
return $phpDocInfo->hasByNames(['api', 'required']);
return $phpDocInfo->hasByNames(['api', TagName::REQUIRED]);
}
private function isControllerAction(Class_ $class, ClassMethod $classMethod): bool

View File

@ -11,6 +11,7 @@ use PhpParser\Node\Stmt\Property;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Rector\VendorLocker\NodeVendorLocker\PropertyVisibilityVendorLockResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -26,7 +27,7 @@ final class PrivatizeLocalPropertyToPrivatePropertyRector extends AbstractRector
private const ANNOTATIONS_REQUIRING_PUBLIC = [
'api',
// Symfony DI
'required',
TagName::REQUIRED,
// other DI
'inject',
];

View File

@ -14,6 +14,7 @@ use Rector\Core\PhpParser\Builder\ParamBuilder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PhpAttribute\ValueObject\TagName;
use Rector\SOLID\Rector\Class_\MultiParentingToAbstractDependencyRector;
final class InjectMethodFactory
@ -86,7 +87,7 @@ final class InjectMethodFactory
if ($framework === MultiParentingToAbstractDependencyRector::FRAMEWORK_SYMFONY) {
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($classMethod);
$phpDocInfo->addBareTag('required');
$phpDocInfo->addBareTag(TagName::REQUIRED);
}
return $classMethod;

View File

@ -9,6 +9,7 @@ use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpAttribute\ValueObject\TagName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -17,11 +18,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class AutoWireWithClassNameSuffixForMethodWithRequiredAnnotationRector extends AbstractRector
{
/**
* @var string
*/
private const REQUIRED_TAG = 'required';
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
@ -65,7 +61,7 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->hasTagByName($node, self::REQUIRED_TAG)) {
if (! $this->hasTagByName($node, TagName::REQUIRED)) {
return null;
}

View File

@ -13,7 +13,7 @@ use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareDataProviderTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\DataProviderTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -78,8 +78,8 @@ final class PHPUnitDataProviderParamTypeInferer implements ParamTypeInfererInter
return null;
}
/** @var AttributeAwareDataProviderTagValueNode|null $attributeAwareDataProviderTagValueNode */
$attributeAwareDataProviderTagValueNode = $phpDocInfo->getByType(AttributeAwareDataProviderTagValueNode::class);
/** @var DataProviderTagValueNode|null $attributeAwareDataProviderTagValueNode */
$attributeAwareDataProviderTagValueNode = $phpDocInfo->getByType(DataProviderTagValueNode::class);
if ($attributeAwareDataProviderTagValueNode === null) {
return null;
}