cover return array method

This commit is contained in:
TomasVotruba 2020-06-30 10:46:05 +02:00
parent 87fa3a25e4
commit 59dbe98883
4 changed files with 197 additions and 52 deletions

View File

@ -1,4 +1,4 @@
# All 512 Rectors Overview
# All 513 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -26,7 +26,7 @@
- [MagicDisclosure](#magicdisclosure) (5)
- [MockistaToMockery](#mockistatomockery) (2)
- [MysqlToMysqli](#mysqltomysqli) (4)
- [Naming](#naming) (1)
- [Naming](#naming) (2)
- [Nette](#nette) (12)
- [NetteCodeQuality](#nettecodequality) (1)
- [NetteKdyby](#nettekdyby) (4)
@ -4812,6 +4812,28 @@ Rename property and method param to match its type
<br><br>
### `RenameVariableToMatchNewTypeRector`
- class: [`Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector`](/../master/rules/naming/src/Rector/ClassMethod/RenameVariableToMatchNewTypeRector.php)
- [test fixtures](/../master/rules/naming/tests/Rector/ClassMethod/RenameVariableToMatchNewTypeRector/Fixture)
Rename variable to match new ClassType
```diff
final class SomeClass
{
public function run()
{
- $search = new DreamSearch();
- $search->advance();
+ $dreamSearch = new DreamSearch();
+ $dreamSearch->advance();
}
}
```
<br><br>
## Nette
### `AddDatePickerToDateControlRector`
@ -10383,7 +10405,7 @@ Turns `@Template` annotation to explicit method call in Controller of FrameworkE
- */
public function indexAction()
{
+ return $this->render("index.html.twig");
+ return $this->render('index.html.twig');
}
```

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\Sensio\NodeFactory;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\FuncCall;
@ -13,9 +14,11 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ArrayType;
use Rector\BetterPhpDocParser\PhpDocNode\Sensio\SensioTemplateTagValueNode;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\Sensio\Helper\TemplateGuesser;
final class ThisRenderFactory
@ -40,16 +43,23 @@ final class ThisRenderFactory
*/
private $arrayFromCompactFactory;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
public function __construct(
NodeFactory $nodeFactory,
TemplateGuesser $templateGuesser,
NodeNameResolver $nodeNameResolver,
ArrayFromCompactFactory $arrayFromCompactFactory
ArrayFromCompactFactory $arrayFromCompactFactory,
NodeTypeResolver $nodeTypeResolver
) {
$this->nodeFactory = $nodeFactory;
$this->templateGuesser = $templateGuesser;
$this->nodeNameResolver = $nodeNameResolver;
$this->arrayFromCompactFactory = $arrayFromCompactFactory;
$this->nodeTypeResolver = $nodeTypeResolver;
}
public function create(
@ -71,21 +81,10 @@ final class ThisRenderFactory
SensioTemplateTagValueNode $sensioTemplateTagValueNode
): array {
$arguments = [$this->resolveTemplateName($classMethod, $sensioTemplateTagValueNode)];
if ($return === null) {
return $this->nodeFactory->createArgs($arguments);
}
if ($return->expr instanceof Array_ && count($return->expr->items)) {
$arguments[] = $return->expr;
} elseif ($return->expr instanceof FuncCall && $this->nodeNameResolver->isName($return->expr, 'compact')) {
/** @var FuncCall $compactFunCall */
$compactFunCall = $return->expr;
$array = $this->arrayFromCompactFactory->createArrayFromCompactFuncCall($compactFunCall);
$arguments[1] = new Arg($array);
} elseif ($sensioTemplateTagValueNode->getVars() !== []) {
$variableList = $this->createArrayFromVars($sensioTemplateTagValueNode->getVars());
$arguments[1] = new Arg($variableList);
$parametersExpr = $this->resolveParametersExpr($return, $sensioTemplateTagValueNode);
if ($parametersExpr !== null) {
$arguments[] = new Arg($parametersExpr);
}
return $this->nodeFactory->createArgs($arguments);
@ -114,4 +113,36 @@ final class ThisRenderFactory
return new Array_($arrayItems);
}
private function resolveParametersExpr(
?Return_ $return,
SensioTemplateTagValueNode $sensioTemplateTagValueNode
): ?Expr {
if ($return === null) {
return null;
}
if ($return->expr instanceof Array_ && count($return->expr->items)) {
return $return->expr;
}
if ($return->expr instanceof MethodCall) {
$returnStaticType = $this->nodeTypeResolver->getStaticType($return->expr);
if ($returnStaticType instanceof ArrayType) {
return $return->expr;
}
}
if ($return->expr instanceof FuncCall && $this->nodeNameResolver->isName($return->expr, 'compact')) {
/** @var FuncCall $compactFunCall */
$compactFunCall = $return->expr;
return $this->arrayFromCompactFactory->createArrayFromCompactFuncCall($compactFunCall);
}
if ($sensioTemplateTagValueNode->getVars() !== []) {
return $this->createArrayFromVars($sensioTemplateTagValueNode->getVars());
}
return null;
}
}

View File

@ -15,6 +15,7 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use Rector\BetterPhpDocParser\PhpDocNode\Sensio\SensioTemplateTagValueNode;
use Rector\Core\Rector\AbstractRector;
@ -81,7 +82,7 @@ PHP
<<<'PHP'
public function indexAction()
{
return $this->render("index.html.twig");
return $this->render('index.html.twig');
}
PHP
),
@ -162,46 +163,35 @@ PHP
/** @var Return_|null $lastReturn */
$lastReturn = $this->betterNodeFinder->findLastInstanceOf((array) $classMethod->stmts, Return_::class);
if ($lastReturn === null) {
$this->processClassMethodWithoutReturn($classMethod, $sensioTemplateTagValueNode);
} elseif ($lastReturn->expr !== null) {
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
$thisRenderMethodCall = $this->thisRenderFactory->create(
$classMethod,
$lastReturn,
$sensioTemplateTagValueNode
);
$returnStaticType = $this->getStaticType($lastReturn->expr);
if (! $lastReturn->expr instanceof MethodCall) {
if (! $hasThisRenderOrReturnsResponse) {
$lastReturn->expr = $thisRenderMethodCall;
}
} elseif ($returnStaticType instanceof MixedType) {
return;
}
$isArrayOrResponseType = $this->arrayUnionResponseTypeAnalyzer->isArrayUnionResponseType(
$returnStaticType,
self::RESPONSE_CLASS
);
if ($isArrayOrResponseType) {
$this->processIsArrayOrResponseType($lastReturn, $lastReturn->expr, $thisRenderMethodCall);
}
// nothing we can do
if ($lastReturn !== null && $lastReturn->expr === null) {
return;
}
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, self::RESPONSE_CLASS);
$this->removePhpDocTagValueNode($classMethod, SensioTemplateTagValueNode::class);
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
$thisRenderMethodCall = $this->thisRenderFactory->create(
$classMethod,
$lastReturn,
$sensioTemplateTagValueNode
);
if ($lastReturn === null) {
$this->refactorNoReturn($classMethod, $thisRenderMethodCall);
return;
}
$this->refactorReturnWithValue(
$lastReturn,
$hasThisRenderOrReturnsResponse,
$thisRenderMethodCall,
$classMethod
);
}
private function processClassMethodWithoutReturn(
ClassMethod $classMethod,
SensioTemplateTagValueNode $sensioTemplateTagValueNode
MethodCall $thisRenderMethodCall
): void {
// create "$this->render('template.file.twig.html', ['key' => 'value']);" method call
$thisRenderMethodCall = $this->thisRenderFactory->create($classMethod, null, $sensioTemplateTagValueNode);
$classMethod->stmts[] = new Return_($thisRenderMethodCall);
}
@ -234,4 +224,48 @@ PHP
return $this->isReturnOfObjectType($lastReturn, self::RESPONSE_CLASS);
}
private function refactorNoReturn(ClassMethod $classMethod, MethodCall $thisRenderMethodCall): void
{
$this->processClassMethodWithoutReturn($classMethod, $thisRenderMethodCall);
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, self::RESPONSE_CLASS);
$this->removePhpDocTagValueNode($classMethod, SensioTemplateTagValueNode::class);
}
private function refactorReturnWithValue(
Return_ $lastReturn,
bool $hasThisRenderOrReturnsResponse,
MethodCall $thisRenderMethodCall,
ClassMethod $classMethod
): void {
/** @var Expr $lastReturnExpr */
$lastReturnExpr = $lastReturn->expr;
$returnStaticType = $this->getStaticType($lastReturnExpr);
if (! $lastReturn->expr instanceof MethodCall) {
if (! $hasThisRenderOrReturnsResponse) {
$lastReturn->expr = $thisRenderMethodCall;
}
} elseif ($returnStaticType instanceof ArrayType) {
$lastReturn->expr = $thisRenderMethodCall;
} elseif ($returnStaticType instanceof MixedType) {
// nothing we can do
return;
}
$isArrayOrResponseType = $this->arrayUnionResponseTypeAnalyzer->isArrayUnionResponseType(
$returnStaticType,
self::RESPONSE_CLASS
);
if ($isArrayOrResponseType) {
$this->processIsArrayOrResponseType($lastReturn, $lastReturnExpr, $thisRenderMethodCall);
}
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, self::RESPONSE_CLASS);
$this->removePhpDocTagValueNode($classMethod, SensioTemplateTagValueNode::class);
}
}

View File

@ -0,0 +1,58 @@
<?php declare (strict_types=1);
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
final class ClassWithNestedArrayController extends AbstractController
{
/**
* @Template("with_some_template.twig")
*/
public function indexAction()
{
return $this->handleStuff();
}
/**
* @return array
*/
private function handleStuff()
{
return [
'hello' => 'world'
];
}
}
?>
-----
<?php declare (strict_types=1);
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
final class ClassWithNestedArrayController extends AbstractController
{
public function indexAction(): \Symfony\Component\HttpFoundation\Response
{
return $this->render('with_some_template.twig', $this->handleStuff());
}
/**
* @return array
*/
private function handleStuff()
{
return [
'hello' => 'world'
];
}
}
?>