mirror of
https://github.com/rectorphp/rector.git
synced 2025-03-19 14:59:46 +01:00
Merge pull request #3790 from rectorphp/cover-array-dim-fetch
[Nette] Add ChangeControlArrayAccessToAnnotatedControlVariableRector
This commit is contained in:
commit
b99bf8e0a2
@ -58,6 +58,7 @@
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Rector\\Architecture\\": "rules/architecture/src",
|
||||
"Rector\\AnonymousClass\\": "packages/anonymous-class/src",
|
||||
"Rector\\PostRector\\": "packages/post-rector/src",
|
||||
"Rector\\AttributeAwarePhpDoc\\": "packages/attribute-aware-php-doc/src",
|
||||
"Rector\\Autodiscovery\\": "rules/autodiscovery/src",
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Nette\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector;
|
||||
use Rector\Nette\Rector\Assign\MakeGetComponentAssignAnnotatedRector;
|
||||
use Rector\Nette\Rector\ClassMethod\TemplateMagicAssignToExplicitVariableArrayRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
@ -12,4 +13,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services->set(TemplateMagicAssignToExplicitVariableArrayRector::class);
|
||||
|
||||
$services->set(MakeGetComponentAssignAnnotatedRector::class);
|
||||
|
||||
$services->set(ChangeControlArrayAccessToAnnotatedControlVariableRector::class);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
# All 534 Rectors Overview
|
||||
# All 536 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
@ -31,7 +31,7 @@
|
||||
- [MockistaToMockery](#mockistatomockery) (2)
|
||||
- [MysqlToMysqli](#mysqltomysqli) (4)
|
||||
- [Naming](#naming) (3)
|
||||
- [Nette](#nette) (14)
|
||||
- [Nette](#nette) (15)
|
||||
- [NetteCodeQuality](#nettecodequality) (1)
|
||||
- [NetteKdyby](#nettekdyby) (4)
|
||||
- [NetteTesterToPHPUnit](#nettetestertophpunit) (3)
|
||||
@ -5184,6 +5184,37 @@ Nextras/Form upgrade of addDatePicker method call to DateControl assign
|
||||
|
||||
<br><br>
|
||||
|
||||
### `ChangeControlArrayAccessToAnnotatedControlVariableRector`
|
||||
|
||||
- class: [`Rector\Nette\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector`](/../master/rules/nette/src/Rector/ArrayDimFetch/ChangeControlArrayAccessToAnnotatedControlVariableRector.php)
|
||||
- [test fixtures](/../master/rules/nette/tests/Rector/ArrayDimFetch/ChangeControlArrayAccessToAnnotatedControlVariableRector/Fixture)
|
||||
|
||||
Change magic `$this["some_component"]` to variable assign with @var annotation
|
||||
|
||||
```diff
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomePresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
- if ($this['some_form']->isSubmitted()) {
|
||||
+ /** @var \Nette\Application\UI\Form $someForm */
|
||||
+ $someForm = $this['some_form'];
|
||||
+ if ($someForm->isSubmitted()) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
### `ChangeFormArrayAccessToAnnotatedControlVariableRector`
|
||||
|
||||
- class: [`Rector\Nette\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector`](/../master/rules/nette/src/Rector/ArrayDimFetch/ChangeFormArrayAccessToAnnotatedControlVariableRector.php)
|
||||
|
15
packages/anonymous-class/config/config.php
Normal file
15
packages/anonymous-class/config/config.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->defaults()
|
||||
->autowire()
|
||||
->public();
|
||||
|
||||
$services->load('Rector\AnonymousClass\\', __DIR__ . '/../src');
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\AnonymousClass\NodeAnalyzer;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class ClassNodeAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
public function isAnonymousClass(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Class_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = $this->nodeNameResolver->getName($node);
|
||||
if ($className === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// match PHPStan pattern for anonymous classes
|
||||
return (bool) Strings::match($className, '#AnonymousClass\w+$#');
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\FloatType;
|
||||
@ -25,12 +24,13 @@ use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\AnonymousClass\NodeAnalyzer\ClassNodeAnalyzer;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\ParentClassesInterfacesAndUsedTraitsCorrector;
|
||||
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
|
||||
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
|
||||
use Rector\TypeDeclaration\PHPStan\Type\ObjectTypeSpecifier;
|
||||
|
||||
final class NodeTypeResolver
|
||||
@ -40,11 +40,6 @@ final class NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolvers = [];
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var ObjectTypeSpecifier
|
||||
*/
|
||||
@ -60,23 +55,34 @@ final class NodeTypeResolver
|
||||
*/
|
||||
private $parentClassesInterfacesAndUsedTraitsCorrector;
|
||||
|
||||
/**
|
||||
* @var TypeUnwrapper
|
||||
*/
|
||||
private $typeUnwrapper;
|
||||
|
||||
/**
|
||||
* @var ClassNodeAnalyzer
|
||||
*/
|
||||
private $classNodeAnalyzer;
|
||||
|
||||
/**
|
||||
* @param NodeTypeResolverInterface[] $nodeTypeResolvers
|
||||
*/
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
ObjectTypeSpecifier $objectTypeSpecifier,
|
||||
ParentClassesInterfacesAndUsedTraitsCorrector $parentClassesInterfacesAndUsedTraitsCorrector,
|
||||
TypeUnwrapper $typeUnwrapper,
|
||||
ClassNodeAnalyzer $classNodeAnalyzer,
|
||||
array $nodeTypeResolvers
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
|
||||
foreach ($nodeTypeResolvers as $nodeTypeResolver) {
|
||||
$this->addNodeTypeResolver($nodeTypeResolver);
|
||||
}
|
||||
|
||||
$this->objectTypeSpecifier = $objectTypeSpecifier;
|
||||
$this->parentClassesInterfacesAndUsedTraitsCorrector = $parentClassesInterfacesAndUsedTraitsCorrector;
|
||||
$this->typeUnwrapper = $typeUnwrapper;
|
||||
$this->classNodeAnalyzer = $classNodeAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,7 +172,7 @@ final class NodeTypeResolver
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
if ($node instanceof New_ && $this->isAnonymousClass($node->class)) {
|
||||
if ($node instanceof New_ && $this->classNodeAnalyzer->isAnonymousClass($node->class)) {
|
||||
return new ObjectWithoutClassType();
|
||||
}
|
||||
|
||||
@ -178,6 +184,48 @@ final class NodeTypeResolver
|
||||
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAlaisedObjectType($node, $staticType);
|
||||
}
|
||||
|
||||
public function isNumberType(Node $node): bool
|
||||
{
|
||||
return $this->isStaticType($node, IntegerType::class) || $this->isStaticType($node, FloatType::class);
|
||||
}
|
||||
|
||||
public function isStaticType(Node $node, string $staticTypeClass): bool
|
||||
{
|
||||
if (! is_a($staticTypeClass, Type::class, true)) {
|
||||
throw new ShouldNotHappenException(sprintf(
|
||||
'"%s" in "%s()" must be type of "%s"',
|
||||
$staticTypeClass,
|
||||
__METHOD__,
|
||||
Type::class
|
||||
));
|
||||
}
|
||||
|
||||
return is_a($this->resolve($node), $staticTypeClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectType|string $desiredType
|
||||
*/
|
||||
public function isObjectTypeOrNullableObjectType(Node $node, $desiredType): bool
|
||||
{
|
||||
if ($this->isObjectType($node, $desiredType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$nodeType = $this->getStaticType($node);
|
||||
if (! $nodeType instanceof UnionType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$unwrappedNodeType = $this->typeUnwrapper->unwrapNullableType($nodeType);
|
||||
if (! $unwrappedNodeType instanceof TypeWithClassName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$desiredTypeString = $desiredType instanceof ObjectType ? $desiredType->getClassName() : $desiredType;
|
||||
return is_a($unwrappedNodeType->getClassName(), $desiredTypeString, true);
|
||||
}
|
||||
|
||||
public function isNullableObjectType(Node $node): bool
|
||||
{
|
||||
$nodeType = $this->resolve($node);
|
||||
@ -203,25 +251,6 @@ final class NodeTypeResolver
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isNumberType(Node $node): bool
|
||||
{
|
||||
return $this->isStaticType($node, IntegerType::class) || $this->isStaticType($node, FloatType::class);
|
||||
}
|
||||
|
||||
public function isStaticType(Node $node, string $staticTypeClass): bool
|
||||
{
|
||||
if (! is_a($staticTypeClass, Type::class, true)) {
|
||||
throw new ShouldNotHappenException(sprintf(
|
||||
'"%s" in "%s()" must be type of "%s"',
|
||||
$staticTypeClass,
|
||||
__METHOD__,
|
||||
Type::class
|
||||
));
|
||||
}
|
||||
|
||||
return is_a($this->resolve($node), $staticTypeClass);
|
||||
}
|
||||
|
||||
private function addNodeTypeResolver(NodeTypeResolverInterface $nodeTypeResolver): void
|
||||
{
|
||||
foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) {
|
||||
@ -296,7 +325,7 @@ final class NodeTypeResolver
|
||||
}
|
||||
|
||||
// skip anonymous classes, ref https://github.com/rectorphp/rector/issues/1574
|
||||
if ($node instanceof New_ && $this->isAnonymousClass($node->class)) {
|
||||
if ($node instanceof New_ && $this->classNodeAnalyzer->isAnonymousClass($node->class)) {
|
||||
return new ObjectWithoutClassType();
|
||||
}
|
||||
|
||||
@ -331,17 +360,6 @@ final class NodeTypeResolver
|
||||
return new ArrayType(new MixedType(), new MixedType());
|
||||
}
|
||||
|
||||
private function isAnonymousClass(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Class_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = $this->nodeNameResolver->getName($node);
|
||||
|
||||
return $className === null || Strings::contains($className, 'AnonymousClass');
|
||||
}
|
||||
|
||||
private function resolveByNodeTypeResolvers(Node $node): ?Type
|
||||
{
|
||||
foreach ($this->nodeTypeResolvers as $nodeClass => $nodeTypeResolver) {
|
||||
|
@ -8,6 +8,7 @@ use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStan\TypeFactoryStaticHelper;
|
||||
|
||||
final class TypeUnwrapper
|
||||
{
|
||||
@ -51,4 +52,22 @@ final class TypeUnwrapper
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type|UnionType
|
||||
*/
|
||||
public function removeNullTypeFromUnionType(UnionType $unionType): Type
|
||||
{
|
||||
$unionedTypesWithoutNullType = [];
|
||||
|
||||
foreach ($unionType->getTypes() as $type) {
|
||||
if ($type instanceof UnionType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$unionedTypesWithoutNullType[] = $type;
|
||||
}
|
||||
|
||||
return TypeFactoryStaticHelper::createUnionObjectType($unionedTypesWithoutNullType);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PostRector\Contract\Collector\NodeCollectorInterface;
|
||||
|
||||
@ -41,6 +42,11 @@ final class NodesToAddCollector implements NodeCollectorInterface
|
||||
|
||||
public function addNodeBeforeNode(Node $addedNode, Node $positionNode): void
|
||||
{
|
||||
if ($positionNode->getAttributes() === []) {
|
||||
$message = sprintf('Switch arguments in "%s()" method', __METHOD__);
|
||||
throw new ShouldNotHappenException($message);
|
||||
}
|
||||
|
||||
$position = $this->resolveNearestExpressionPosition($positionNode);
|
||||
$this->nodesToAddBefore[$position][] = $this->wrapToExpression($addedNode);
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\StaticTypeAnalyzer;
|
||||
use Rector\PHPStan\TypeFactoryStaticHelper;
|
||||
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
|
||||
|
||||
/**
|
||||
* @see \Rector\CodeQuality\Tests\Rector\If_\SimplifyIfReturnBoolRector\SimplifyIfReturnBoolRectorTest
|
||||
@ -40,12 +40,19 @@ final class SimplifyIfReturnBoolRector extends AbstractRector
|
||||
*/
|
||||
private $mergedNodeCommentPreserver;
|
||||
|
||||
/**
|
||||
* @var TypeUnwrapper
|
||||
*/
|
||||
private $typeUnwrapper;
|
||||
|
||||
public function __construct(
|
||||
MergedNodeCommentPreserver $mergedNodeCommentPreserver,
|
||||
TypeUnwrapper $typeUnwrapper,
|
||||
StaticTypeAnalyzer $staticTypeAnalyzer
|
||||
) {
|
||||
$this->staticTypeAnalyzer = $staticTypeAnalyzer;
|
||||
$this->mergedNodeCommentPreserver = $mergedNodeCommentPreserver;
|
||||
$this->typeUnwrapper = $typeUnwrapper;
|
||||
$this->staticTypeAnalyzer = $staticTypeAnalyzer;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -205,7 +212,7 @@ PHP
|
||||
$exprStaticType = $this->getStaticType($expr);
|
||||
// if we remove null type, still has to be trueable
|
||||
if ($exprStaticType instanceof UnionType) {
|
||||
$unionTypeWithoutNullType = $this->removeNullTypeFromUnionType($exprStaticType);
|
||||
$unionTypeWithoutNullType = $this->typeUnwrapper->removeNullTypeFromUnionType($exprStaticType);
|
||||
if ($this->staticTypeAnalyzer->isAlwaysTruableType($unionTypeWithoutNullType)) {
|
||||
return new NotIdentical($expr, $this->createNull());
|
||||
}
|
||||
@ -221,23 +228,6 @@ PHP
|
||||
return new Bool_($expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type|UnionType
|
||||
*/
|
||||
private function removeNullTypeFromUnionType(UnionType $unionType): Type
|
||||
{
|
||||
$unionedTypesWithoutNullType = [];
|
||||
foreach ($unionType->getTypes() as $type) {
|
||||
if ($type instanceof UnionType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$unionedTypesWithoutNullType[] = $type;
|
||||
}
|
||||
|
||||
return TypeFactoryStaticHelper::createUnionObjectType($unionedTypesWithoutNullType);
|
||||
}
|
||||
|
||||
private function isBoolCastNeeded(Expr $expr): bool
|
||||
{
|
||||
if ($expr instanceof BooleanNot) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Naming;
|
||||
namespace Rector\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\ValueObject\RenamedNamespaceValueObject;
|
72
rules/nette/src/DocBlock/VarAnnotationManipulator.php
Normal file
72
rules/nette/src/DocBlock/VarAnnotationManipulator.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\DocBlock;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareFullyQualifiedIdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
final class VarAnnotationManipulator
|
||||
{
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
public function __construct(PhpDocInfoFactory $phpDocInfoFactory)
|
||||
{
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
}
|
||||
|
||||
public function decorateNodeWithInlineVarType(
|
||||
Node $node,
|
||||
TypeWithClassName $controlTypeWithClassName,
|
||||
string $variableName
|
||||
): void {
|
||||
$phpDocInfo = $this->resolvePhpDocInfo($node);
|
||||
|
||||
// already done
|
||||
if ($phpDocInfo->getVarTagValue() !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributeAwareFullyQualifiedIdentifierTypeNode = new AttributeAwareFullyQualifiedIdentifierTypeNode(
|
||||
$controlTypeWithClassName->getClassName()
|
||||
);
|
||||
|
||||
$attributeAwareVarTagValueNode = new AttributeAwareVarTagValueNode(
|
||||
$attributeAwareFullyQualifiedIdentifierTypeNode,
|
||||
'$' . $variableName,
|
||||
''
|
||||
);
|
||||
|
||||
$phpDocInfo->addTagValueNode($attributeAwareVarTagValueNode);
|
||||
}
|
||||
|
||||
private function resolvePhpDocInfo(Node $node): PhpDocInfo
|
||||
{
|
||||
$currentStmt = $node->getAttribute(AttributeKey::CURRENT_STATEMENT);
|
||||
if ($currentStmt instanceof Expression) {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $currentStmt->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
} else {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
}
|
||||
|
||||
if ($phpDocInfo === null) {
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($node);
|
||||
}
|
||||
|
||||
$phpDocInfo->makeSingleLined();
|
||||
|
||||
return $phpDocInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\FormControlTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\Nette\Contract\FormControlTypeResolverInterface;
|
||||
use Rector\Nette\Naming\NetteControlNaming;
|
||||
use Rector\Nette\NodeAnalyzer\ControlDimFetchAnalyzer;
|
||||
use Rector\NodeCollector\NodeFinder\FunctionLikeParsedNodesFinder;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
|
||||
final class ArrayDimFetchControlTypeResolver implements FormControlTypeResolverInterface
|
||||
{
|
||||
/**
|
||||
* @var FunctionLikeParsedNodesFinder
|
||||
*/
|
||||
private $functionLikeParsedNodesFinder;
|
||||
|
||||
/**
|
||||
* @var ControlDimFetchAnalyzer
|
||||
*/
|
||||
private $controlDimFetchAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var NetteControlNaming
|
||||
*/
|
||||
private $netteControlNaming;
|
||||
|
||||
/**
|
||||
* @var ReturnTypeInferer
|
||||
*/
|
||||
private $returnTypeInferer;
|
||||
|
||||
public function __construct(
|
||||
FunctionLikeParsedNodesFinder $functionLikeParsedNodesFinder,
|
||||
ControlDimFetchAnalyzer $controlDimFetchAnalyzer,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
NetteControlNaming $netteControlNaming,
|
||||
ReturnTypeInferer $returnTypeInferer
|
||||
) {
|
||||
$this->functionLikeParsedNodesFinder = $functionLikeParsedNodesFinder;
|
||||
$this->controlDimFetchAnalyzer = $controlDimFetchAnalyzer;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->netteControlNaming = $netteControlNaming;
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function resolve(Node $node): array
|
||||
{
|
||||
if (! $node instanceof ArrayDimFetch) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$controlShortName = $this->controlDimFetchAnalyzer->matchName($node);
|
||||
if ($controlShortName === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$createComponentClassMethod = $this->matchCreateComponentClassMethod($node, $controlShortName);
|
||||
if ($createComponentClassMethod === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$createComponentClassMethodReturnType = $this->returnTypeInferer->inferFunctionLike(
|
||||
$createComponentClassMethod
|
||||
);
|
||||
|
||||
if (! $createComponentClassMethodReturnType instanceof TypeWithClassName) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
$controlShortName => $createComponentClassMethodReturnType->getClassName(),
|
||||
];
|
||||
}
|
||||
|
||||
private function matchCreateComponentClassMethod(
|
||||
ArrayDimFetch $arrayDimFetch,
|
||||
string $controlShortName
|
||||
): ?ClassMethod {
|
||||
$callerType = $this->nodeTypeResolver->getStaticType($arrayDimFetch->var);
|
||||
if (! $callerType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$createComponentClassMethodName = $this->netteControlNaming->createCreateComponentClassMethodName(
|
||||
$controlShortName
|
||||
);
|
||||
|
||||
return $this->functionLikeParsedNodesFinder->findClassMethod(
|
||||
$createComponentClassMethodName,
|
||||
$callerType->getClassName()
|
||||
);
|
||||
}
|
||||
}
|
26
rules/nette/src/Naming/NetteControlNaming.php
Normal file
26
rules/nette/src/Naming/NetteControlNaming.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\Util\StaticRectorStrings;
|
||||
|
||||
final class NetteControlNaming
|
||||
{
|
||||
public function createVariableName(string $shortName): string
|
||||
{
|
||||
$variableName = StaticRectorStrings::underscoreToPascalCase($shortName);
|
||||
if (Strings::endsWith($variableName, 'Form')) {
|
||||
return $variableName;
|
||||
}
|
||||
|
||||
return $variableName . 'Control';
|
||||
}
|
||||
|
||||
public function createCreateComponentClassMethodName(string $shortName): string
|
||||
{
|
||||
return 'createComponent' . ucfirst($this->createVariableName($shortName));
|
||||
}
|
||||
}
|
50
rules/nette/src/NodeAnalyzer/ControlDimFetchAnalyzer.php
Normal file
50
rules/nette/src/NodeAnalyzer/ControlDimFetchAnalyzer.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class ControlDimFetchAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
public function __construct(NodeTypeResolver $nodeTypeResolver)
|
||||
{
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
}
|
||||
|
||||
public function matchName(Node $node): ?string
|
||||
{
|
||||
if (! $node instanceof ArrayDimFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isContainerVariable($node->var)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $node->dim instanceof String_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node->dim->value;
|
||||
}
|
||||
|
||||
private function isContainerVariable(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->nodeTypeResolver->isObjectTypeOrNullableObjectType($node, 'Nette\ComponentModel\IContainer');
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\NodeResolver;
|
||||
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Expr;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
|
||||
@ -38,14 +38,13 @@ final class FormVariableInputNameTypeResolver
|
||||
$this->methodNamesByInputNamesResolver = $methodNamesByInputNamesResolver;
|
||||
}
|
||||
|
||||
public function resolveControlTypeByInputName(Variable $formVariable, string $inputName): string
|
||||
public function resolveControlTypeByInputName(Expr $formOrControlExpr, string $inputName): string
|
||||
{
|
||||
$methodNamesByInputNames = $this->methodNamesByInputNamesResolver->resolveExpr($formVariable);
|
||||
$methodNamesByInputNames = $this->methodNamesByInputNamesResolver->resolveExpr($formOrControlExpr);
|
||||
|
||||
$formAddMethodName = $methodNamesByInputNames[$inputName] ?? null;
|
||||
|
||||
if ($formAddMethodName === null) {
|
||||
$message = sprintf('Not found for "%s" input name', $inputName);
|
||||
$message = sprintf('Type was not found for "%s" input name', $inputName);
|
||||
throw new ShouldNotHappenException($message);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ final class MethodNamesByInputNamesResolver
|
||||
|
||||
foreach ($this->formControlTypeResolvers as $formControlTypeResolver) {
|
||||
$currentMethodNamesByInputNames = $formControlTypeResolver->resolve($node);
|
||||
|
||||
$methodNamesByInputNames = array_merge($methodNamesByInputNames, $currentMethodNamesByInputNames);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\Rector\ArrayDimFetch;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Nette\DocBlock\VarAnnotationManipulator;
|
||||
use Rector\Nette\Naming\NetteControlNaming;
|
||||
use Rector\Nette\NodeAnalyzer\ControlDimFetchAnalyzer;
|
||||
use Rector\Nette\NodeResolver\MethodNamesByInputNamesResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://amateri.com for sponsoring this rule - visit them on https://www.startupjobs.cz/startup/scrumworks-s-r-o
|
||||
*
|
||||
* @see \Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector\ChangeControlArrayAccessToAnnotatedControlVariableRectorTest
|
||||
*/
|
||||
final class ChangeControlArrayAccessToAnnotatedControlVariableRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ControlDimFetchAnalyzer
|
||||
*/
|
||||
private $controlDimFetchAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NetteControlNaming
|
||||
*/
|
||||
private $netteControlNaming;
|
||||
|
||||
/**
|
||||
* @var VarAnnotationManipulator
|
||||
*/
|
||||
private $varAnnotationManipulator;
|
||||
|
||||
/**
|
||||
* @var MethodNamesByInputNamesResolver
|
||||
*/
|
||||
private $methodNamesByInputNamesResolver;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $alreadyInitializedAssignsClassMethodObjectHashes = [];
|
||||
|
||||
public function __construct(
|
||||
VarAnnotationManipulator $varAnnotationManipulator,
|
||||
ControlDimFetchAnalyzer $controlDimFetchAnalyzer,
|
||||
NetteControlNaming $netteControlNaming,
|
||||
MethodNamesByInputNamesResolver $methodNamesByInputNamesResolver
|
||||
) {
|
||||
$this->controlDimFetchAnalyzer = $controlDimFetchAnalyzer;
|
||||
$this->netteControlNaming = $netteControlNaming;
|
||||
$this->varAnnotationManipulator = $varAnnotationManipulator;
|
||||
$this->methodNamesByInputNamesResolver = $methodNamesByInputNamesResolver;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Change magic $this["some_component"] to variable assign with @var annotation', [
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomePresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if ($this['some_form']->isSubmitted()) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomePresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
/** @var \Nette\Application\UI\Form $someForm */
|
||||
$someForm = $this['some_form'];
|
||||
if ($someForm->isSubmitted()) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
PHP
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ArrayDimFetch::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayDimFetch $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$controlName = $this->controlDimFetchAnalyzer->matchName($node);
|
||||
if ($controlName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$variableName = $this->netteControlNaming->createVariableName($controlName);
|
||||
|
||||
$controlObjectType = $this->resolveControlType($node, $controlName);
|
||||
$this->addAssignExpressionForFirstCase($variableName, $node, $controlObjectType);
|
||||
|
||||
return new Variable($variableName);
|
||||
}
|
||||
|
||||
private function createAssignExpression(string $variableName, ArrayDimFetch $arrayDimFetch): Expression
|
||||
{
|
||||
$variable = new Variable($variableName);
|
||||
$assignedDimFetch = clone $arrayDimFetch;
|
||||
$assign = new Assign($variable, $assignedDimFetch);
|
||||
|
||||
return new Expression($assign);
|
||||
}
|
||||
|
||||
private function addAssignExpressionForFirstCase(
|
||||
string $variableName,
|
||||
ArrayDimFetch $arrayDimFetch,
|
||||
ObjectType $controlObjectType
|
||||
): void {
|
||||
/** @var ClassMethod|null $classMethod */
|
||||
$classMethod = $arrayDimFetch->getAttribute(AttributeKey::METHOD_NODE);
|
||||
if ($classMethod !== null) {
|
||||
$classMethodObjectHash = spl_object_hash($classMethod);
|
||||
if (in_array($classMethodObjectHash, $this->alreadyInitializedAssignsClassMethodObjectHashes, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->alreadyInitializedAssignsClassMethodObjectHashes[] = $classMethodObjectHash;
|
||||
}
|
||||
|
||||
$assignExpression = $this->createAssignExpression($variableName, $arrayDimFetch);
|
||||
|
||||
$this->varAnnotationManipulator->decorateNodeWithInlineVarType(
|
||||
$assignExpression,
|
||||
$controlObjectType,
|
||||
$variableName
|
||||
);
|
||||
|
||||
$currentStatement = $arrayDimFetch->getAttribute(AttributeKey::CURRENT_STATEMENT);
|
||||
$this->addNodeBeforeNode($assignExpression, $currentStatement);
|
||||
}
|
||||
|
||||
private function resolveControlType(ArrayDimFetch $arrayDimFetch, string $controlName): ObjectType
|
||||
{
|
||||
$controlTypes = $this->methodNamesByInputNamesResolver->resolveExpr($arrayDimFetch);
|
||||
if ($controlTypes === []) {
|
||||
throw new NotImplementedYetException();
|
||||
}
|
||||
|
||||
if (! isset($controlTypes[$controlName])) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$controlType = $controlTypes[$controlName];
|
||||
|
||||
return new ObjectType($controlType);
|
||||
}
|
||||
}
|
@ -8,15 +8,15 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareFullyQualifiedIdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\Util\StaticRectorStrings;
|
||||
use Rector\Nette\DocBlock\VarAnnotationManipulator;
|
||||
use Rector\Nette\Naming\NetteControlNaming;
|
||||
use Rector\Nette\NodeAnalyzer\ControlDimFetchAnalyzer;
|
||||
use Rector\Nette\NodeResolver\FormVariableInputNameTypeResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
@ -32,9 +32,31 @@ final class ChangeFormArrayAccessToAnnotatedControlVariableRector extends Abstra
|
||||
*/
|
||||
private $formVariableInputNameTypeResolver;
|
||||
|
||||
public function __construct(FormVariableInputNameTypeResolver $formVariableInputNameTypeResolver)
|
||||
{
|
||||
/**
|
||||
* @var ControlDimFetchAnalyzer
|
||||
*/
|
||||
private $controlDimFetchAnalyzer;
|
||||
|
||||
/**
|
||||
* @var NetteControlNaming
|
||||
*/
|
||||
private $netteControlNaming;
|
||||
|
||||
/**
|
||||
* @var VarAnnotationManipulator
|
||||
*/
|
||||
private $varAnnotationManipulator;
|
||||
|
||||
public function __construct(
|
||||
FormVariableInputNameTypeResolver $formVariableInputNameTypeResolver,
|
||||
ControlDimFetchAnalyzer $controlDimFetchAnalyzer,
|
||||
NetteControlNaming $netteControlNaming,
|
||||
VarAnnotationManipulator $varAnnotationManipulator
|
||||
) {
|
||||
$this->formVariableInputNameTypeResolver = $formVariableInputNameTypeResolver;
|
||||
$this->controlDimFetchAnalyzer = $controlDimFetchAnalyzer;
|
||||
$this->netteControlNaming = $netteControlNaming;
|
||||
$this->varAnnotationManipulator = $varAnnotationManipulator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -90,8 +112,8 @@ PHP
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$dimString = $this->matchNetteFormArrayDimString($node);
|
||||
if ($dimString === null) {
|
||||
$inputName = $this->controlDimFetchAnalyzer->matchName($node);
|
||||
if ($inputName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -99,8 +121,7 @@ PHP
|
||||
return null;
|
||||
}
|
||||
|
||||
$inputName = $this->getValue($dimString);
|
||||
$controlVariableName = $this->createControlVariableName($inputName);
|
||||
$controlVariableName = $this->netteControlNaming->createVariableName($inputName);
|
||||
|
||||
$controlVariableToFormDimFetchAssign = new Assign(new Variable($controlVariableName), clone $node);
|
||||
$assignExpression = new Expression($controlVariableToFormDimFetchAssign);
|
||||
@ -114,52 +135,23 @@ PHP
|
||||
$inputName
|
||||
);
|
||||
|
||||
$this->addVarTag($controlVariableToFormDimFetchAssign, $assignExpression, $controlVariableName, $controlType);
|
||||
$formVariableName = $this->getName($formVariable);
|
||||
if ($formVariableName === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$controlObjectType = new ObjectType($controlType);
|
||||
$this->varAnnotationManipulator->decorateNodeWithInlineVarType(
|
||||
$assignExpression,
|
||||
$controlObjectType,
|
||||
$controlVariableName
|
||||
);
|
||||
|
||||
$this->addNodeBeforeNode($assignExpression, $node);
|
||||
|
||||
return new Variable($controlVariableName);
|
||||
}
|
||||
|
||||
private function addVarTag(
|
||||
Assign $assign,
|
||||
Expression $assignExpression,
|
||||
string $controlName,
|
||||
string $controlType
|
||||
): PhpDocInfo {
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($assignExpression);
|
||||
|
||||
$varTagValueNode = new VarTagValueNode(
|
||||
new AttributeAwareFullyQualifiedIdentifierTypeNode($controlType),
|
||||
'$' . $controlName,
|
||||
''
|
||||
);
|
||||
|
||||
$phpDocInfo->addTagValueNode($varTagValueNode);
|
||||
$phpDocInfo->makeSingleLined();
|
||||
|
||||
$assign->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
|
||||
|
||||
return $phpDocInfo;
|
||||
}
|
||||
|
||||
private function matchNetteFormArrayDimString(ArrayDimFetch $arrayDimFetch): ?String_
|
||||
{
|
||||
if (! $arrayDimFetch->var instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isObjectTypeOrNullableObjectType($arrayDimFetch->var, 'Nette\ComponentModel\IComponent')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $arrayDimFetch->dim instanceof String_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $arrayDimFetch->dim;
|
||||
}
|
||||
|
||||
private function isBeingAssignedOrInitialized(ArrayDimFetch $arrayDimFetch): bool
|
||||
{
|
||||
$parent = $arrayDimFetch->getAttribute(AttributeKey::PARENT_NODE);
|
||||
@ -173,9 +165,4 @@ PHP
|
||||
|
||||
return $parent->expr === $arrayDimFetch;
|
||||
}
|
||||
|
||||
private function createControlVariableName(string $inputName): string
|
||||
{
|
||||
return StaticRectorStrings::underscoreToPascalCase($inputName) . 'Control';
|
||||
}
|
||||
}
|
||||
|
@ -11,20 +11,17 @@ use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareFullyQualifiedIdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Nette\DocBlock\VarAnnotationManipulator;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
@ -34,6 +31,16 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
*/
|
||||
final class MakeGetComponentAssignAnnotatedRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var VarAnnotationManipulator
|
||||
*/
|
||||
private $varAnnotationManipulator;
|
||||
|
||||
public function __construct(VarAnnotationManipulator $varAnnotationManipulator)
|
||||
{
|
||||
$this->varAnnotationManipulator = $varAnnotationManipulator;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Add doc type for magic $control->getComponent(...) assign', [
|
||||
@ -125,48 +132,15 @@ PHP
|
||||
}
|
||||
|
||||
$controlType = $this->resolveControlType($node);
|
||||
if ($controlType instanceof MixedType) {
|
||||
if (! $controlType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->resolvePhpDocInfo($node);
|
||||
if ($phpDocInfo->getVarTagValue() !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$attributeAwareFullyQualifiedIdentifierTypeNode = new AttributeAwareFullyQualifiedIdentifierTypeNode(
|
||||
$controlType->getClassName()
|
||||
);
|
||||
$varTagValueNode = new VarTagValueNode(
|
||||
$attributeAwareFullyQualifiedIdentifierTypeNode,
|
||||
'$' . $variableName,
|
||||
''
|
||||
);
|
||||
$phpDocInfo->addTagValueNode($varTagValueNode);
|
||||
$this->varAnnotationManipulator->decorateNodeWithInlineVarType($node, $controlType, $variableName);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function resolvePhpDocInfo(Assign $assign): PhpDocInfo
|
||||
{
|
||||
$currentStmt = $assign->getAttribute(AttributeKey::CURRENT_STATEMENT);
|
||||
if ($currentStmt instanceof Expression) {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $currentStmt->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
} else {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $assign->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
}
|
||||
|
||||
if ($phpDocInfo === null) {
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($assign);
|
||||
}
|
||||
|
||||
$phpDocInfo->makeSingleLined();
|
||||
|
||||
return $phpDocInfo;
|
||||
}
|
||||
|
||||
private function resolveCreateComponentMethodCallReturnType(MethodCall $methodCall): Type
|
||||
{
|
||||
/** @var Scope|null $scope */
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Nette\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ChangeControlArrayAccessToAnnotatedControlVariableRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return ChangeControlArrayAccessToAnnotatedControlVariableRector::class;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomePresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if ($this['some_form']->isSubmitted()) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomePresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
/** @var \Nette\Application\UI\Form $someForm */
|
||||
$someForm = $this['some_form'];
|
||||
if ($someForm->isSubmitted()) {
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomeOtherPresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
if ($this['some_form']->isSubmitted()) {
|
||||
return $this['some_form']->getValues();
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeControlArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Presenter;
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
final class SomeOtherPresenter extends Presenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
/** @var \Nette\Application\UI\Form $someForm */
|
||||
$someForm = $this['some_form'];
|
||||
if ($someForm->isSubmitted()) {
|
||||
return $someForm->getValues();
|
||||
}
|
||||
}
|
||||
|
||||
protected function createComponentSomeForm()
|
||||
{
|
||||
return new Form();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -6,6 +6,9 @@ use PhpParser\NodeTraverser;
|
||||
|
||||
class SkipAnonymousClass
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$anonymousClass = new class() extends NodeTraverser
|
||||
|
@ -11,11 +11,11 @@ use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use Rector\Core\Naming\NamespaceMatcher;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\ValueObject\RenamedNamespaceValueObject;
|
||||
use Rector\Naming\NamespaceMatcher;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Rector;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
@ -13,11 +12,11 @@ use PhpParser\Node\Expr\Cast\Bool_;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\AnonymousClass\NodeAnalyzer\ClassNodeAnalyzer;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\Contract\Rector\PhpRectorInterface;
|
||||
use Rector\Core\Exclusion\ExclusionManager;
|
||||
@ -102,6 +101,11 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
*/
|
||||
private $currentRectorProvider;
|
||||
|
||||
/**
|
||||
* @var ClassNodeAnalyzer
|
||||
*/
|
||||
private $classNodeAnalyzer;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
@ -113,7 +117,8 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
ParameterProvider $parameterProvider,
|
||||
CurrentRectorProvider $currentRectorProvider
|
||||
CurrentRectorProvider $currentRectorProvider,
|
||||
ClassNodeAnalyzer $classNodeAnalyzer
|
||||
): void {
|
||||
$this->symfonyStyle = $symfonyStyle;
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
@ -123,6 +128,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
$this->currentRectorProvider = $currentRectorProvider;
|
||||
$this->classNodeAnalyzer = $classNodeAnalyzer;
|
||||
}
|
||||
|
||||
public function beforeTraverse(array $nodes)
|
||||
@ -215,13 +221,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
|
||||
|
||||
protected function isAnonymousClass(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof Class_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$className = $this->nodeNameResolver->getName($node);
|
||||
|
||||
return $className === null || Strings::contains($className, 'AnonymousClass');
|
||||
return $this->classNodeAnalyzer->isAnonymousClass($node);
|
||||
}
|
||||
|
||||
protected function createCountedValueName(string $countedValueName, ?Scope $scope): string
|
||||
|
@ -14,7 +14,6 @@ use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
|
||||
@ -90,25 +89,6 @@ trait NodeTypeResolverTrait
|
||||
return $this->nodeTypeResolver->isObjectType($node, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ObjectType|string $desiredType
|
||||
*/
|
||||
protected function isObjectTypeOrNullableObjectType(Node $node, $desiredType): bool
|
||||
{
|
||||
if ($this->isNullableObjectType($node)) {
|
||||
/** @var UnionType $nodeType */
|
||||
$nodeType = $this->nodeTypeResolver->resolve($node);
|
||||
|
||||
$nodeType = $this->typeUnwrapper->unwrapNullableType($nodeType);
|
||||
if ($nodeType instanceof TypeWithClassName) {
|
||||
$desiredTypeString = $desiredType instanceof ObjectType ? $desiredType->getClassName() : $desiredType;
|
||||
return is_a($nodeType->getClassName(), $desiredTypeString, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->nodeTypeResolver->isObjectType($node, $desiredType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[]|ObjectType[] $requiredTypes
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user