Skip to content

Commit

Permalink
[Core] Refactor AstResolver to locate node with SimpleCallableNodeTra…
Browse files Browse the repository at this point in the history
…verser (#3494)

* [Core] Refactor AstResolver to locate node with SimpleCallableNodeTraverser

* ensure nullify referenced $traitNode after assign to ensure it will re-fill

* next loop is always refill

* fix assign

* fix assign
  • Loading branch information
samsonasik committed Mar 20, 2023
1 parent 172d354 commit ea0c6c1
Showing 1 changed file with 116 additions and 73 deletions.
189 changes: 116 additions & 73 deletions src/PhpParser/AstResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Core\ValueObject\Application\File;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
use Rector\PhpDocParser\PhpParser\SmartPhpParser;

/**
Expand All @@ -54,7 +54,7 @@ final class AstResolver
public function __construct(
private readonly SmartPhpParser $smartPhpParser,
private readonly NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator,
private readonly BetterNodeFinder $betterNodeFinder,
private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
private readonly NodeNameResolver $nodeNameResolver,
private readonly ReflectionProvider $reflectionProvider,
private readonly ReflectionResolver $reflectionResolver,
Expand Down Expand Up @@ -92,27 +92,29 @@ public function resolveClassMethodFromMethodReflection(MethodReflection $methodR
return null;
}

/** @var ClassMethod|null $classMethod */
$classMethod = $this->betterNodeFinder->findFirst(
$classMethod = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$nodes,
function (Node $node) use ($classLikeName, $methodName): bool {
if (! $node instanceof ClassMethod) {
return false;
function (Node $node) use ($classLikeName, $methodName, &$classMethod): ?int {
if (! $node instanceof ClassLike) {
return null;
}

$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof ClassLike) {
return false;
if (! $this->nodeNameResolver->isName($node, $classLikeName)) {
return null;
}

if (! $this->nodeNameResolver->isName($parentNode, $classLikeName)) {
return false;
$method = $node->getMethod($methodName);
if ($method instanceof ClassMethod) {
$classMethod = $method;
return NodeTraverser::STOP_TRAVERSAL;
}

return $parentNode->getMethod($methodName) === $node;
return null;
}
);

/** @var ClassMethod|null $classMethod */
return $classMethod;
}

Expand Down Expand Up @@ -141,16 +143,25 @@ public function resolveFunctionFromFunctionReflection(FunctionReflection $functi
return null;
}

/** @var Function_|null $function */
$function = $this->betterNodeFinder->findFirst(
$functionNode = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$nodes,
fn (Node $node): bool => $node instanceof Function_ && $this->nodeNameResolver->isName(
$node,
$functionName
)
function (Node $node) use ($functionName, &$functionNode): ?int {
if (! $node instanceof Function_) {
return null;
}

if (! $this->nodeNameResolver->isName($node, $functionName)) {
return null;
}

$functionNode = $node;
return NodeTraverser::STOP_TRAVERSAL;
}
);

return $function;
/** @var Function_|null $functionNode */
return $functionNode;
}

/**
Expand All @@ -174,11 +185,9 @@ public function resolveClassMethod(string $className, string $methodName): ?Clas

public function resolveClassMethodFromCall(MethodCall | StaticCall $call): ?ClassMethod
{
if ($call instanceof MethodCall) {
$callerStaticType = $this->nodeTypeResolver->getType($call->var);
} else {
$callerStaticType = $this->nodeTypeResolver->getType($call->class);
}
$callerStaticType = $call instanceof MethodCall
? $this->nodeTypeResolver->getType($call->var)
: $this->nodeTypeResolver->getType($call->class);

if (! $callerStaticType instanceof TypeWithClassName) {
return null;
Expand Down Expand Up @@ -217,20 +226,30 @@ public function parseClassReflectionTraits(ClassReflection $classReflection): ar
continue;
}

/** @var Trait_|null $trait */
$trait = $this->betterNodeFinder->findFirst(
$traitName = $classLike->getName();

$traitNode = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$nodes,
fn (Node $node): bool => $node instanceof Trait_ && $this->nodeNameResolver->isName(
$node,
$classLike->getName()
)
function (Node $node) use ($traitName, &$traitNode): ?int {
if (! $node instanceof Trait_) {
return null;
}

if (! $this->nodeNameResolver->isName($node, $traitName)) {
return null;
}

$traitNode = $node;
return NodeTraverser::STOP_TRAVERSAL;
}
);

if (! $trait instanceof Trait_) {
if (! $traitNode instanceof Trait_) {
continue;
}

$traits[] = $trait;
$traits[] = $traitNode;
}

return $traits;
Expand All @@ -255,33 +274,35 @@ public function resolvePropertyFromPropertyReflection(
$desiredClassName = $classReflection->getName();
$desiredPropertyName = $nativeReflectionProperty->getName();

/** @var Property|null $property */
$property = $this->betterNodeFinder->findFirst(
/** @var Property|null $propertyNode */
$propertyNode = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$nodes,
function (Node $node) use ($desiredClassName, $desiredPropertyName): bool {
if (! $node instanceof Property) {
return false;
function (Node $node) use ($desiredClassName, $desiredPropertyName, &$propertyNode): ?int {
if (! $node instanceof ClassLike) {
return null;
}

$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof ClassLike) {
return false;
if (! $this->nodeNameResolver->isName($node, $desiredClassName)) {
return null;
}

if (! $this->nodeNameResolver->isName($parentNode, $desiredClassName)) {
return false;
$property = $node->getProperty($desiredPropertyName);
if ($property instanceof Property) {
$propertyNode = $property;
return NodeTraverser::STOP_TRAVERSAL;
}

return $parentNode->getProperty($desiredPropertyName) === $node;
return null;
}
);

if ($property instanceof Property) {
return $property;
if ($propertyNode instanceof Property) {
return $propertyNode;
}

// promoted property
return $this->findPromotedPropertyByName($nodes, $desiredPropertyName);
return $this->findPromotedPropertyByName($nodes, $desiredClassName, $desiredPropertyName);
}

private function locateClassMethodInTrait(string $methodName, MethodReflection $methodReflection): ?ClassMethod
Expand All @@ -290,16 +311,24 @@ private function locateClassMethodInTrait(string $methodName, MethodReflection $
$traits = $this->parseClassReflectionTraits($classReflection);

/** @var ClassMethod|null $classMethod */
$classMethod = $this->betterNodeFinder->findFirst(
$classMethod = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$traits,
fn (Node $node): bool => $node instanceof ClassMethod && $this->nodeNameResolver->isName($node, $methodName)
);
function (Node $node) use ($methodName, &$classMethod): ?int {
if (! $node instanceof ClassMethod) {
return null;
}

if ($classMethod instanceof ClassMethod) {
return $classMethod;
}
if (! $this->nodeNameResolver->isName($node, $methodName)) {
return null;
}

return null;
$classMethod = $node;
return NodeTraverser::STOP_TRAVERSAL;
}
);

return $classMethod;
}

/**
Expand All @@ -326,29 +355,43 @@ private function parseFileNameToDecoratedNodes(string $fileName): ?array
/**
* @param Stmt[] $stmts
*/
private function findPromotedPropertyByName(array $stmts, string $desiredPropertyName): ?Param
{
$class = $this->betterNodeFinder->findFirstInstanceOf($stmts, Class_::class);
if (! $class instanceof Class_) {
return null;
}
private function findPromotedPropertyByName(
array $stmts,
string $desiredClassName,
string $desiredPropertyName
): ?Param {
/** @var Param|null $paramNode */
$paramNode = null;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
$stmts,
function (Node $node) use ($desiredClassName, $desiredPropertyName, &$paramNode) {
if (! $node instanceof Class_) {
return null;
}

$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return null;
}
if (! $this->nodeNameResolver->isName($node, $desiredClassName)) {
return null;
}

foreach ($constructClassMethod->getParams() as $param) {
if ($param->flags === 0) {
continue;
}
$constructClassMethod = $node->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return null;
}

if ($this->nodeNameResolver->isName($param, $desiredPropertyName)) {
return $param;
foreach ($constructClassMethod->getParams() as $param) {
if ($param->flags === 0) {
continue;
}

if ($this->nodeNameResolver->isName($param, $desiredPropertyName)) {
$paramNode = $param;
return NodeTraverser::STOP_TRAVERSAL;
}
}
}
}
);

return null;
return $paramNode;
}

private function resolveFunctionFromFuncCall(FuncCall $funcCall, Scope $scope): ?Function_
Expand Down

0 comments on commit ea0c6c1

Please sign in to comment.