Make remove parent/name only on component, skip form (#5266)

This commit is contained in:
Tomas Votruba 2021-01-20 16:57:49 +01:00 committed by GitHub
parent 7d001c13b0
commit 85e7f7fa2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 60 deletions

View File

@ -104,6 +104,6 @@ CODE_SAMPLE
return true;
}
return ! $this->isInObjectType($classLike, 'Doctrine\ORM\EntityRepository');
return ! $this->isObjectType($classLike, 'Doctrine\ORM\EntityRepository');
}
}

View File

@ -5,10 +5,12 @@ declare(strict_types=1);
namespace Rector\Laravel\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use Rector\Core\Rector\AbstractRector;
use Rector\Nette\NodeAnalyzer\StaticCallAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -82,7 +84,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->isInObjectType($node, 'Illuminate\Database\Eloquent\Model')) {
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof ClassLike) {
return null;
}
if (! $this->isObjectType($classLike, 'Illuminate\Database\Eloquent\Model')) {
return null;
}

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Rector\Nette\NodeFinder;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Param;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class ParamFinder
{
/**
* @var BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @var BetterStandardPrinter
*/
private $betterStandardPrinter;
public function __construct(BetterNodeFinder $betterNodeFinder, BetterStandardPrinter $betterStandardPrinter)
{
$this->betterNodeFinder = $betterNodeFinder;
$this->betterStandardPrinter = $betterStandardPrinter;
}
/**
* @param Node|Node[] $nodeHaystack
*/
public function isInAssign($nodeHaystack, Param $param): bool
{
$variable = $param->var;
return (bool) $this->betterNodeFinder->find($nodeHaystack, function (Node $node) use ($variable): bool {
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $parent instanceof Assign) {
return false;
}
return $this->betterStandardPrinter->areNodesEqual($node, $variable);
});
}
}

View File

@ -5,16 +5,16 @@ declare(strict_types=1);
namespace Rector\Nette\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Rector\Nette\NodeAnalyzer\StaticCallAnalyzer;
use Rector\Nette\NodeFinder\ParamFinder;
use Rector\NodeCollector\Reflection\MethodReflectionProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -22,6 +22,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see https://github.com/nette/component-model/commit/1fb769f4602cf82694941530bac1111b3c5cd11b
* This only applied to child of \Nette\Application\UI\Control, not Forms! Forms still need to be attached to their parents
*
* @see \Rector\Nette\Tests\Rector\ClassMethod\RemoveParentAndNameFromComponentConstructorRector\RemoveParentAndNameFromComponentConstructorRectorTest
*/
@ -60,12 +61,19 @@ final class RemoveParentAndNameFromComponentConstructorRector extends AbstractRe
*/
private $methodReflectionProvider;
/**
* @var ParamFinder
*/
private $paramFinder;
public function __construct(
ParamFinder $paramFinder,
StaticCallAnalyzer $staticCallAnalyzer,
MethodReflectionProvider $methodReflectionProvider
) {
$this->staticCallAnalyzer = $staticCallAnalyzer;
$this->methodReflectionProvider = $methodReflectionProvider;
$this->paramFinder = $paramFinder;
}
public function getRuleDefinition(): RuleDefinition
@ -126,7 +134,7 @@ CODE_SAMPLE
return $this->refactorStaticCall($node);
}
if ($node instanceof New_ && $this->isObjectType($node->class, self::COMPONENT_CONTAINER_CLASS)) {
if ($this->isObjectType($node->class, self::CONTROL_CLASS)) {
return $this->refactorNew($node);
}
@ -135,7 +143,7 @@ CODE_SAMPLE
private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod
{
if (! $this->isInObjectType($classMethod, self::CONTROL_CLASS)) {
if (! $this->isInsideNetteControlClass($classMethod)) {
return null;
}
@ -146,44 +154,36 @@ CODE_SAMPLE
return $this->removeClassMethodParams($classMethod);
}
private function removeClassMethodParams(ClassMethod $classMethod): ?ClassMethod
private function removeClassMethodParams(ClassMethod $classMethod): ClassMethod
{
$hasClassMethodChanged = false;
foreach ($classMethod->params as $param) {
if ($this->isInAssign($classMethod, $param)) {
if ($this->paramFinder->isInAssign((array) $classMethod->stmts, $param)) {
continue;
}
if ($this->isName($param, self::PARENT) && $param->type !== null && $this->isName(
$param->type,
self::COMPONENT_CONTAINER_CLASS
)) {
if ($this->isObjectType($param, self::COMPONENT_CONTAINER_CLASS)) {
$this->removeNode($param);
$hasClassMethodChanged = true;
continue;
}
if ($this->isName($param, self::NAME)) {
$this->removeNode($param);
$hasClassMethodChanged = true;
}
}
if (! $hasClassMethodChanged) {
return null;
}
return $classMethod;
}
private function refactorStaticCall(StaticCall $staticCall): ?StaticCall
{
if (! $this->isInsideNetteControlClass($staticCall)) {
return null;
}
if (! $this->staticCallAnalyzer->isParentCallNamed($staticCall, MethodName::CONSTRUCT)) {
return null;
}
$hasStaticCallChanged = false;
/** @var Arg $staticCallArg */
foreach ($staticCall->args as $staticCallArg) {
if (! $staticCallArg->value instanceof Variable) {
continue;
@ -196,11 +196,6 @@ CODE_SAMPLE
}
$this->removeNode($staticCallArg);
$hasStaticCallChanged = true;
}
if (! $hasStaticCallChanged) {
return null;
}
if ($this->shouldRemoveEmptyCall($staticCall)) {
@ -211,11 +206,10 @@ CODE_SAMPLE
return $staticCall;
}
private function refactorNew(New_ $new): ?New_
private function refactorNew(New_ $new): New_
{
$parameterNames = $this->methodReflectionProvider->provideParameterNamesByNew($new);
$hasNewChanged = false;
foreach ($new->args as $position => $arg) {
// is on position of $parent or $name?
if (! isset($parameterNames[$position])) {
@ -227,14 +221,9 @@ CODE_SAMPLE
continue;
}
$hasNewChanged = true;
$this->removeNode($arg);
}
if (! $hasNewChanged) {
return null;
}
return $new;
}
@ -251,18 +240,13 @@ CODE_SAMPLE
return true;
}
private function isInAssign(ClassMethod $classMethod, Param $param): bool
private function isInsideNetteControlClass(Node $node): bool
{
$variable = $param->var;
return (bool) $this->betterNodeFinder->find((array) $classMethod->stmts, function (Node $node) use (
$variable
): bool {
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $parent instanceof Assign) {
return false;
}
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return false;
}
return $this->areNodesEqual($node, $variable);
});
return $this->isObjectType($classLike, self::CONTROL_CLASS);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Nette\Tests\Rector\ClassMethod\RemoveParentAndNameFromComponentConstructorRector\Fixture;
use Nette\ComponentModel\IContainer;
use Nette\Forms\Form;
class SkipForm extends Form
{
public function __construct(IContainer $parent = null, $name = null, int $value)
{
parent::__construct($parent, $name);
}
}

View File

@ -6,9 +6,11 @@ namespace Rector\Symfony3\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Symfony3\FormHelper\FormTypeStringToTypeProvider;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -127,11 +129,16 @@ CODE_SAMPLE
private function isClassAndMethodMatch(ClassMethod $classMethod): bool
{
if ($this->isInObjectType($classMethod, 'Symfony\Component\Form\AbstractType')) {
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return false;
}
if ($this->isObjectType($classLike, 'Symfony\Component\Form\AbstractType')) {
return $this->isName($classMethod->name, 'getParent');
}
if ($this->isInObjectType($classMethod, 'Symfony\Component\Form\AbstractTypeExtension')) {
if ($this->isObjectType($classMethod, 'Symfony\Component\Form\AbstractTypeExtension')) {
return $this->isName($classMethod->name, 'getExtendedType');
}

View File

@ -7,6 +7,7 @@ namespace Rector\Symfony3\Rector\ClassMethod;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\Exception\ShouldNotHappenException;
@ -94,7 +95,12 @@ CODE_SAMPLE
private function isObjectMethodNameMatch(ClassMethod $classMethod): bool
{
if (! $this->isInObjectType($classMethod, 'Symfony\Component\Form\AbstractType')) {
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return false;
}
if (! $this->isObjectType($classMethod, 'Symfony\Component\Form\AbstractType')) {
return false;
}

View File

@ -70,18 +70,6 @@ trait NodeTypeResolverTrait
$this->typeUnwrapper = $typeUnwrapper;
}
public function isInObjectType(Node $node, string $type): bool
{
$objectType = $this->nodeTypeResolver->resolve($node);
$desiredObjectType = new ObjectType($type);
if ($objectType->isSuperTypeOf($desiredObjectType)->yes()) {
return true;
}
return $objectType->equals($desiredObjectType);
}
public function isPropertyBoolean(Property $property): bool
{
return $this->nodeTypeResolver->isPropertyBoolean($property);