mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 05:18:18 +01:00
Updated Rector to commit 2c1ae9bc46b3c8d31b1a45dcaac57c7d1522a8c1
2c1ae9bc46
Split StringReturnTypeFromStrictScalarReturnsRector into string, bool and numeric scalar + strict return type rules (#6113)
This commit is contained in:
parent
3839c795dc
commit
cd6dafbe86
@ -1,4 +1,4 @@
|
||||
# 377 Rules Overview
|
||||
# 380 Rules Overview
|
||||
|
||||
<br>
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
|
||||
- [Transform](#transform) (25)
|
||||
|
||||
- [TypeDeclaration](#typedeclaration) (51)
|
||||
- [TypeDeclaration](#typedeclaration) (56)
|
||||
|
||||
- [Visibility](#visibility) (3)
|
||||
|
||||
@ -6769,11 +6769,34 @@ Change && and || between nullable objects to instanceof compares
|
||||
|
||||
<br>
|
||||
|
||||
### BoolReturnTypeFromStrictScalarReturnsRector
|
||||
### BoolReturnTypeFromBooleanConstReturnsRector
|
||||
|
||||
Change return type based on strict returns type operations
|
||||
Add return bool, based on direct true/false returns
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromStrictScalarReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromStrictScalarReturnsRector.php)
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanConstReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php)
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
- public function resolve($value)
|
||||
+ public function resolve($value): bool
|
||||
{
|
||||
if ($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### BoolReturnTypeFromBooleanStrictReturnsRector
|
||||
|
||||
Add bool return type based on strict bool returns type operations
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanStrictReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php)
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
@ -6781,10 +6804,6 @@ Change return type based on strict returns type operations
|
||||
- public function resolve($first, $second)
|
||||
+ public function resolve($first, $second): bool
|
||||
{
|
||||
if ($first) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $first > $second;
|
||||
}
|
||||
}
|
||||
@ -6909,19 +6928,38 @@ Set DateTime to DateTimeInterface for DateTime property with DateTimeInterface d
|
||||
|
||||
<br>
|
||||
|
||||
### NumericReturnTypeFromStrictReturnsRector
|
||||
|
||||
Add int/float return type based on strict typed returns
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php)
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
- public function increase($value)
|
||||
+ public function increase($value): int
|
||||
{
|
||||
return ++$value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### NumericReturnTypeFromStrictScalarReturnsRector
|
||||
|
||||
Change numeric return type based on strict returns type operations
|
||||
Add int/float return type based on strict scalar returns type
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictScalarReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php)
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
- public function resolve(int $first, int $second)
|
||||
+ public function resolve(int $first, int $second): int
|
||||
- public function getNumber()
|
||||
+ public function getNumber(): int
|
||||
{
|
||||
return $first - $second;
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -7383,6 +7421,52 @@ Add string type based on concat use
|
||||
|
||||
<br>
|
||||
|
||||
### StringReturnTypeFromStrictScalarReturnsRector
|
||||
|
||||
Add string return type based on returned string scalar values
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictScalarReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php)
|
||||
|
||||
```diff
|
||||
final class SomeClass
|
||||
{
|
||||
- public function foo($condition)
|
||||
+ public function foo($condition): string;
|
||||
{
|
||||
if ($condition) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return 'no';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### StringReturnTypeFromStrictStringReturnsRector
|
||||
|
||||
Add string return type based on returned strict string values
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictStringReturnsRector`](../rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php)
|
||||
|
||||
```diff
|
||||
final class SomeClass
|
||||
{
|
||||
- public function foo($condition, $value)
|
||||
+ public function foo($condition, $value): string;
|
||||
{
|
||||
if ($value) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### TypedPropertyFromAssignsRector
|
||||
|
||||
Add typed property from assigned types
|
||||
|
@ -15,7 +15,6 @@ use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Enum_;
|
||||
use PhpParser\Node\Stmt\EnumCase;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
@ -142,8 +141,7 @@ final class EnumFactory
|
||||
if (!$classMethod instanceof ClassMethod) {
|
||||
return [];
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($classMethod, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($classMethod);
|
||||
/** @var array<int|string, mixed> $mapping */
|
||||
$mapping = [];
|
||||
foreach ($returns as $return) {
|
||||
|
@ -40,7 +40,7 @@ final class AlwaysStrictReturnAnalyzer
|
||||
return [];
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($functionLike);
|
||||
if ($returns === []) {
|
||||
return [];
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ use PhpParser\Node\Expr\Yield_;
|
||||
use PhpParser\Node\Expr\YieldFrom;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\ReturnFilter\ExclusiveNativeCallLikeReturnMatcher;
|
||||
@ -47,8 +46,7 @@ final class StrictNativeFunctionReturnTypeAnalyzer
|
||||
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($functionLike, [Yield_::class, YieldFrom::class])) {
|
||||
return null;
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($functionLike);
|
||||
if ($returns === []) {
|
||||
return null;
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
@ -60,8 +59,7 @@ final class StrictReturnNewAnalyzer
|
||||
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($functionLike, [Yield_::class, YieldFrom::class])) {
|
||||
return null;
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($functionLike);
|
||||
if ($returns === []) {
|
||||
return null;
|
||||
}
|
||||
|
@ -165,8 +165,7 @@ CODE_SAMPLE
|
||||
if (!$dataProviderClassMethod instanceof ClassMethod) {
|
||||
return new MixedType();
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($dataProviderClassMethod, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($dataProviderClassMethod);
|
||||
if ($returns !== []) {
|
||||
return $this->resolveReturnStaticArrayTypeByParameterPosition($returns, $parameterPosition);
|
||||
}
|
||||
|
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanConstReturnsRector\BoolReturnTypeFromBooleanConstReturnsRectorTest
|
||||
*/
|
||||
final class BoolReturnTypeFromBooleanConstReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\Value\ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
public function __construct(ValueResolver $valueResolver, BetterNodeFinder $betterNodeFinder, ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard)
|
||||
{
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add return bool, based on direct true/false returns', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve($value)
|
||||
{
|
||||
if ($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve($value): bool
|
||||
{
|
||||
if ($value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
)]);
|
||||
}
|
||||
/**
|
||||
* @funcCall array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes() : array
|
||||
{
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
if ($this->shouldSkip($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if (!$this->hasOnlyBooleanConstExprs($returns)) {
|
||||
return null;
|
||||
}
|
||||
$node->returnType = new Identifier('bool');
|
||||
return $node;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersionFeature::SCALAR_TYPES;
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_|Closure $node
|
||||
*/
|
||||
private function shouldSkip(Node $node, Scope $scope) : bool
|
||||
{
|
||||
// already has the type, skip
|
||||
if ($node->returnType instanceof Node) {
|
||||
return \true;
|
||||
}
|
||||
return $node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope);
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function hasOnlyBooleanConstExprs(array $returns) : bool
|
||||
{
|
||||
if ($returns === []) {
|
||||
return \false;
|
||||
}
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof ConstFetch) {
|
||||
return \false;
|
||||
}
|
||||
if (!$this->valueResolver->isTrueOrFalse($return->expr)) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
|
||||
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
|
||||
use PhpParser\Node\Expr\BinaryOp\Equal;
|
||||
use PhpParser\Node\Expr\BinaryOp\Greater;
|
||||
use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual;
|
||||
use PhpParser\Node\Expr\BinaryOp\Identical;
|
||||
use PhpParser\Node\Expr\BinaryOp\NotEqual;
|
||||
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
|
||||
use PhpParser\Node\Expr\BinaryOp\Smaller;
|
||||
use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\Value\ValueResolver;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanStrictReturnsRector\BoolReturnTypeFromBooleanStrictReturnsRectorTest
|
||||
*/
|
||||
final class BoolReturnTypeFromBooleanStrictReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \PHPStan\Reflection\ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\Value\ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
public function __construct(ReflectionProvider $reflectionProvider, ValueResolver $valueResolver, BetterNodeFinder $betterNodeFinder, ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->valueResolver = $valueResolver;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add bool return type based on strict bool returns type operations', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve($first, $second)
|
||||
{
|
||||
return $first > $second;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve($first, $second): bool
|
||||
{
|
||||
return $first > $second;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
)]);
|
||||
}
|
||||
/**
|
||||
* @funcCall array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes() : array
|
||||
{
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
if ($this->shouldSkip($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
// handled in another rule
|
||||
if ($this->hasOnlyBooleanConstExprs($returns)) {
|
||||
return null;
|
||||
}
|
||||
// handled in another rule
|
||||
if (!$this->hasOnlyBoolScalarReturnExprs($returns)) {
|
||||
return null;
|
||||
}
|
||||
$node->returnType = new Identifier('bool');
|
||||
return $node;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersionFeature::SCALAR_TYPES;
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_|Closure $node
|
||||
*/
|
||||
private function shouldSkip(Node $node, Scope $scope) : bool
|
||||
{
|
||||
// already has the type, skip
|
||||
if ($node->returnType instanceof Node) {
|
||||
return \true;
|
||||
}
|
||||
return $node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope);
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function hasOnlyBoolScalarReturnExprs(array $returns) : bool
|
||||
{
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof Expr) {
|
||||
return \false;
|
||||
}
|
||||
if ($this->isBooleanBinaryOp($return->expr)) {
|
||||
continue;
|
||||
}
|
||||
if ($return->expr instanceof FuncCall && $this->isNativeBooleanReturnTypeFuncCall($return->expr)) {
|
||||
continue;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
private function isNativeBooleanReturnTypeFuncCall(FuncCall $funcCall) : bool
|
||||
{
|
||||
$functionName = $this->getName($funcCall);
|
||||
if (!\is_string($functionName)) {
|
||||
return \false;
|
||||
}
|
||||
$functionReflection = $this->reflectionProvider->getFunction(new Name($functionName), null);
|
||||
if (!$functionReflection->isBuiltin()) {
|
||||
return \false;
|
||||
}
|
||||
foreach ($functionReflection->getVariants() as $parametersAcceptorWithPhpDoc) {
|
||||
return $parametersAcceptorWithPhpDoc->getNativeReturnType() instanceof BooleanType;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
private function isBooleanBinaryOp(Expr $expr) : bool
|
||||
{
|
||||
if ($expr instanceof Smaller) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof SmallerOrEqual) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof Greater) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof GreaterOrEqual) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof BooleanOr) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof BooleanAnd) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof Identical) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof NotIdentical) {
|
||||
return \true;
|
||||
}
|
||||
if ($expr instanceof Equal) {
|
||||
return \true;
|
||||
}
|
||||
return $expr instanceof NotEqual;
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function hasOnlyBooleanConstExprs(array $returns) : bool
|
||||
{
|
||||
if ($returns === []) {
|
||||
return \false;
|
||||
}
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof ConstFetch) {
|
||||
return \false;
|
||||
}
|
||||
if (!$this->valueResolver->isTrueOrFalse($return->expr)) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
@ -35,6 +35,9 @@ use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @deprecated Since 1.2.1, as name is invalid and work with native constants, not scalars. Use
|
||||
* @see BoolReturnTypeFromBooleanConstReturnsRector instead.
|
||||
*
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromStrictScalarReturnsRector\BoolReturnTypeFromStrictScalarReturnsRectorTest
|
||||
*/
|
||||
final class BoolReturnTypeFromStrictScalarReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
|
@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\DNumber;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictReturnsRector\NumericReturnTypeFromStrictReturnsRectorTest
|
||||
*/
|
||||
final class NumericReturnTypeFromStrictReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add int/float return type based on strict typed returns', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function increase($value)
|
||||
{
|
||||
return ++$value;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function increase($value): int
|
||||
{
|
||||
return ++$value;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
)]);
|
||||
}
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes() : array
|
||||
{
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
if ($this->shouldSkip($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if ($returns === []) {
|
||||
return null;
|
||||
}
|
||||
// handled by another rule
|
||||
if ($this->isAlwaysNumeric($returns)) {
|
||||
return null;
|
||||
}
|
||||
$isAlwaysIntType = \true;
|
||||
$isAlwaysFloatType = \true;
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof Expr) {
|
||||
return null;
|
||||
}
|
||||
$exprType = $this->nodeTypeResolver->getType($return->expr);
|
||||
if (!$exprType->isInteger()->yes()) {
|
||||
$isAlwaysIntType = \false;
|
||||
}
|
||||
if (!$exprType->isFloat()->yes()) {
|
||||
$isAlwaysFloatType = \false;
|
||||
}
|
||||
}
|
||||
if ($isAlwaysFloatType) {
|
||||
$node->returnType = new Identifier('float');
|
||||
return $node;
|
||||
}
|
||||
if ($isAlwaysIntType) {
|
||||
$node->returnType = new Identifier('int');
|
||||
return $node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersionFeature::SCALAR_TYPES;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
||||
*/
|
||||
private function shouldSkip($functionLike, Scope $scope) : bool
|
||||
{
|
||||
// type is already known, skip
|
||||
if ($functionLike->returnType instanceof Node) {
|
||||
return \true;
|
||||
}
|
||||
// empty, nothing to find
|
||||
if ($functionLike->stmts === null || $functionLike->stmts === []) {
|
||||
return \true;
|
||||
}
|
||||
if (!$functionLike instanceof ClassMethod) {
|
||||
return \false;
|
||||
}
|
||||
return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope);
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function isAlwaysNumeric(array $returns) : bool
|
||||
{
|
||||
$isAlwaysFloat = \true;
|
||||
$isAlwaysInt = \true;
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof DNumber) {
|
||||
$isAlwaysFloat = \false;
|
||||
}
|
||||
if (!$return->expr instanceof LNumber) {
|
||||
$isAlwaysInt = \false;
|
||||
}
|
||||
}
|
||||
if ($isAlwaysFloat) {
|
||||
return \true;
|
||||
}
|
||||
return $isAlwaysInt;
|
||||
}
|
||||
}
|
@ -5,11 +5,13 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\DNumber;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\ValueObject\PhpVersionFeature;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
@ -27,31 +29,31 @@ final class NumericReturnTypeFromStrictScalarReturnsRector extends AbstractScope
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $returnTypeInferer;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, ReturnTypeInferer $returnTypeInferer)
|
||||
private $betterNodeFinder;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change numeric return type based on strict returns type operations', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
return new RuleDefinition('Add int/float return type based on strict scalar returns type', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve(int $first, int $second)
|
||||
public function getNumber()
|
||||
{
|
||||
return $first - $second;
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function resolve(int $first, int $second): int
|
||||
public function getNumber(): int
|
||||
{
|
||||
return $first - $second;
|
||||
return 200;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
@ -69,21 +71,28 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
if ($node->returnType instanceof Node) {
|
||||
if ($this->shouldSkip($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
if ($node->stmts === null) {
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if ($returns === []) {
|
||||
return null;
|
||||
}
|
||||
if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) {
|
||||
return null;
|
||||
$isAlwaysInt = \true;
|
||||
$isAlwaysFloat = \true;
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof DNumber) {
|
||||
$isAlwaysFloat = \false;
|
||||
}
|
||||
if (!$return->expr instanceof LNumber) {
|
||||
$isAlwaysInt = \false;
|
||||
}
|
||||
}
|
||||
$returnType = $this->returnTypeInferer->inferFunctionLike($node);
|
||||
if ($returnType->isFloat()->yes()) {
|
||||
if ($isAlwaysFloat) {
|
||||
$node->returnType = new Identifier('float');
|
||||
return $node;
|
||||
}
|
||||
if ($returnType->isInteger()->yes()) {
|
||||
if ($isAlwaysInt) {
|
||||
$node->returnType = new Identifier('int');
|
||||
return $node;
|
||||
}
|
||||
@ -93,4 +102,22 @@ CODE_SAMPLE
|
||||
{
|
||||
return PhpVersionFeature::SCALAR_TYPES;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
||||
*/
|
||||
private function shouldSkip($functionLike, Scope $scope) : bool
|
||||
{
|
||||
// type is already known, skip
|
||||
if ($functionLike->returnType instanceof Node) {
|
||||
return \true;
|
||||
}
|
||||
// empty, nothing to ifnd
|
||||
if ($functionLike->stmts === null || $functionLike->stmts === []) {
|
||||
return \true;
|
||||
}
|
||||
if (!$functionLike instanceof ClassMethod) {
|
||||
return \false;
|
||||
}
|
||||
return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
@ -105,7 +104,7 @@ CODE_SAMPLE
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_|Closure $node
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
@ -125,8 +124,7 @@ CODE_SAMPLE
|
||||
if ($this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($node, [Yield_::class, YieldFrom::class])) {
|
||||
return null;
|
||||
}
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($node, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if ($returns === []) {
|
||||
return null;
|
||||
}
|
||||
@ -199,7 +197,7 @@ CODE_SAMPLE
|
||||
/**
|
||||
* @param Variable[] $variables
|
||||
* @return Variable[]
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure $functionLike
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
||||
*/
|
||||
private function matchVariableNotOverriddenByNonArray($functionLike, array $variables) : array
|
||||
{
|
||||
|
@ -8,7 +8,6 @@ use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticPropertyFetch;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\Php\PhpPropertyReflection;
|
||||
use PHPStan\Type\MixedType;
|
||||
@ -131,8 +130,7 @@ CODE_SAMPLE
|
||||
*/
|
||||
private function resolveReturnPropertyType(ClassMethod $classMethod) : array
|
||||
{
|
||||
/** @var Return_[] $returns */
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($classMethod, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($classMethod);
|
||||
$propertyTypes = [];
|
||||
foreach ($returns as $return) {
|
||||
if (!$return->expr instanceof Expr) {
|
||||
|
@ -3,15 +3,15 @@
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PHPStan\Type\Type;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\Encapsed;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictScalarReturnTypeAnalyzer;
|
||||
use Rector\ValueObject\PhpVersion;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
@ -22,11 +22,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
*/
|
||||
final class StringReturnTypeFromStrictScalarReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictScalarReturnTypeAnalyzer
|
||||
*/
|
||||
private $strictScalarReturnTypeAnalyzer;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
@ -34,40 +29,39 @@ final class StringReturnTypeFromStrictScalarReturnsRector extends AbstractScopeA
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\StaticTypeMapper\StaticTypeMapper
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
public function __construct(StrictScalarReturnTypeAnalyzer $strictScalarReturnTypeAnalyzer, ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, StaticTypeMapper $staticTypeMapper)
|
||||
private $betterNodeFinder;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->strictScalarReturnTypeAnalyzer = $strictScalarReturnTypeAnalyzer;
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change return type based on strict string scalar returns', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
return new RuleDefinition('Add string return type based on returned string scalar values', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function foo($condition, $value)
|
||||
public function foo($condition)
|
||||
{
|
||||
if ($value) {
|
||||
if ($condition) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
return 'no';
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function foo($condition, $value): string;
|
||||
public function foo($condition): string;
|
||||
{
|
||||
if ($value) {
|
||||
if ($condition) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
return 'no';
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
@ -89,26 +83,35 @@ CODE_SAMPLE
|
||||
if ($node->returnType instanceof Node) {
|
||||
return null;
|
||||
}
|
||||
$scalarReturnType = $this->strictScalarReturnTypeAnalyzer->matchAlwaysScalarReturnType($node);
|
||||
if (!$scalarReturnType instanceof Type) {
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if ($returns === []) {
|
||||
// void
|
||||
return null;
|
||||
}
|
||||
// must be string type
|
||||
if (!$scalarReturnType->isString()->yes()) {
|
||||
foreach ($returns as $return) {
|
||||
// we need exact string "value" return
|
||||
if (!$return->expr instanceof String_ && !$return->expr instanceof Encapsed) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($this->shouldSkipClassMethodForOverride($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($scalarReturnType, TypeKind::RETURN);
|
||||
if (!$returnTypeNode instanceof Node) {
|
||||
return null;
|
||||
}
|
||||
if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$node->returnType = $returnTypeNode;
|
||||
$node->returnType = new Identifier('string');
|
||||
return $node;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersion::PHP_70;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
||||
*/
|
||||
private function shouldSkipClassMethodForOverride($functionLike, Scope $scope) : bool
|
||||
{
|
||||
if (!$functionLike instanceof ClassMethod) {
|
||||
return \false;
|
||||
}
|
||||
return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Scalar\Encapsed;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractScopeAwareRector;
|
||||
use Rector\ValueObject\PhpVersion;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictStringReturnsRector\StringReturnTypeFromStrictStringReturnsRectorTest
|
||||
*/
|
||||
final class StringReturnTypeFromStrictStringReturnsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PhpParser\Node\BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
public function __construct(ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add string return type based on returned strict string values', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function foo($condition, $value)
|
||||
{
|
||||
if ($value) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
, <<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function foo($condition, $value): string;
|
||||
{
|
||||
if ($value) {
|
||||
return 'yes';
|
||||
}
|
||||
|
||||
return strtoupper($value);
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
)]);
|
||||
}
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes() : array
|
||||
{
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactorWithScope(Node $node, Scope $scope) : ?Node
|
||||
{
|
||||
// already added → skip
|
||||
if ($node->returnType instanceof Node) {
|
||||
return null;
|
||||
}
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if ($returns === []) {
|
||||
// void
|
||||
return null;
|
||||
}
|
||||
// handled by another rule
|
||||
if ($this->hasAlwaysStringScalarReturn($returns)) {
|
||||
return null;
|
||||
}
|
||||
// anything that return strict string, but no strings only
|
||||
if (!$this->isAlwaysStringStrictType($returns)) {
|
||||
return null;
|
||||
}
|
||||
if ($this->shouldSkipClassMethodForOverride($node, $scope)) {
|
||||
return null;
|
||||
}
|
||||
$node->returnType = new Identifier('string');
|
||||
return $node;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersion::PHP_70;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
|
||||
*/
|
||||
private function shouldSkipClassMethodForOverride($functionLike, Scope $scope) : bool
|
||||
{
|
||||
if (!$functionLike instanceof ClassMethod) {
|
||||
return \false;
|
||||
}
|
||||
return $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($functionLike, $scope);
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function hasAlwaysStringScalarReturn(array $returns) : bool
|
||||
{
|
||||
foreach ($returns as $return) {
|
||||
// we need exact string "value" return
|
||||
if (!$return->expr instanceof String_ && !$return->expr instanceof Encapsed) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* @param Return_[] $returns
|
||||
*/
|
||||
private function isAlwaysStringStrictType(array $returns) : bool
|
||||
{
|
||||
foreach ($returns as $return) {
|
||||
// void return
|
||||
if (!$return->expr instanceof Expr) {
|
||||
return \false;
|
||||
}
|
||||
$exprType = $this->nodeTypeResolver->getNativeType($return->expr);
|
||||
if (!$exprType->isString()->yes()) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Ternary;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\TypeCombinator;
|
||||
@ -95,7 +94,7 @@ CODE_SAMPLE
|
||||
if ($node->stmts === null) {
|
||||
return null;
|
||||
}
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($node, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($node);
|
||||
if (\count($returns) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ final class ReturnTypeInferer
|
||||
return $unionType;
|
||||
}
|
||||
if (!$functionLike instanceof ArrowFunction) {
|
||||
$returns = $this->betterNodeFinder->findInstancesOfInFunctionLikeScoped($functionLike, Return_::class);
|
||||
$returns = $this->betterNodeFinder->findReturnsScoped($functionLike);
|
||||
$returnsWithExpr = \array_filter($returns, static function (Return_ $return) : bool {
|
||||
return $return->expr instanceof Expr;
|
||||
});
|
||||
|
@ -19,12 +19,12 @@ final class VersionResolver
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const PACKAGE_VERSION = 'd8400c7cf1b75378603c7276c95393e2fd74c5b5';
|
||||
public const PACKAGE_VERSION = '2c1ae9bc46b3c8d31b1a45dcaac57c7d1522a8c1';
|
||||
/**
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const RELEASE_DATE = '2024-07-03 18:39:31';
|
||||
public const RELEASE_DATE = '2024-07-03 20:12:05';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
@ -16,7 +16,9 @@ use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeBasedOnPHPUnitDataProv
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromPropertyTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddReturnTypeDeclarationBasedOnParentClassMethodRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddVoidReturnTypeWhereNoReturnRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromStrictScalarReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanConstReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanStrictReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\NumericReturnTypeFromStrictScalarReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByMethodCallTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ParamTypeByParentCallTypeRector;
|
||||
@ -38,6 +40,7 @@ use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\StrictArrayParamDimFetchRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\StrictStringParamConcatRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictScalarReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictStringReturnsRector;
|
||||
use Rector\TypeDeclaration\Rector\Closure\AddClosureNeverReturnTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
|
||||
use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeRector;
|
||||
@ -66,9 +69,12 @@ final class TypeDeclarationLevel
|
||||
ReturnTypeFromStrictNewArrayRector::class,
|
||||
ReturnTypeFromStrictBoolReturnExprRector::class,
|
||||
// scalar values
|
||||
BoolReturnTypeFromStrictScalarReturnsRector::class,
|
||||
NumericReturnTypeFromStrictScalarReturnsRector::class,
|
||||
BoolReturnTypeFromBooleanConstReturnsRector::class,
|
||||
StringReturnTypeFromStrictScalarReturnsRector::class,
|
||||
NumericReturnTypeFromStrictScalarReturnsRector::class,
|
||||
BoolReturnTypeFromBooleanStrictReturnsRector::class,
|
||||
StringReturnTypeFromStrictStringReturnsRector::class,
|
||||
NumericReturnTypeFromStrictReturnsRector::class,
|
||||
ReturnTypeFromStrictTernaryRector::class,
|
||||
ReturnTypeFromReturnDirectArrayRector::class,
|
||||
ReturnTypeFromReturnNewRector::class,
|
||||
|
4
vendor/composer/autoload_classmap.php
vendored
4
vendor/composer/autoload_classmap.php
vendored
@ -2417,7 +2417,10 @@ return array(
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddTypeFromResourceDocblockRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddTypeFromResourceDocblockRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddVoidReturnTypeWhereNoReturnRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromBooleanConstReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromBooleanStrictReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromStrictScalarReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\NumericReturnTypeFromStrictReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\NumericReturnTypeFromStrictScalarReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\ParamTypeByMethodCallTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\ParamTypeByParentCallTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php',
|
||||
@ -2440,6 +2443,7 @@ return array(
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StringReturnTypeFromStrictScalarReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StringReturnTypeFromStrictStringReturnsRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
|
||||
|
4
vendor/composer/autoload_static.php
vendored
4
vendor/composer/autoload_static.php
vendored
@ -2636,7 +2636,10 @@ class ComposerStaticInit2971ea952332f12bcefd9fbb21b5c118
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddTypeFromResourceDocblockRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddTypeFromResourceDocblockRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddVoidReturnTypeWhereNoReturnRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddVoidReturnTypeWhereNoReturnRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromBooleanConstReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanConstReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromBooleanStrictReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\BoolReturnTypeFromStrictScalarReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\NumericReturnTypeFromStrictReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\NumericReturnTypeFromStrictScalarReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/NumericReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\ParamTypeByMethodCallTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\ParamTypeByParentCallTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByParentCallTypeRector.php',
|
||||
@ -2659,6 +2662,7 @@ class ComposerStaticInit2971ea952332f12bcefd9fbb21b5c118
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StringReturnTypeFromStrictScalarReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictScalarReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StringReturnTypeFromStrictStringReturnsRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php',
|
||||
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
|
||||
|
Loading…
x
Reference in New Issue
Block a user