infer from @return doc type in PropertyTypeDeclaratoin

This commit is contained in:
Tomas Votruba 2019-08-01 10:04:49 +02:00
parent fcf3409584
commit 310ef2ae6e
7 changed files with 291 additions and 41 deletions

View File

@ -8,21 +8,20 @@ use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Return_;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PhpParser\Printer\BetterStandardPrinter;
use Rector\TypeDeclaration\Contract\PropertyTypeInfererInterface;
use Rector\TypeDeclaration\ReturnTypeResolver\ReturnTypeResolver;
final class GetterOrSetterPropertyTypeInferer extends AbstractPropertyTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var BetterStandardPrinter
* @var ReturnTypeResolver
*/
private $betterStandardPrinter;
private $returnTypeResolver;
public function __construct(BetterStandardPrinter $betterStandardPrinter)
public function __construct(ReturnTypeResolver $returnTypeResolver)
{
$this->betterStandardPrinter = $betterStandardPrinter;
$this->returnTypeResolver = $returnTypeResolver;
}
/**
@ -40,16 +39,11 @@ final class GetterOrSetterPropertyTypeInferer extends AbstractPropertyTypeInfere
if (! $this->hasClassMethodOnlyStatementReturnOfPropertyFetch($classMethod, $propertyName)) {
continue;
}
$returnTypes = $this->resolveClassMethodReturnTypes($classMethod);
if ($returnTypes !== []) {
return $returnTypes;
}
throw new ShouldNotHappenException(sprintf(
'"%s" for "%s" type',
__METHOD__,
$this->betterStandardPrinter->print($classMethod->returnType)
));
}
return [];
@ -88,9 +82,11 @@ final class GetterOrSetterPropertyTypeInferer extends AbstractPropertyTypeInfere
*/
private function resolveClassMethodReturnTypes(ClassMethod $classMethod): array
{
// @todo resolve from doc?
// resolve from doc
if (! $classMethod->returnType) {
return [];
$returnType = $this->returnTypeResolver->resolveFunctionLikeReturnType($classMethod);
return $returnType->getDocTypes();
}
if ($classMethod->returnType instanceof NullableType) {

View File

@ -49,11 +49,14 @@ abstract class AbstractTypeDeclarationRector extends AbstractRector
*/
protected $functionLikeManipulator;
public function __construct(
/**
* @required
*/
public function autowireAbstractTypeDeclarationRector(
DocBlockManipulator $docBlockManipulator,
ParsedNodesByType $parsedNodesByType,
FunctionLikeManipulator $functionLikeManipulator
) {
): void {
$this->docBlockManipulator = $docBlockManipulator;
$this->parsedNodesByType = $parsedNodesByType;
$this->functionLikeManipulator = $functionLikeManipulator;

View File

@ -3,7 +3,6 @@
namespace Rector\TypeDeclaration\Rector\FunctionLike;
use PhpParser\Node;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
@ -13,6 +12,7 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Rector\TypeDeclaration\ReturnTypeResolver\ReturnTypeResolver;
final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
{
@ -21,6 +21,16 @@ final class ReturnTypeDeclarationRector extends AbstractTypeDeclarationRector
*/
private const EXCLUDED_METHOD_NAMES = ['__construct', '__destruct', '__clone'];
/**
* @var ReturnTypeResolver
*/
private $returnTypeResolver;
public function __construct(ReturnTypeResolver $returnTypeResolver)
{
$this->returnTypeResolver = $returnTypeResolver;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
@ -82,7 +92,7 @@ CODE_SAMPLE
}
}
$returnTypeInfo = $this->resolveReturnType($node);
$returnTypeInfo = $this->returnTypeResolver->resolveFunctionLikeReturnType($node);
if ($returnTypeInfo === null) {
return null;
}
@ -125,26 +135,6 @@ CODE_SAMPLE
return $node;
}
/**
* @param ClassMethod|Function_ $functionLike
*/
private function resolveReturnType(FunctionLike $functionLike): ?ReturnTypeInfo
{
$docReturnTypeInfo = $this->docBlockManipulator->getReturnTypeInfo($functionLike);
$codeReturnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($functionLike);
// code has priority over docblock
if ($docReturnTypeInfo === null) {
return $codeReturnTypeInfo;
}
if ($codeReturnTypeInfo && $codeReturnTypeInfo->getTypeNode()) {
return $codeReturnTypeInfo;
}
return $docReturnTypeInfo;
}
/**
* Add typehint to all children
*/

View File

@ -0,0 +1,51 @@
<?php declare(strict_types=1);
namespace Rector\TypeDeclaration\ReturnTypeResolver;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\NodeTypeResolver\Php\ReturnTypeInfo;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Manipulator\FunctionLikeManipulator;
final class ReturnTypeResolver
{
/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;
/**
* @var FunctionLikeManipulator
*/
private $functionLikeManipulator;
public function __construct(
DocBlockManipulator $docBlockManipulator,
FunctionLikeManipulator $functionLikeManipulator
) {
$this->docBlockManipulator = $docBlockManipulator;
$this->functionLikeManipulator = $functionLikeManipulator;
}
/**
* @param ClassMethod|Function_ $functionLike
*/
public function resolveFunctionLikeReturnType(FunctionLike $functionLike): ?ReturnTypeInfo
{
$docReturnTypeInfo = $this->docBlockManipulator->getReturnTypeInfo($functionLike);
$codeReturnTypeInfo = $this->functionLikeManipulator->resolveStaticReturnTypeInfo($functionLike);
// code has priority over docblock
if ($docReturnTypeInfo === null) {
return $codeReturnTypeInfo;
}
if ($codeReturnTypeInfo && $codeReturnTypeInfo->getTypeNode()) {
return $codeReturnTypeInfo;
}
return $docReturnTypeInfo;
}
}

View File

@ -0,0 +1,151 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\PropertyTypeDeclarationRector\Fixture;
use JMS\Serializer\Annotation as Serializer;
final class GetterType
{
/**
* @Serializer\Type("string")
*/
private $email;
/**
* @Serializer\Type("string")
*/
private $password;
/**
* @Serializer\Type("string")
*/
private $language;
private $surname;
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function hasLanguage(): bool
{
return $this->language !== null;
}
public function getLanguage()
{
return $this->language;
}
public function setLanguage(string $language): void
{
$this->language = $language;
}
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* @return string
*/
public function getSurname()
{
return $this->surname;
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\PropertyTypeDeclarationRector\Fixture;
use JMS\Serializer\Annotation as Serializer;
final class GetterType
{
/**
* @Serializer\Type("string")
* @var string
*/
private $email;
/**
* @Serializer\Type("string")
* @var string
*/
private $password;
/**
* @Serializer\Type("string")
* @var string
*/
private $language;
/**
* @var string
*/
private $surname;
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function hasLanguage(): bool
{
return $this->language !== null;
}
public function getLanguage()
{
return $this->language;
}
public function setLanguage(string $language): void
{
$this->language = $language;
}
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* @return string
*/
public function getSurname()
{
return $this->surname;
}
}
?>

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\PropertyTypeDeclarationRector\Fixture;
final class SetterType
{
private $email;
private $name;
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* @param string $name
*/
public function setName($name)
{
return $this->name = $name;
}
}
?>
-----
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\PropertyTypeDeclarationRector\Fixture;
final class SetterType
{
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $name;
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* @param string $name
*/
public function setName($name)
{
return $this->name = $name;
}
}
?>

View File

@ -15,13 +15,13 @@ final class PropertyTypeDeclarationRectorTest extends AbstractRectorTestCase
__DIR__ . '/Fixture/constructor_assign.php.inc',
__DIR__ . '/Fixture/phpunit_setup.php.inc',
__DIR__ . '/Fixture/default_value.php.inc',
__DIR__ . '/Fixture/doctrine_column.php.inc',
__DIR__ . '/Fixture/doctrine_relation.php.inc',
// get and set
__DIR__ . '/Fixture/complex.php.inc',
__DIR__ . '/Fixture/single_nullable_return.php.inc',
__DIR__ . '/Fixture/getter_type.php.inc',
__DIR__ . '/Fixture/setter_type.php.inc',
// skip
__DIR__ . '/Fixture/skip_multi_vars.php.inc',
]);