Updated Rector to commit e8e2db769d857346202bc0f59ab4a618ee5b5202

e8e2db769d [TypeDeclaration] Add assigned new to variable in ReturnTypeFromReturnNewRector (#2647)
This commit is contained in:
Tomas Votruba 2022-07-09 14:55:41 +00:00
parent 8b3c974a42
commit 42d59f4003
7 changed files with 207 additions and 36 deletions

View File

@ -0,0 +1,129 @@
<?php
declare (strict_types=1);
namespace Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeNameResolver\NodeNameResolver;
final class StrictReturnNewAnalyzer
{
/**
* @readonly
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(BetterNodeFinder $betterNodeFinder, NodeNameResolver $nodeNameResolver)
{
$this->betterNodeFinder = $betterNodeFinder;
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\Function_ $functionLike
*/
public function matchAlwaysReturnVariableNew($functionLike) : ?string
{
if ($functionLike->stmts === null) {
return null;
}
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($functionLike, [Yield_::class])) {
return null;
}
/** @var Return_[] $returns */
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
if ($returns === []) {
return null;
}
// is one statement depth 3?
if (!$this->areExclusiveExprReturns($returns)) {
return null;
}
// has root return?
if (!$this->hasClassMethodRootReturn($functionLike)) {
return null;
}
if (\count($returns) !== 1) {
return null;
}
// exact one return of variable
$onlyReturn = $returns[0];
if (!$onlyReturn->expr instanceof Variable) {
return null;
}
$createdVariablesToTypes = $this->resolveCreatedVariablesToTypes($functionLike);
$returnedVariableName = $this->nodeNameResolver->getName($onlyReturn->expr);
return $createdVariablesToTypes[$returnedVariableName] ?? null;
}
/**
* @param Return_[] $returns
*/
private function areExclusiveExprReturns(array $returns) : bool
{
foreach ($returns as $return) {
if (!$return->expr instanceof Expr) {
return \false;
}
}
return \true;
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $functionLike
*/
private function hasClassMethodRootReturn($functionLike) : bool
{
foreach ((array) $functionLike->stmts as $stmt) {
if ($stmt instanceof Return_) {
return \true;
}
}
return \false;
}
/**
* @return array<string, string>
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $functionLike
*/
private function resolveCreatedVariablesToTypes($functionLike) : array
{
$createdVariablesToTypes = [];
// what new is assigned to it?
foreach ((array) $functionLike->stmts as $stmt) {
if (!$stmt instanceof Expression) {
continue;
}
if (!$stmt->expr instanceof Assign) {
continue;
}
$assign = $stmt->expr;
if (!$assign->expr instanceof New_) {
continue;
}
if (!$assign->var instanceof Variable) {
continue;
}
$variableName = $this->nodeNameResolver->getName($assign->var);
if (!\is_string($variableName)) {
continue;
}
$className = $this->nodeNameResolver->getName($assign->expr->class);
if (!\is_string($className)) {
continue;
}
$createdVariablesToTypes[$variableName] = $className;
}
return $createdVariablesToTypes;
}
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
@ -15,6 +16,7 @@ use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StaticType;
use PHPStan\Type\Type;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
@ -23,6 +25,7 @@ use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\StaticTypeMapper\ValueObject\Type\SelfStaticType;
use Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictReturnNewAnalyzer;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -46,11 +49,17 @@ final class ReturnTypeFromReturnNewRector extends AbstractRector implements MinP
* @var \Rector\Core\Reflection\ReflectionResolver
*/
private $reflectionResolver;
public function __construct(TypeFactory $typeFactory, ReflectionProvider $reflectionProvider, ReflectionResolver $reflectionResolver)
/**
* @readonly
* @var \Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictReturnNewAnalyzer
*/
private $strictReturnNewAnalyzer;
public function __construct(TypeFactory $typeFactory, ReflectionProvider $reflectionProvider, ReflectionResolver $reflectionResolver, StrictReturnNewAnalyzer $strictReturnNewAnalyzer)
{
$this->typeFactory = $typeFactory;
$this->reflectionProvider = $reflectionProvider;
$this->reflectionResolver = $reflectionResolver;
$this->strictReturnNewAnalyzer = $strictReturnNewAnalyzer;
}
public function getRuleDefinition() : RuleDefinition
{
@ -89,29 +98,14 @@ CODE_SAMPLE
if ($node->returnType !== null) {
return null;
}
if ($node instanceof ArrowFunction) {
$returns = [new Return_($node->expr)];
} else {
/** @var Return_[] $returns */
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($node, Return_::class);
}
if ($returns === []) {
return null;
}
$newTypes = [];
foreach ($returns as $return) {
if (!$return->expr instanceof New_) {
return null;
if (!$node instanceof ArrowFunction) {
$returnedNewClassName = $this->strictReturnNewAnalyzer->matchAlwaysReturnVariableNew($node);
if (\is_string($returnedNewClassName)) {
$node->returnType = new FullyQualified($returnedNewClassName);
return $node;
}
$new = $return->expr;
if (!$new->class instanceof Name) {
return null;
}
$newTypes[] = $this->createObjectTypeFromNew($new);
}
$returnType = $this->typeFactory->createMixedPassedOrUnionType($newTypes);
$node->returnType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN);
return $node;
return $this->refactorDirectReturnNew($node);
}
public function provideMinPhpVersion() : int
{
@ -139,4 +133,50 @@ CODE_SAMPLE
$classReflection = $this->reflectionProvider->getClass($className);
return new ObjectType($className, null, $classReflection);
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\ArrowFunction|\PhpParser\Node\Expr\Closure $node
* @return null|\PhpParser\Node\Expr\ArrowFunction|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure
*/
private function refactorDirectReturnNew($node)
{
if ($node instanceof ArrowFunction) {
$returns = [new Return_($node->expr)];
} else {
/** @var Return_[] $returns */
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($node, Return_::class);
}
if ($returns === []) {
return null;
}
$newTypes = $this->resolveReturnNewType($returns);
if ($newTypes === null) {
return null;
}
$returnType = $this->typeFactory->createMixedPassedOrUnionType($newTypes);
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType, TypeKind::RETURN);
if (!$returnTypeNode instanceof Node) {
return null;
}
$node->returnType = $returnTypeNode;
return $node;
}
/**
* @param Return_[] $returns
* @return Type[]|null
*/
private function resolveReturnNewType(array $returns) : ?array
{
$newTypes = [];
foreach ($returns as $return) {
if (!$return->expr instanceof New_) {
return null;
}
$new = $return->expr;
if (!$new->class instanceof Name) {
return null;
}
$newTypes[] = $this->createObjectTypeFromNew($new);
}
return $newTypes;
}
}

View File

@ -17,12 +17,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = 'fba410bf6ed6da0657cb8ec939a22ad3988fc4ad';
public const PACKAGE_VERSION = 'e8e2db769d857346202bc0f59ab4a618ee5b5202';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2022-07-09 14:09:38';
public const RELEASE_DATE = '2022-07-09 14:50:32';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f::getLoader();
return ComposerAutoloaderInitf833c8a4a67a7cd48d0e4f56703bd04d::getLoader();

View File

@ -2996,6 +2996,7 @@ return array(
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\AlwaysStrictReturnAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/AlwaysStrictReturnAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictBoolReturnTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictBoolReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictNativeFunctionReturnTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictNativeFunctionReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictReturnNewAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictReturnNewAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictScalarReturnTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\TypeNodeUnwrapper' => $baseDir . '/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php',
'Rector\\TypeDeclaration\\NodeTypeAnalyzer\\CallTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/NodeTypeAnalyzer/CallTypeAnalyzer.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f
class ComposerAutoloaderInitf833c8a4a67a7cd48d0e4f56703bd04d
{
private static $loader;
@ -22,19 +22,19 @@ class ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitf833c8a4a67a7cd48d0e4f56703bd04d', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitf833c8a4a67a7cd48d0e4f56703bd04d', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit45dd80ba9097691ea865802d8b675d2f::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit45dd80ba9097691ea865802d8b675d2f::$files;
$includeFiles = \Composer\Autoload\ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire45dd80ba9097691ea865802d8b675d2f($fileIdentifier, $file);
composerRequiref833c8a4a67a7cd48d0e4f56703bd04d($fileIdentifier, $file);
}
return $loader;
@ -46,7 +46,7 @@ class ComposerAutoloaderInit45dd80ba9097691ea865802d8b675d2f
* @param string $file
* @return void
*/
function composerRequire45dd80ba9097691ea865802d8b675d2f($fileIdentifier, $file)
function composerRequiref833c8a4a67a7cd48d0e4f56703bd04d($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInit45dd80ba9097691ea865802d8b675d2f
class ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d
{
public static $files = array (
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
@ -3303,6 +3303,7 @@ class ComposerStaticInit45dd80ba9097691ea865802d8b675d2f
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\AlwaysStrictReturnAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/AlwaysStrictReturnAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictBoolReturnTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictBoolReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictNativeFunctionReturnTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictNativeFunctionReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictReturnNewAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictReturnNewAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\ReturnTypeAnalyzer\\StrictScalarReturnTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\NodeAnalyzer\\TypeNodeUnwrapper' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeAnalyzer/TypeNodeUnwrapper.php',
'Rector\\TypeDeclaration\\NodeTypeAnalyzer\\CallTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/NodeTypeAnalyzer/CallTypeAnalyzer.php',
@ -3412,9 +3413,9 @@ class ComposerStaticInit45dd80ba9097691ea865802d8b675d2f
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit45dd80ba9097691ea865802d8b675d2f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit45dd80ba9097691ea865802d8b675d2f::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit45dd80ba9097691ea865802d8b675d2f::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitf833c8a4a67a7cd48d0e4f56703bd04d::$classMap;
}, null, ClassLoader::class);
}