mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-02 09:20:52 +00:00
[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:
parent
311ffc6ec3
commit
4399f28ee1
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
19
src/NodeDecorator/CreatedByRuleDecorator.php
Normal file
19
src/NodeDecorator/CreatedByRuleDecorator.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
Loading…
Reference in New Issue
Block a user