mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-12 14:12:23 +00:00
Updated Rector to commit b75b5d396f0d0c04242a1fb7fb44f03376f4d8e7
b75b5d396f
ReturnTypeFromStrictTernaryRector: Support complex ternaries (#4515)
This commit is contained in:
parent
8701fc25cc
commit
f6c482799f
|
@ -6,18 +6,25 @@ namespace Rector\TypeDeclaration\Rector\Class_;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\Ternary;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\ConstantType;
|
||||
use PHPStan\Type\GeneralizePrecision;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeCombinator;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Rector\AbstractScopeAwareRector;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\ValueObject\TernaryIfElseTypes;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
|
@ -33,9 +40,15 @@ final class ReturnTypeFromStrictTernaryRector extends AbstractScopeAwareRector i
|
|||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard)
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer
|
||||
*/
|
||||
private $returnTypeInferer;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, ReturnTypeInferer $returnTypeInferer)
|
||||
{
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
|
@ -64,93 +77,62 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function getNodeTypes() : array
|
||||
{
|
||||
return [Class_::class];
|
||||
return [ClassMethod::class, Function_::class, Closure::class];
|
||||
}
|
||||
/**
|
||||
* @param Class_ $node
|
||||
* @param ClassMethod|Function_|Closure $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
$hasChanged = \false;
|
||||
foreach ($node->getMethods() as $classMethod) {
|
||||
if ($classMethod->returnType instanceof Node) {
|
||||
continue;
|
||||
}
|
||||
$onlyStmt = $classMethod->stmts[0] ?? null;
|
||||
if (!$onlyStmt instanceof Return_) {
|
||||
continue;
|
||||
}
|
||||
if (!$onlyStmt->expr instanceof Ternary) {
|
||||
continue;
|
||||
}
|
||||
$ternary = $onlyStmt->expr;
|
||||
// has scalar in if/else of ternary
|
||||
$ternaryIfElseTypes = $this->matchScalarTernaryIfElseTypes($ternary);
|
||||
if (!$ternaryIfElseTypes instanceof TernaryIfElseTypes) {
|
||||
continue;
|
||||
}
|
||||
$ifType = $ternaryIfElseTypes->getFirstType();
|
||||
$elseType = $ternaryIfElseTypes->getSecondType();
|
||||
if (!$this->areTypesEqual($ifType, $elseType)) {
|
||||
continue;
|
||||
}
|
||||
if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($classMethod, $scope)) {
|
||||
continue;
|
||||
}
|
||||
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($ifType, TypeKind::RETURN);
|
||||
if (!$returnTypeNode instanceof Node) {
|
||||
continue;
|
||||
}
|
||||
$classMethod->returnType = $returnTypeNode;
|
||||
$hasChanged = \true;
|
||||
if ($this->shouldSkip($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
if ($hasChanged) {
|
||||
return $node;
|
||||
if ($node->stmts === null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
$returns = $this->betterNodeFinder->findInstanceOf($node->stmts, Return_::class);
|
||||
if (\count($returns) !== 1) {
|
||||
return null;
|
||||
}
|
||||
$return = $returns[0];
|
||||
if (!$return->expr instanceof Ternary) {
|
||||
return null;
|
||||
}
|
||||
$ternary = $return->expr;
|
||||
$nativeTernaryType = $scope->getNativeType($ternary);
|
||||
if ($nativeTernaryType instanceof MixedType) {
|
||||
return null;
|
||||
}
|
||||
$ternaryType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($ternary);
|
||||
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($ternaryType, TypeKind::RETURN);
|
||||
if (!$returnTypeNode instanceof Node) {
|
||||
return null;
|
||||
}
|
||||
$node->returnType = $returnTypeNode;
|
||||
return $node;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersionFeature::SCALAR_TYPES;
|
||||
}
|
||||
private function isAlwaysScalarExpr(?Expr $expr) : bool
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $node
|
||||
*/
|
||||
private function shouldSkip($node, Scope $scope) : bool
|
||||
{
|
||||
// check if Scalar node
|
||||
if ($expr instanceof Scalar) {
|
||||
if ($node->returnType !== null) {
|
||||
return \true;
|
||||
}
|
||||
// check if constant
|
||||
if ($expr instanceof ConstFetch) {
|
||||
$returnType = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
$returnType = TypeCombinator::removeNull($returnType);
|
||||
if ($returnType instanceof UnionType) {
|
||||
return \true;
|
||||
}
|
||||
// check if class constant
|
||||
return $expr instanceof ClassConstFetch;
|
||||
}
|
||||
private function areTypesEqual(Type $firstType, Type $secondType) : bool
|
||||
{
|
||||
// this is needed to make comparison tolerant to constant values, e.g. 5 and 10 are same only then
|
||||
if ($firstType instanceof ConstantType) {
|
||||
$firstType = $firstType->generalize(GeneralizePrecision::lessSpecific());
|
||||
if ($node instanceof ClassMethod) {
|
||||
if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
if ($secondType instanceof ConstantType) {
|
||||
$secondType = $secondType->generalize(GeneralizePrecision::lessSpecific());
|
||||
}
|
||||
return $firstType->equals($secondType);
|
||||
}
|
||||
private function matchScalarTernaryIfElseTypes(Ternary $ternary) : ?TernaryIfElseTypes
|
||||
{
|
||||
if (!$this->isAlwaysScalarExpr($ternary->if)) {
|
||||
return null;
|
||||
}
|
||||
if (!$this->isAlwaysScalarExpr($ternary->else)) {
|
||||
return null;
|
||||
}
|
||||
/** @var Node\Expr $if */
|
||||
$if = $ternary->if;
|
||||
/** @var Node\Expr $else */
|
||||
$else = $ternary->else;
|
||||
$ifType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($if);
|
||||
$elseType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($else);
|
||||
return new TernaryIfElseTypes($ifType, $elseType);
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\ValueObject;
|
||||
|
||||
use PHPStan\Type\Type;
|
||||
final class TernaryIfElseTypes
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \PHPStan\Type\Type
|
||||
*/
|
||||
private $firstType;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \PHPStan\Type\Type
|
||||
*/
|
||||
private $secondType;
|
||||
public function __construct(Type $firstType, Type $secondType)
|
||||
{
|
||||
$this->firstType = $firstType;
|
||||
$this->secondType = $secondType;
|
||||
}
|
||||
public function getFirstType() : Type
|
||||
{
|
||||
return $this->firstType;
|
||||
}
|
||||
public function getSecondType() : Type
|
||||
{
|
||||
return $this->secondType;
|
||||
}
|
||||
}
|
|
@ -19,12 +19,12 @@ final class VersionResolver
|
|||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const PACKAGE_VERSION = '5a9315aab0e60e7a8086beb1c6891de678b2f003';
|
||||
public const PACKAGE_VERSION = 'b75b5d396f0d0c04242a1fb7fb44f03376f4d8e7';
|
||||
/**
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const RELEASE_DATE = '2023-07-18 08:44:36';
|
||||
public const RELEASE_DATE = '2023-07-18 10:19:12';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
|
2
vendor/autoload.php
vendored
2
vendor/autoload.php
vendored
|
@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
|
|||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit7c2d1664db3c0cb93d0f0538a851f407::getLoader();
|
||||
return ComposerAutoloaderInitfb6357a522451a3a2edd8b2727b42b45::getLoader();
|
||||
|
|
1
vendor/composer/autoload_classmap.php
vendored
1
vendor/composer/autoload_classmap.php
vendored
|
@ -2741,7 +2741,6 @@ return array(
|
|||
'Rector\\TypeDeclaration\\ValueObject\\AddReturnTypeDeclaration' => $baseDir . '/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\AssignToVariable' => $baseDir . '/rules/TypeDeclaration/ValueObject/AssignToVariable.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\NestedArrayType' => $baseDir . '/rules/TypeDeclaration/ValueObject/NestedArrayType.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\TernaryIfElseTypes' => $baseDir . '/rules/TypeDeclaration/ValueObject/TernaryIfElseTypes.php',
|
||||
'Rector\\ValueObject\\ClassMethodWillChangeReturnType' => $vendorDir . '/rector/rector-downgrade-php/src/ValueObject/ClassMethodWillChangeReturnType.php',
|
||||
'Rector\\VendorLocker\\NodeVendorLocker\\ClassMethodParamVendorLockResolver' => $baseDir . '/packages/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php',
|
||||
'Rector\\VendorLocker\\NodeVendorLocker\\ClassMethodReturnTypeOverrideGuard' => $baseDir . '/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php',
|
||||
|
|
10
vendor/composer/autoload_real.php
vendored
10
vendor/composer/autoload_real.php
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit7c2d1664db3c0cb93d0f0538a851f407
|
||||
class ComposerAutoloaderInitfb6357a522451a3a2edd8b2727b42b45
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
|
@ -22,17 +22,17 @@ class ComposerAutoloaderInit7c2d1664db3c0cb93d0f0538a851f407
|
|||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit7c2d1664db3c0cb93d0f0538a851f407', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInitfb6357a522451a3a2edd8b2727b42b45', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit7c2d1664db3c0cb93d0f0538a851f407', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitfb6357a522451a3a2edd8b2727b42b45', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45::getInitializer($loader));
|
||||
|
||||
$loader->setClassMapAuthoritative(true);
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407::$files;
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
|
9
vendor/composer/autoload_static.php
vendored
9
vendor/composer/autoload_static.php
vendored
|
@ -4,7 +4,7 @@
|
|||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407
|
||||
class ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45
|
||||
{
|
||||
public static $files = array (
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
|
@ -2995,7 +2995,6 @@ class ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407
|
|||
'Rector\\TypeDeclaration\\ValueObject\\AddReturnTypeDeclaration' => __DIR__ . '/../..' . '/rules/TypeDeclaration/ValueObject/AddReturnTypeDeclaration.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\AssignToVariable' => __DIR__ . '/../..' . '/rules/TypeDeclaration/ValueObject/AssignToVariable.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\NestedArrayType' => __DIR__ . '/../..' . '/rules/TypeDeclaration/ValueObject/NestedArrayType.php',
|
||||
'Rector\\TypeDeclaration\\ValueObject\\TernaryIfElseTypes' => __DIR__ . '/../..' . '/rules/TypeDeclaration/ValueObject/TernaryIfElseTypes.php',
|
||||
'Rector\\ValueObject\\ClassMethodWillChangeReturnType' => __DIR__ . '/..' . '/rector/rector-downgrade-php/src/ValueObject/ClassMethodWillChangeReturnType.php',
|
||||
'Rector\\VendorLocker\\NodeVendorLocker\\ClassMethodParamVendorLockResolver' => __DIR__ . '/../..' . '/packages/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php',
|
||||
'Rector\\VendorLocker\\NodeVendorLocker\\ClassMethodReturnTypeOverrideGuard' => __DIR__ . '/../..' . '/packages/VendorLocker/NodeVendorLocker/ClassMethodReturnTypeOverrideGuard.php',
|
||||
|
@ -3028,9 +3027,9 @@ class ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407
|
|||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit7c2d1664db3c0cb93d0f0538a851f407::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitfb6357a522451a3a2edd8b2727b42b45::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user