2020-09-01 17:56:30 +00:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2022-06-06 17:12:56 +00:00
|
|
|
namespace Rector\TypeDeclaration\TypeInferer;
|
2020-09-01 17:56:30 +00:00
|
|
|
|
2022-06-06 17:12:56 +00:00
|
|
|
use PhpParser\Node\Expr\ArrowFunction;
|
|
|
|
use PhpParser\Node\Expr\Closure;
|
|
|
|
use PhpParser\Node\Expr\Yield_;
|
|
|
|
use PhpParser\Node\FunctionLike;
|
|
|
|
use PhpParser\Node\Stmt;
|
|
|
|
use PhpParser\Node\Stmt\ClassLike;
|
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
|
|
|
use PhpParser\Node\Stmt\Expression;
|
|
|
|
use PhpParser\Node\Stmt\Function_;
|
|
|
|
use PhpParser\Node\Stmt\Interface_;
|
|
|
|
use PhpParser\Node\Stmt\Return_;
|
|
|
|
use PhpParser\Node\Stmt\Switch_;
|
|
|
|
use PhpParser\Node\Stmt\Throw_;
|
|
|
|
use PhpParser\Node\Stmt\TryCatch;
|
|
|
|
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
2020-09-01 17:56:30 +00:00
|
|
|
final class SilentVoidResolver
|
|
|
|
{
|
2021-02-16 16:12:37 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
|
2021-02-16 16:12:37 +00:00
|
|
|
*/
|
|
|
|
private $betterNodeFinder;
|
2022-06-06 17:12:56 +00:00
|
|
|
public function __construct(\Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder)
|
2021-02-16 16:12:37 +00:00
|
|
|
{
|
|
|
|
$this->betterNodeFinder = $betterNodeFinder;
|
|
|
|
}
|
|
|
|
/**
|
2022-04-26 08:13:18 +00:00
|
|
|
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\Function_ $functionLike
|
2021-02-16 16:12:37 +00:00
|
|
|
*/
|
2021-06-07 23:40:38 +00:00
|
|
|
public function hasExclusiveVoid($functionLike) : bool
|
2021-02-16 16:12:37 +00:00
|
|
|
{
|
2022-06-06 17:12:56 +00:00
|
|
|
$classLike = $this->betterNodeFinder->findParentType($functionLike, \PhpParser\Node\Stmt\ClassLike::class);
|
|
|
|
if ($classLike instanceof \PhpParser\Node\Stmt\Interface_) {
|
2021-11-07 04:15:35 +00:00
|
|
|
return \false;
|
|
|
|
}
|
2021-04-20 19:50:10 +00:00
|
|
|
if ($this->hasNeverType($functionLike)) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2021-04-20 19:50:10 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($functionLike, \PhpParser\Node\Expr\Yield_::class)) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2021-02-16 16:12:37 +00:00
|
|
|
}
|
|
|
|
/** @var Return_[] $returns */
|
2022-06-06 17:12:56 +00:00
|
|
|
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, \PhpParser\Node\Stmt\Return_::class);
|
2021-02-16 16:12:37 +00:00
|
|
|
foreach ($returns as $return) {
|
|
|
|
if ($return->expr !== null) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2021-02-16 16:12:37 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2021-02-16 16:12:37 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
public function hasSilentVoid(\PhpParser\Node\FunctionLike $functionLike) : bool
|
2020-09-01 17:56:30 +00:00
|
|
|
{
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($functionLike instanceof \PhpParser\Node\Expr\ArrowFunction) {
|
2021-07-31 11:40:13 +00:00
|
|
|
return \false;
|
|
|
|
}
|
2021-11-28 16:42:02 +00:00
|
|
|
if ($this->hasStmtsAlwaysReturn((array) $functionLike->getStmts())) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2021-11-28 16:42:02 +00:00
|
|
|
foreach ((array) $functionLike->getStmts() as $stmt) {
|
2020-09-01 17:56:30 +00:00
|
|
|
// has switch with always return
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($stmt instanceof \PhpParser\Node\Stmt\Switch_ && $this->isSwitchWithAlwaysReturn($stmt)) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
// is part of try/catch
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($stmt instanceof \PhpParser\Node\Stmt\TryCatch && $this->isTryCatchAlwaysReturn($stmt)) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($stmt instanceof \PhpParser\Node\Stmt\Throw_) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param Stmt[]|Expression[] $stmts
|
|
|
|
*/
|
2021-05-09 20:15:43 +00:00
|
|
|
private function hasStmtsAlwaysReturn(array $stmts) : bool
|
2020-09-01 17:56:30 +00:00
|
|
|
{
|
|
|
|
foreach ($stmts as $stmt) {
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($stmt instanceof \PhpParser\Node\Stmt\Expression) {
|
2020-09-01 17:56:30 +00:00
|
|
|
$stmt = $stmt->expr;
|
|
|
|
}
|
|
|
|
// is 1st level return
|
2022-06-06 17:12:56 +00:00
|
|
|
if ($stmt instanceof \PhpParser\Node\Stmt\Return_) {
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
private function isSwitchWithAlwaysReturn(\PhpParser\Node\Stmt\Switch_ $switch) : bool
|
2020-09-01 17:56:30 +00:00
|
|
|
{
|
2021-05-09 20:15:43 +00:00
|
|
|
$hasDefault = \false;
|
2021-05-01 12:16:35 +00:00
|
|
|
foreach ($switch->cases as $case) {
|
|
|
|
if ($case->cond === null) {
|
2021-05-09 20:15:43 +00:00
|
|
|
$hasDefault = \true;
|
2021-05-01 12:33:28 +00:00
|
|
|
break;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
if (!$hasDefault) {
|
|
|
|
return \false;
|
2021-05-01 12:16:35 +00:00
|
|
|
}
|
|
|
|
$casesWithReturnCount = $this->resolveReturnCount($switch);
|
2020-09-01 17:56:30 +00:00
|
|
|
// has same amount of returns as switches
|
2021-05-09 20:15:43 +00:00
|
|
|
return \count($switch->cases) === $casesWithReturnCount;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
private function isTryCatchAlwaysReturn(\PhpParser\Node\Stmt\TryCatch $tryCatch) : bool
|
2020-09-01 17:56:30 +00:00
|
|
|
{
|
2021-05-09 20:15:43 +00:00
|
|
|
if (!$this->hasStmtsAlwaysReturn($tryCatch->stmts)) {
|
|
|
|
return \false;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
|
|
|
foreach ($tryCatch->catches as $catch) {
|
2020-12-25 00:22:45 +00:00
|
|
|
return $this->hasStmtsAlwaysReturn($catch->stmts);
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
return \true;
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|
2021-04-20 19:50:10 +00:00
|
|
|
/**
|
|
|
|
* @see https://phpstan.org/writing-php-code/phpdoc-types#bottom-type
|
2022-04-26 08:13:18 +00:00
|
|
|
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\Function_ $functionLike
|
2021-04-20 19:50:10 +00:00
|
|
|
*/
|
2021-06-29 14:24:45 +00:00
|
|
|
private function hasNeverType($functionLike) : bool
|
2021-04-20 19:50:10 +00:00
|
|
|
{
|
2022-06-06 17:12:56 +00:00
|
|
|
return $this->betterNodeFinder->hasInstancesOf($functionLike, [\PhpParser\Node\Stmt\Throw_::class]);
|
2021-04-20 19:50:10 +00:00
|
|
|
}
|
2022-06-06 17:12:56 +00:00
|
|
|
private function resolveReturnCount(\PhpParser\Node\Stmt\Switch_ $switch) : int
|
2021-05-01 12:16:35 +00:00
|
|
|
{
|
|
|
|
$casesWithReturnCount = 0;
|
|
|
|
foreach ($switch->cases as $case) {
|
|
|
|
foreach ($case->stmts as $caseStmt) {
|
2022-06-06 17:12:56 +00:00
|
|
|
if (!$caseStmt instanceof \PhpParser\Node\Stmt\Return_) {
|
2021-05-01 12:16:35 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++$casesWithReturnCount;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $casesWithReturnCount;
|
|
|
|
}
|
2020-09-01 17:56:30 +00:00
|
|
|
}
|