[Core] Apply CREATED_BY_RULE attribute as array collection of applied Rector rule (#1600)

* [Core] Apply CREATED_BY_RULE attribute as array collection of applied Rector rule

* phpstan

* early return

* [ci-review] Rector Rectify

* update comment

* comment

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2021-12-31 17:23:19 +07:00 committed by GitHub
parent 311ffc6ec3
commit 4399f28ee1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 40 deletions

View File

@ -40,7 +40,7 @@ final class ExprUsedInNextNodeAnalyzer
private function hasIfChangedByRemoveAlwaysElseRector(If_ $if): bool
{
$createdByRule = $if->getAttribute(AttributeKey::CREATED_BY_RULE);
return $createdByRule === RemoveAlwaysElseRector::class;
$createdByRule = $if->getAttribute(AttributeKey::CREATED_BY_RULE) ?? [];
return in_array(RemoveAlwaysElseRector::class, $createdByRule, true);
}
}

View File

@ -96,8 +96,8 @@ CODE_SAMPLE
return null;
}
$createdByRule = $node->getAttribute(AttributeKey::CREATED_BY_RULE);
if ($createdByRule === self::class) {
$createdByRule = $node->getAttribute(AttributeKey::CREATED_BY_RULE) ?? [];
if (in_array(self::class, $createdByRule, true)) {
return null;
}
@ -106,7 +106,6 @@ CODE_SAMPLE
$args = [$args[0]];
$node->args = $args;
$node->setAttribute(AttributeKey::CREATED_BY_RULE, self::class);
return $node;
}
@ -117,7 +116,6 @@ CODE_SAMPLE
$node->args[1] = new Arg($this->createNewArgFirstTernary($args));
$node->args[2] = new Arg($this->createNewArgSecondTernary($args));
$node->setAttribute(AttributeKey::CREATED_BY_RULE, self::class);
return $node;
}

View File

@ -14,7 +14,6 @@ use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Throw_;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -82,7 +81,6 @@ CODE_SAMPLE
$originalNode = clone $node;
$if = new If_($node->cond);
$if->stmts = $node->stmts;
$node->setAttribute(AttributeKey::CREATED_BY_RULE, self::class);
$this->nodesToAddCollector->addNodeBeforeNode($if, $node);
$this->mirrorComments($if, $node);
@ -110,7 +108,6 @@ CODE_SAMPLE
$this->nodesToAddCollector->addNodesAfterNode($node->else->stmts, $node);
$node->else = null;
$node->setAttribute(AttributeKey::CREATED_BY_RULE, self::class);
return $node;
}

View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Rector\Core\NodeDecorator;
use PhpParser\Node;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class CreatedByRuleDecorator
{
public function decorate(Node $node, string $rectorClass): void
{
$mergeCreatedByRule = array_merge($node->getAttribute(AttributeKey::CREATED_BY_RULE) ?? [], [$rectorClass]);
$mergeCreatedByRule = array_unique($mergeCreatedByRule);
$node->setAttribute(AttributeKey::CREATED_BY_RULE, $mergeCreatedByRule);
}
}

View File

@ -6,18 +6,19 @@ namespace Rector\Core\PhpParser\NodeVisitor;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Core\NodeDecorator\CreatedByRuleDecorator;
final class CreatedByRuleNodeVisitor extends NodeVisitorAbstract
{
public function __construct(
private readonly CreatedByRuleDecorator $createdByRuleDecorator,
private readonly string $rectorClass
) {
}
public function enterNode(Node $node)
{
$node->setAttribute(AttributeKey::CREATED_BY_RULE, $this->rectorClass);
$this->createdByRuleDecorator->decorate($node, $this->rectorClass);
return $node;
}
}

View File

@ -27,6 +27,7 @@ use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Exclusion\ExclusionManager;
use Rector\Core\Logging\CurrentRectorProvider;
use Rector\Core\NodeAnalyzer\ChangedNodeAnalyzer;
use Rector\Core\NodeDecorator\CreatedByRuleDecorator;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
@ -127,6 +128,8 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
private RectifiedAnalyzer $rectifiedAnalyzer;
private CreatedByRuleDecorator $createdByRuleDecorator;
#[Required]
public function autowire(
NodesToRemoveCollector $nodesToRemoveCollector,
@ -153,7 +156,8 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
CurrentFileProvider $currentFileProvider,
ChangedNodeAnalyzer $changedNodeAnalyzer,
InfiniteLoopValidator $infiniteLoopValidator,
RectifiedAnalyzer $rectifiedAnalyzer
RectifiedAnalyzer $rectifiedAnalyzer,
CreatedByRuleDecorator $createdByRuleDecorator
): void {
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->nodesToAddCollector = $nodesToAddCollector;
@ -180,6 +184,7 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$this->changedNodeAnalyzer = $changedNodeAnalyzer;
$this->infiniteLoopValidator = $infiniteLoopValidator;
$this->rectifiedAnalyzer = $rectifiedAnalyzer;
$this->createdByRuleDecorator = $createdByRuleDecorator;
}
/**
@ -228,7 +233,14 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
$node = $this->refactor($node);
// nothing to change → continue
if ($node === null) {
return null;
}
if (is_array($node)) {
$this->createdByRuleDecorator->decorate($originalNode, static::class);
$originalNodeHash = spl_object_hash($originalNode);
$this->nodesToReturn[$originalNodeHash] = $node;
@ -241,37 +253,37 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
return $originalNode;
}
// nothing to change → continue
if (! $node instanceof Node) {
return null;
// not changed, return node early
if (! $this->changedNodeAnalyzer->hasNodeChanged($originalNode, $node)) {
return $node;
}
// changed!
if ($this->changedNodeAnalyzer->hasNodeChanged($originalNode, $node)) {
$rectorWithLineChange = new RectorWithLineChange($this::class, $originalNode->getLine());
$this->file->addRectorClassWithLine($rectorWithLineChange);
$this->createdByRuleDecorator->decorate($node, static::class);
// update parents relations - must run before connectParentNodes()
$this->mirrorAttributes($originalAttributes, $node);
$this->connectParentNodes($node);
$rectorWithLineChange = new RectorWithLineChange($this::class, $originalNode->getLine());
$this->file->addRectorClassWithLine($rectorWithLineChange);
// is different node type? do not traverse children to avoid looping
if ($originalNode::class !== $node::class) {
$this->infiniteLoopValidator->process($node, $originalNode, static::class);
// update parents relations - must run before connectParentNodes()
$this->mirrorAttributes($originalAttributes, $node);
$this->connectParentNodes($node);
// search "infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown
$originalNodeHash = spl_object_hash($originalNode);
if ($originalNode instanceof Stmt && $node instanceof Expr) {
$node = new Expression($node);
}
$this->nodesToReturn[$originalNodeHash] = $node;
return $node;
}
// is equals node type? return node early
if ($originalNode::class === $node::class) {
return $node;
}
// is different node type? do not traverse children to avoid looping
$this->infiniteLoopValidator->process($node, $originalNode, static::class);
// search "infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown
$originalNodeHash = spl_object_hash($originalNode);
if ($originalNode instanceof Stmt && $node instanceof Expr) {
$node = new Expression($node);
}
$this->nodesToReturn[$originalNodeHash] = $node;
return $node;
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node;
use PhpParser\NodeTraverser;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\Exception\NodeTraverser\InfiniteLoopTraversingException;
use Rector\Core\NodeDecorator\CreatedByRuleDecorator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\NodeVisitor\CreatedByRuleNodeVisitor;
use Rector\DowngradePhp74\Rector\ArrowFunction\ArrowFunctionToAnonymousFunctionRector;
@ -27,7 +28,8 @@ final class InfiniteLoopValidator
];
public function __construct(
private readonly BetterNodeFinder $betterNodeFinder
private readonly BetterNodeFinder $betterNodeFinder,
private readonly CreatedByRuleDecorator $createdByRuleDecorator
) {
}
@ -40,10 +42,10 @@ final class InfiniteLoopValidator
return;
}
$createdByRule = $originalNode->getAttribute(AttributeKey::CREATED_BY_RULE);
$createdByRule = $originalNode->getAttribute(AttributeKey::CREATED_BY_RULE) ?? [];
// special case
if ($createdByRule === $rectorClass) {
if (in_array($rectorClass, $createdByRule, true)) {
// does it contain the same node type as input?
$originalNodeClass = $originalNode::class;
@ -63,7 +65,7 @@ final class InfiniteLoopValidator
{
$nodeTraverser = new NodeTraverser();
$createdByRuleNodeVisitor = new CreatedByRuleNodeVisitor($rectorClass);
$createdByRuleNodeVisitor = new CreatedByRuleNodeVisitor($this->createdByRuleDecorator, $rectorClass);
$nodeTraverser->addVisitor($createdByRuleNodeVisitor);
$nodeTraverser->traverse([$node]);