Merge pull request #3787 from rectorphp/cover-array-dim-fetch

This commit is contained in:
Tomas Votruba 2020-07-26 13:08:01 +02:00 committed by GitHub
commit 812dea97d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 105 additions and 10 deletions

View File

@ -387,3 +387,4 @@ parameters:
- '#Method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:findPreviousAssignToExpr\(\) should return PhpParser\\Node\\Expr\\Assign\|null but returns PhpParser\\Node\|null#'
- '#Method Rector\\Nette\\FormControlTypeResolver\\GetComponentMethodCallFormControlTypeResolver\:\:resolve\(\) should return array<string, string\> but returns array<int\|string, string\>#'
- '#Class with base "LexerFactory" name is already used in "PHPStan\\Parser\\LexerFactory", "Rector\\Core\\PhpParser\\Parser\\LexerFactory"\. Use unique name to make classes easy to recognize#'
- '#Parameter \#1 \$shortControlString of method Rector\\Nette\\Rector\\Assign\\MakeGetComponentAssignAnnotatedRector\:\:resolveTypeFromShortControlNameAndVariable\(\) expects PhpParser\\Node\\Scalar\\String_, PhpParser\\Node\\Expr\|null given#'

View File

@ -5,6 +5,8 @@ declare(strict_types=1);
namespace Rector\Nette\Rector\Assign;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
@ -19,6 +21,7 @@ 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;
@ -103,7 +106,7 @@ PHP
*/
public function refactor(Node $node): ?Node
{
if (! $this->isOnClassMethodCall($node->expr, 'Nette\Application\UI\Control', 'getComponent')) {
if (! $this->isGetComponentMethodCallOrArrayDimFetchOnControl($node->expr)) {
return null;
}
@ -121,10 +124,8 @@ PHP
return null;
}
/** @var MethodCall $methodCall */
$methodCall = $node->expr;
$createComponentClassMethodReturnType = $this->resolveGetComponentReturnType($methodCall);
if (! $createComponentClassMethodReturnType instanceof TypeWithClassName) {
$controlType = $this->resolveControlType($node);
if ($controlType instanceof MixedType) {
return null;
}
@ -134,7 +135,7 @@ PHP
}
$attributeAwareFullyQualifiedIdentifierTypeNode = new AttributeAwareFullyQualifiedIdentifierTypeNode(
$createComponentClassMethodReturnType->getClassName()
$controlType->getClassName()
);
$varTagValueNode = new VarTagValueNode(
$attributeAwareFullyQualifiedIdentifierTypeNode,
@ -166,7 +167,7 @@ PHP
return $phpDocInfo;
}
private function resolveGetComponentReturnType(MethodCall $methodCall): Type
private function resolveCreateComponentMethodCallReturnType(MethodCall $methodCall): Type
{
/** @var Scope|null $scope */
$scope = $methodCall->getAttribute(AttributeKey::SCOPE);
@ -174,8 +175,6 @@ PHP
return new MixedType();
}
$calledOnType = $scope->getType($methodCall->var);
if (count($methodCall->args) !== 1) {
return new MixedType();
}
@ -185,9 +184,53 @@ PHP
return new MixedType();
}
$componentName = $this->getValue($firstArgumentValue);
return $this->resolveTypeFromShortControlNameAndVariable($firstArgumentValue, $scope, $methodCall->var);
}
private function isGetComponentMethodCallOrArrayDimFetchOnControl(Expr $expr): bool
{
if ($this->isOnClassMethodCall($expr, 'Nette\Application\UI\Control', 'getComponent')) {
return true;
}
return $this->isArrayDimFetchStringOnControlVariable($expr);
}
private function resolveArrayDimFetchControlType(ArrayDimFetch $arrayDimFetch): Type
{
/** @var Scope|null $scope */
$scope = $arrayDimFetch->getAttribute(AttributeKey::SCOPE);
if ($scope === null) {
throw new ShouldNotHappenException();
}
return $this->resolveTypeFromShortControlNameAndVariable($arrayDimFetch->dim, $scope, $arrayDimFetch->var);
}
private function isArrayDimFetchStringOnControlVariable(Expr $expr): bool
{
if (! $expr instanceof ArrayDimFetch) {
return false;
}
if (! $expr->dim instanceof String_) {
return false;
}
$varStaticType = $this->getStaticType($expr->var);
if (! $varStaticType instanceof TypeWithClassName) {
return false;
}
return is_a($varStaticType->getClassName(), 'Nette\Application\UI\Control', true);
}
private function resolveTypeFromShortControlNameAndVariable(String_ $shortControlString, Scope $scope, Expr $expr)
{
$componentName = $this->getValue($shortControlString);
$methodName = sprintf('createComponent%s', ucfirst($componentName));
$calledOnType = $scope->getType($expr);
if (! $calledOnType instanceof ObjectType) {
return new MixedType();
}
@ -201,4 +244,21 @@ PHP
return ParametersAcceptorSelector::selectSingle($method->getVariants())->getReturnType();
}
private function resolveControlType(Assign $assign): Type
{
if ($assign->expr instanceof MethodCall) {
/** @var MethodCall $methodCall */
$methodCall = $assign->expr;
return $this->resolveCreateComponentMethodCallReturnType($methodCall);
}
if ($assign->expr instanceof ArrayDimFetch) {
/** @var ArrayDimFetch $arrayDimFetch */
$arrayDimFetch = $assign->expr;
return $this->resolveArrayDimFetchControlType($arrayDimFetch);
}
return new MixedType();
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Rector\Nette\Tests\Rector\Assign\MakeGetComponentAssignAnnotatedRector\Fixture;
use Rector\Nette\Tests\Rector\Assign\MakeGetComponentAssignAnnotatedRector\Source\ExternalControl;
final class ArrayDimAccess
{
public function run()
{
$externalControl = new ExternalControl();
$anotherControl = $externalControl['another'];
}
}
?>
-----
<?php
namespace Rector\Nette\Tests\Rector\Assign\MakeGetComponentAssignAnnotatedRector\Fixture;
use Rector\Nette\Tests\Rector\Assign\MakeGetComponentAssignAnnotatedRector\Source\ExternalControl;
final class ArrayDimAccess
{
public function run()
{
$externalControl = new ExternalControl();
/** @var \Rector\Nette\Tests\Rector\Assign\MakeGetComponentAssignAnnotatedRector\Source\AnotherControl $anotherControl */
$anotherControl = $externalControl['another'];
}
}
?>