fix return void (#4067)

This commit is contained in:
Tomas Votruba 2020-08-30 09:55:32 +02:00 committed by GitHub
parent 7312185dc3
commit 55c45b8c2b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 130 additions and 12 deletions

View File

@ -49,14 +49,11 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
$localReturnNodes = $this->collectReturns($functionLike);
if ($localReturnNodes === []) {
// void type
if (! $this->isAbstractMethod($classLike, $functionLike)) {
return new VoidType();
}
return new MixedType();
return $this->resolveNoLocalReturnNodes($classLike, $functionLike);
}
$hasSilentVoid = $this->hasSilentVoid($functionLike, $localReturnNodes);
foreach ($localReturnNodes as $localReturnNode) {
if ($localReturnNode->expr === null) {
$this->types[] = new VoidType();
@ -66,6 +63,10 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
$this->types[] = $this->nodeTypeResolver->getStaticType($localReturnNode->expr);
}
if ($hasSilentVoid) {
$this->types[] = new VoidType();
}
return $this->typeFactory->createMixedPassedOrUnionType($this->types);
}
@ -105,15 +106,36 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
return $returns;
}
private function isAbstractMethod(ClassLike $classLike, FunctionLike $functionLike): bool
private function resolveNoLocalReturnNodes(ClassLike $classLike, FunctionLike $functionLike): Type
{
// abstract class method
if ($functionLike instanceof ClassMethod && $functionLike->isAbstract()) {
return true;
// void type
if (! $this->isAbstractMethod($classLike, $functionLike)) {
return new VoidType();
}
// abstract class
return $classLike instanceof Class_ && $classLike->isAbstract();
return new MixedType();
}
/**
* @param ClassMethod|Closure|Function_ $functionLike
* @param Return_[] $localReturns
*/
private function hasSilentVoid(FunctionLike $functionLike, array $localReturns): bool
{
foreach ((array) $functionLike->stmts as $stmt) {
foreach ($localReturns as $localReturn) {
if ($localReturn === $stmt) {
return false;
}
}
// has switch with always return
if ($stmt instanceof Switch_ && $this->isSwitchWithAlwaysReturn($stmt)) {
return false;
}
}
return true;
}
private function processSwitch(Switch_ $switch): void
@ -126,4 +148,31 @@ final class ReturnedNodesReturnTypeInferer extends AbstractTypeInferer implement
$this->types[] = new VoidType();
}
private function isAbstractMethod(ClassLike $classLike, FunctionLike $functionLike): bool
{
// abstract class method
if ($functionLike instanceof ClassMethod && $functionLike->isAbstract()) {
return true;
}
// abstract class
return $classLike instanceof Class_ && $classLike->isAbstract();
}
private function isSwitchWithAlwaysReturn(Switch_ $switch): bool
{
$casesWithReturn = 0;
foreach ($switch->cases as $case) {
foreach ($case->stmts as $caseStmt) {
if ($caseStmt instanceof Return_) {
++$casesWithReturn;
break;
}
}
}
// has same amount of returns as switches
return count($switch->cases) === $casesWithReturn;
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
final class DoubleReturn
{
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return $this->getName();
}
return '';
}
private function getName(): string
{
return 'name';
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
final class DoubleReturn
{
protected function redirectTo($request): string
{
if (! $request->expectsJson()) {
return $this->getName();
}
return '';
}
private function getName(): string
{
return 'name';
}
}
?>

View File

@ -0,0 +1,24 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
final class SkipReturnVoid
{
/**
* @return string|void
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route2("test");
}
}
}
/**
* @param string $name
*/
function route($name, $parameters = [], $absolute = true): string
{
return $name;
}