Skip to content

Commit

Permalink
[DX] Add ExprScopeFromStmtNodeVisitor, move logic deep Expr from Scop…
Browse files Browse the repository at this point in the history
…eAnalyzer to it (#4815)

Co-authored-by: GitHub Action <actions@github.com>
  • Loading branch information
samsonasik and actions-user committed Aug 19, 2023
1 parent 0477f5b commit 87c677e
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Core\PHPStan\NodeVisitor\ExprScopeFromStmtNodeVisitor;
use Rector\Core\PHPStan\NodeVisitor\WrappedNodeRestoringNodeVisitor;
use Rector\Core\Util\Reflection\PrivatesAccessor;
use Rector\NodeNameResolver\NodeNameResolver;
Expand Down Expand Up @@ -196,6 +197,7 @@ public function processNodes(

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new WrappedNodeRestoringNodeVisitor());
$nodeTraverser->addVisitor(new ExprScopeFromStmtNodeVisitor($this->scopeFactory, $filePath));
$nodeTraverser->traverse($stmts);

return $stmts;
Expand Down
5 changes: 2 additions & 3 deletions src/Application/ChangedNodeScopeRefresher.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ public function __construct(
public function refresh(
Node $node,
?MutatingScope $mutatingScope,
?string $filePath = null,
?Stmt $currentStmt = null
?string $filePath = null
): void {
// nothing to refresh
if (! $this->scopeAnalyzer->isRefreshable($node)) {
Expand All @@ -62,7 +61,7 @@ public function refresh(

$mutatingScope = $mutatingScope instanceof MutatingScope
? $mutatingScope
: $this->scopeAnalyzer->resolveScope($node, $filePath, $currentStmt);
: $this->scopeAnalyzer->resolveScope($node, $filePath);

if (! $mutatingScope instanceof MutatingScope) {
$errorMessage = sprintf('Node "%s" with is missing scope required for scope refresh', $node::class);
Expand Down
12 changes: 1 addition & 11 deletions src/NodeAnalyzer/ScopeAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
Expand Down Expand Up @@ -39,22 +38,13 @@ public function isRefreshable(Node $node): bool
return true;
}

public function resolveScope(Node $node, string $filePath, ?Stmt $currentStmt = null): ?Scope
public function resolveScope(Node $node, string $filePath): ?Scope
{
// on File level
if ($node instanceof Stmt && $node->getAttribute(AttributeKey::STATEMENT_DEPTH) === 0) {
return $this->scopeFactory->createFromFile($filePath);
}

// too deep Expr, eg: $$param = $$bar = self::decodeValue($result->getItem()->getTextContent());
if ($node instanceof Expr && $node->getAttribute(AttributeKey::EXPRESSION_DEPTH) >= 2) {
$scope = $currentStmt instanceof Stmt
? $currentStmt->getAttribute(AttributeKey::SCOPE)
: $this->scopeFactory->createFromFile($filePath);

return $scope instanceof Scope ? $scope : $this->scopeFactory->createFromFile($filePath);
}

/**
* Node and parent Node doesn't has Scope, and Node Start token pos is < 0,
* it means the node and parent node just re-printed, the Scope need to be resolved from file
Expand Down
53 changes: 53 additions & 0 deletions src/PHPStan/NodeVisitor/ExprScopeFromStmtNodeVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Rector\Core\PHPStan\NodeVisitor;

use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Analyser\Scope;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Scope\ScopeFactory;

final class ExprScopeFromStmtNodeVisitor extends NodeVisitorAbstract
{
private ?Stmt $currentStmt = null;

public function __construct(
private readonly ScopeFactory $scopeFactory,
private string $filePath
) {
}

public function enterNode(Node $node): ?Node
{
if ($node instanceof Stmt) {
$this->currentStmt = $node;
return null;
}

if (! $node instanceof Expr || $node->getAttribute(AttributeKey::EXPRESSION_DEPTH) < 2) {
return null;
}

$scope = $node->getAttribute(AttributeKey::SCOPE);
if ($scope instanceof Scope) {
return null;
}

// too deep Expr, eg: $$param = $$bar = self::decodeValue($result->getItem()->getTextContent());
$filePath = $this->filePath;
$scope = $this->currentStmt instanceof Stmt
? $this->currentStmt->getAttribute(AttributeKey::SCOPE)
: $this->scopeFactory->createFromFile($filePath);

$scope = $scope instanceof Scope ? $scope : $this->scopeFactory->createFromFile($filePath);

$node->setAttribute(AttributeKey::SCOPE, $scope);

return null;
}
}
15 changes: 3 additions & 12 deletions src/Rector/AbstractRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace Rector\Core\Rector;

use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\InlineHTML;
use PhpParser\Node\Stmt\Nop;
use PhpParser\NodeTraverser;
Expand Down Expand Up @@ -70,8 +69,6 @@ abstract class AbstractRector extends NodeVisitorAbstract implements RectorInter

protected File $file;

protected ?Stmt $currentStmt = null;

private ChangedNodeScopeRefresher $changedNodeScopeRefresher;

private SimpleCallableNodeTraverser $simpleCallableNodeTraverser;
Expand Down Expand Up @@ -337,23 +334,17 @@ private function refreshScopeNodes(array | Node $node, string $filePath, ?Mutati
$nodes = $node instanceof Node ? [$node] : $node;

foreach ($nodes as $node) {
$this->changedNodeScopeRefresher->refresh($node, $mutatingScope, $filePath, $this->currentStmt);
$this->changedNodeScopeRefresher->refresh($node, $mutatingScope, $filePath);
}
}

private function isMatchingNodeType(Node $node): bool
{
$nodeClass = $node::class;
foreach ($this->getNodeTypes() as $nodeType) {
if (! is_a($nodeClass, $nodeType, true)) {
if ($node instanceof Stmt) {
$this->currentStmt = $node;
}

continue;
if (is_a($nodeClass, $nodeType, true)) {
return true;
}

return true;
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/Rector/AbstractScopeAwareRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function refactor(Node $node): int|array|Node|null
$currentScope = $node->getAttribute(AttributeKey::SCOPE);

if (! $currentScope instanceof MutatingScope) {
$currentScope = $this->scopeAnalyzer->resolveScope($node, $this->file->getFilePath(), $this->currentStmt);
$currentScope = $this->scopeAnalyzer->resolveScope($node, $this->file->getFilePath());
}

if (! $currentScope instanceof Scope) {
Expand Down

0 comments on commit 87c677e

Please sign in to comment.