Updated Rector to commit 620321b3833dd450a7de79a620a07c7131342422

620321b383 Add ReturnTypeFromStrictBoolReturnExprRector support for if/else returns (#5384)
This commit is contained in:
Tomas Votruba 2023-12-23 21:26:40 +00:00
parent f77e8b8450
commit 3d9b6b2c99
10 changed files with 88 additions and 27 deletions

View File

@ -7,8 +7,10 @@ use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ClassModifierChecker;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -17,8 +19,17 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
* @changelog https://3v4l.org/GU9dP
* @see \Rector\Tests\Php55\Rector\FuncCall\GetCalledClassToSelfClassRector\GetCalledClassToSelfClassRectorTest
*/
final class GetCalledClassToSelfClassRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
final class GetCalledClassToSelfClassRector extends AbstractRector implements MinPhpVersionInterface
{
/**
* @readonly
* @var \Rector\Core\Reflection\ClassModifierChecker
*/
private $classModifierChecker;
public function __construct(ClassModifierChecker $classModifierChecker)
{
$this->classModifierChecker = $classModifierChecker;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Change get_called_class() to self::class on final class', [new CodeSample(<<<'CODE_SAMPLE'
@ -51,19 +62,22 @@ CODE_SAMPLE
/**
* @param FuncCall $node
*/
public function refactorWithScope(Node $node, Scope $scope) : ?Node
public function refactor(Node $node) : ?Node
{
if (!$this->isName($node, 'get_called_class')) {
return null;
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return null;
}
if (!$scope->isInClass()) {
return null;
}
$classReflection = $scope->getClassReflection();
if ($classReflection->isFinalByKeyword()) {
if ($this->classModifierChecker->isInsideFinalClass($node)) {
return $this->nodeFactory->createClassConstFetch(ObjectReference::SELF, 'class');
}
if ($classReflection->isAnonymous()) {
if ($scope->isInAnonymousFunction()) {
return $this->nodeFactory->createClassConstFetch(ObjectReference::SELF, 'class');
}
return null;

View File

@ -6,7 +6,9 @@ namespace Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\Yield_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer;
@ -28,30 +30,69 @@ final class AlwaysStrictReturnAnalyzer
$this->returnAnalyzer = $returnAnalyzer;
}
/**
* @return Return_[]|null
* @return Return_[]
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\Function_ $functionLike
*/
public function matchAlwaysStrictReturns($functionLike) : ?array
public function matchAlwaysStrictReturns($functionLike) : array
{
if ($functionLike->stmts === null) {
return null;
return [];
}
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($functionLike, [Yield_::class])) {
return null;
return [];
}
/** @var Return_[] $returns */
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
if ($returns === []) {
return null;
return [];
}
// is one statement depth 3?
if (!$this->returnAnalyzer->areExclusiveExprReturns($returns)) {
return null;
return [];
}
// is one in ifOrElse, other in else?
if ($this->hasOnlyStmtWithIfAndElse($functionLike)) {
return $returns;
}
// has root return?
if (!$this->returnAnalyzer->hasClassMethodRootReturn($functionLike)) {
return null;
return [];
}
return $returns;
}
/**
* @param \PhpParser\Node\Stmt\If_|\PhpParser\Node\Stmt\Else_ $ifOrElse
*/
private function hasFirstLevelReturn($ifOrElse) : bool
{
foreach ($ifOrElse->stmts as $stmt) {
if ($stmt instanceof Return_) {
return \true;
}
}
return \false;
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $functionLike
*/
private function hasOnlyStmtWithIfAndElse($functionLike) : bool
{
foreach ((array) $functionLike->stmts as $functionLikeStmt) {
if (!$functionLikeStmt instanceof If_) {
continue;
}
$if = $functionLikeStmt;
if ($if->elseifs !== []) {
return \false;
}
if (!$if->else instanceof Else_) {
return \false;
}
if (!$this->hasFirstLevelReturn($if)) {
return \false;
}
return $this->hasFirstLevelReturn($if->else);
}
return \false;
}
}

View File

@ -31,7 +31,7 @@ final class StrictBoolReturnTypeAnalyzer
public function hasAlwaysStrictBoolReturn($functionLike) : bool
{
$returns = $this->alwaysStrictReturnAnalyzer->matchAlwaysStrictReturns($functionLike);
if ($returns === null) {
if ($returns === []) {
return \false;
}
foreach ($returns as $return) {

View File

@ -7,7 +7,6 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Type;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\TypeDeclaration\TypeAnalyzer\AlwaysStrictScalarExprAnalyzer;
@ -37,10 +36,10 @@ final class StrictScalarReturnTypeAnalyzer
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Stmt\Function_ $functionLike
*/
public function matchAlwaysScalarReturnType($functionLike, Scope $scope) : ?Type
public function matchAlwaysScalarReturnType($functionLike) : ?Type
{
$returns = $this->alwaysStrictReturnAnalyzer->matchAlwaysStrictReturns($functionLike);
if ($returns === null) {
if ($returns === []) {
return null;
}
$scalarTypes = [];

View File

@ -12,9 +12,9 @@ use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\Throw_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\Reflection\ClassModifierChecker;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeNestingScope\ValueObject\ControlStructure;
use Rector\TypeDeclaration\NodeAnalyzer\NeverFuncCallAnalyzer;
@ -44,11 +44,17 @@ final class ReturnNeverTypeRector extends AbstractScopeAwareRector implements Mi
* @var \Rector\TypeDeclaration\NodeAnalyzer\NeverFuncCallAnalyzer
*/
private $neverFuncCallAnalyzer;
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder, NeverFuncCallAnalyzer $neverFuncCallAnalyzer)
/**
* @readonly
* @var \Rector\Core\Reflection\ClassModifierChecker
*/
private $classModifierChecker;
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder, NeverFuncCallAnalyzer $neverFuncCallAnalyzer, ClassModifierChecker $classModifierChecker)
{
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
$this->betterNodeFinder = $betterNodeFinder;
$this->neverFuncCallAnalyzer = $neverFuncCallAnalyzer;
$this->classModifierChecker = $classModifierChecker;
}
public function getRuleDefinition() : RuleDefinition
{
@ -115,8 +121,7 @@ CODE_SAMPLE
return \false;
}
// skip as most likely intentional
$classReflection = $scope->getClassReflection();
if ($classReflection instanceof ClassReflection && !$classReflection->isFinalByKeyword() && $this->isName($node->returnType, 'void')) {
if (!$this->classModifierChecker->isInsideFinalClass($node) && $this->isName($node->returnType, 'void')) {
return \true;
}
return $this->isName($node->returnType, 'never');

View File

@ -70,7 +70,7 @@ CODE_SAMPLE
*/
public function refactorWithScope(Node $node, Scope $scope) : ?Node
{
if ($node->returnType !== null) {
if ($node->returnType instanceof Node) {
return null;
}
if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) {

View File

@ -90,6 +90,7 @@ CODE_SAMPLE
*/
public function refactorWithScope(Node $node, Scope $scope) : ?Node
{
// already typed → skip
if ($node->returnType instanceof Node) {
return null;
}

View File

@ -87,10 +87,11 @@ CODE_SAMPLE
*/
public function refactorWithScope(Node $node, Scope $scope) : ?Node
{
if ($node->returnType !== null) {
// already added → skip
if ($node->returnType instanceof Node) {
return null;
}
$scalarReturnType = $this->strictScalarReturnTypeAnalyzer->matchAlwaysScalarReturnType($node, $scope);
$scalarReturnType = $this->strictScalarReturnTypeAnalyzer->matchAlwaysScalarReturnType($node);
if (!$scalarReturnType instanceof Type) {
return null;
}

View File

@ -35,7 +35,7 @@ final class StrictReturnClassConstReturnTypeAnalyzer
public function matchAlwaysReturnConstFetch(ClassMethod $classMethod) : ?Type
{
$returns = $this->alwaysStrictReturnAnalyzer->matchAlwaysStrictReturns($classMethod);
if ($returns === null) {
if ($returns === []) {
return null;
}
$classConstFetchTypes = [];

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '16f8d19db32108740794a68a011c78328bb69aed';
public const PACKAGE_VERSION = '620321b3833dd450a7de79a620a07c7131342422';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-12-23 20:41:53';
public const RELEASE_DATE = '2023-12-23 21:24:27';
/**
* @var int
*/