Skip to content

Commit

Permalink
[TypeDeclaration] Remove parent lookup on ReturnedNodesReturnTypeInfe…
Browse files Browse the repository at this point in the history
…rerTypeInferer (#4246)

* [TypeDeclaration] Remove parent lookup on ReturnedNodesReturnTypeInfererTypeInferer

* [TypeDeclaration] Remove parent lookup on ReturnedNodesReturnTypeInfererTypeInferer

* fix

* fix

* fix

* stop traverser inside simpleCallableNodeTraverser when found
  • Loading branch information
samsonasik committed Jun 16, 2023
1 parent 06d1d08 commit 765450f
Showing 1 changed file with 34 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,19 @@
namespace Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;

use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Trait_;
use PhpParser\NodeTraverser;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\VoidType;
use Rector\Core\PhpParser\AstResolver;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
Expand All @@ -43,27 +40,25 @@ public function __construct(
private readonly AstResolver $reflectionAstResolver,
private readonly BetterStandardPrinter $betterStandardPrinter,
private readonly ReflectionResolver $reflectionResolver,
private readonly BetterNodeFinder $betterNodeFinder,
) {
}

public function inferFunctionLike(FunctionLike $functionLike): Type
{
$classLike = $this->betterNodeFinder->findParentType($functionLike, ClassLike::class);
if (! $classLike instanceof ClassLike) {
$classReflection = $this->reflectionResolver->resolveClassReflection($functionLike);
if (! $classReflection instanceof ClassReflection) {
return new MixedType();
}

if ($functionLike instanceof ClassMethod && $classLike instanceof Interface_) {
if ($functionLike instanceof ClassMethod && $classReflection->isInterface()) {
return new MixedType();
}

$types = [];

$localReturnNodes = $this->collectReturns($functionLike);
if ($localReturnNodes === []) {
/** @var Class_|Interface_|Trait_ $classLike */
return $this->resolveNoLocalReturnNodes($classLike, $functionLike);
return $this->resolveNoLocalReturnNodes($classReflection, $functionLike);
}

foreach ($localReturnNodes as $localReturnNode) {
Expand Down Expand Up @@ -108,28 +103,28 @@ private function collectReturns(FunctionLike $functionLike): array
}

private function resolveNoLocalReturnNodes(
Class_|Interface_|Trait_ $classLike,
ClassReflection $classReflection,
FunctionLike $functionLike
): VoidType | MixedType {
// void type
if (! $this->isAbstractMethod($classLike, $functionLike)) {
if (! $this->isAbstractMethod($classReflection, $functionLike)) {
return new VoidType();
}

return new MixedType();
}

private function isAbstractMethod(Class_|Interface_|Trait_ $classLike, FunctionLike $functionLike): bool
private function isAbstractMethod(ClassReflection $classReflection, FunctionLike $functionLike): bool
{
if ($functionLike instanceof ClassMethod && $functionLike->isAbstract()) {
return true;
}

if (! $classLike instanceof Class_) {
if (! $classReflection->isClass()) {
return false;
}

return $classLike->isAbstract();
return $classReflection->isAbstract();
}

private function inferFromReturnedMethodCall(Return_ $return, FunctionLike $originalFunctionLike): Type
Expand All @@ -143,8 +138,29 @@ private function inferFromReturnedMethodCall(Return_ $return, FunctionLike $orig
return new MixedType();
}

$parentClassMethod = $this->betterNodeFinder->findParentType($return, ClassMethod::class);
if ($parentClassMethod === $originalFunctionLike) {
$isReturnScoped = false;

$this->simpleCallableNodeTraverser->traverseNodesWithCallable(
(array) $originalFunctionLike->getStmts(),
static function (Node $subNode) use ($return, &$isReturnScoped): ?int {
if ($subNode instanceof FunctionLike && ! $subNode instanceof ArrowFunction) {
return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}

if (! $subNode instanceof Return_) {
return null;
}

if ($return === $subNode) {
$isReturnScoped = true;
return NodeTraverser::STOP_TRAVERSAL;
}

return null;
}
);

if ($isReturnScoped) {
return new MixedType();
}

Expand Down

0 comments on commit 765450f

Please sign in to comment.