[DX] Merge getObjectType() and getStaticType() methods to single getType() (#973)

This commit is contained in:
Tomas Votruba 2021-10-07 19:34:41 +02:00 committed by GitHub
parent 5ba1499034
commit f6f7431ce7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 222 additions and 782 deletions

View File

@ -10,7 +10,7 @@
- [Carbon](#carbon) (2)
- [CodeQuality](#codequality) (68)
- [CodeQuality](#codequality) (69)
- [CodingStyle](#codingstyle) (39)
@ -90,7 +90,7 @@
- [Renaming](#renaming) (11)
- [Restoration](#restoration) (6)
- [Restoration](#restoration) (5)
- [Strict](#strict) (5)
@ -845,6 +845,38 @@ Make if conditions more explicit
<br>
### ExplicitMethodCallOverMagicGetSetRector
Replace magic property fetch using `__get()` and `__set()` with existing method get*()/set*() calls
- class: [`Rector\CodeQuality\Rector\PropertyFetch\ExplicitMethodCallOverMagicGetSetRector`](../rules/CodeQuality/Rector/PropertyFetch/ExplicitMethodCallOverMagicGetSetRector.php)
```diff
class MagicCallsObject
{
// adds magic __get() and __set() methods
use \Nette\SmartObject;
private $name;
public function getName()
{
return $this->name;
}
}
class SomeClass
{
public function run(MagicObject $magicObject)
{
- return $magicObject->name;
+ return $magicObject->getName();
}
}
```
<br>
### FixClassCaseSensitivityNameRector
Change miss-typed case sensitivity name to correct one
@ -9651,53 +9683,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
<br>
### InferParamFromClassMethodReturnRector
Change `@param` doc based on another method return type
:wrench: **configure it!**
- class: [`Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector`](../rules/Restoration/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php)
```php
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => ValueObjectInliner::inline([
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
]),
]]);
};
```
```diff
class SomeClass
{
public function getNodeTypes(): array
{
return [String_::class];
}
+ /**
+ * @param String_ $node
+ */
public function process(Node $node)
{
}
}
```
<br>
### MakeTypedPropertyNullableIfCheckedRector
Make typed property nullable if checked

View File

@ -20,7 +20,6 @@ use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Accessory\NonEmptyArrayType;
use PHPStan\Type\ArrayType;
@ -39,7 +38,6 @@ use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\Configuration\RenamedClassesDataCollector;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -108,7 +106,7 @@ final class NodeTypeResolver
return false;
}
$resolvedType = $this->resolve($node);
$resolvedType = $this->getType($node);
if ($resolvedType instanceof MixedType) {
return false;
}
@ -124,32 +122,27 @@ final class NodeTypeResolver
return $this->isMatchingUnionType($resolvedType, $requiredObjectType);
}
/**
* @deprecated
* @see use NodeTypeResolver::getType() instead
*/
public function resolve(Node $node): Type
{
return $this->getType($node);
}
public function getType(Node $node): Type
{
if ($node instanceof Ternary) {
if ($node->if !== null) {
$first = $this->resolve($node->if);
$second = $this->resolve($node->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
$condType = $this->resolve($node->cond);
if ($this->isNullableType($node->cond) && $condType instanceof UnionType) {
$first = $condType->getTypes()[0];
$second = $this->resolve($node->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
$ternaryType = $this->resolveTernaryType($node);
if (! $ternaryType instanceof MixedType) {
return $ternaryType;
}
}
if ($node instanceof Coalesce) {
$first = $this->resolve($node->left);
$second = $this->resolve($node->right);
$first = $this->getType($node->left);
$second = $this->getType($node->right);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
@ -159,6 +152,9 @@ final class NodeTypeResolver
$type = $this->resolveByNodeTypeResolvers($node);
if ($type !== null) {
$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);
$type = $this->genericClassStringTypeCorrector->correct($type);
return $this->hasOffsetTypeCorrector->correct($type);
}
@ -190,8 +186,11 @@ final class NodeTypeResolver
}
$type = $scope->getType($node);
$type = $this->accessoryNonEmptyStringTypeCorrector->correct($type);
$type = $this->genericClassStringTypeCorrector->correct($type);
// hot fix for phpstan not resolving chain method calls
if (! $node instanceof MethodCall) {
return $type;
@ -201,7 +200,7 @@ final class NodeTypeResolver
return $type;
}
return $this->resolve($node->var);
return $this->getType($node->var);
}
/**
@ -209,7 +208,7 @@ final class NodeTypeResolver
*/
public function isNullableType(Node $node): bool
{
$nodeType = $this->resolve($node);
$nodeType = $this->getType($node);
return TypeCombinator::containsNull($nodeType);
}
@ -225,16 +224,8 @@ final class NodeTypeResolver
public function getStaticType(Node $node): Type
{
if ($node instanceof Param) {
return $this->resolve($node);
}
if ($node instanceof New_) {
return $this->resolve($node);
}
if ($node instanceof Return_) {
return $this->resolve($node);
if ($node instanceof Param || $node instanceof New_ || $node instanceof Return_) {
return $this->getType($node);
}
if (! $node instanceof Expr) {
@ -246,7 +237,7 @@ final class NodeTypeResolver
}
if ($node instanceof Scalar) {
return $this->resolve($node);
return $this->getType($node);
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
@ -268,28 +259,12 @@ final class NodeTypeResolver
public function isNumberType(Node $node): bool
{
if ($this->isStaticType($node, IntegerType::class)) {
$nodeType = $this->getType($node);
if ($nodeType instanceof IntegerType) {
return true;
}
return $this->isStaticType($node, FloatType::class);
}
/**
* @param class-string<Type> $staticTypeClass
*/
public function isStaticType(Node $node, string $staticTypeClass): bool
{
if (! is_a($staticTypeClass, Type::class, true)) {
throw new ShouldNotHappenException(sprintf(
'"%s" in "%s()" must be type of "%s"',
$staticTypeClass,
__METHOD__,
Type::class
));
}
return is_a($this->resolve($node), $staticTypeClass);
return $nodeType instanceof FloatType;
}
/**
@ -297,7 +272,7 @@ final class NodeTypeResolver
*/
public function isNullableTypeOfSpecificType(Node $node, string $desiredType): bool
{
$nodeType = $this->resolve($node);
$nodeType = $this->getType($node);
if (! $nodeType instanceof UnionType) {
return false;
}
@ -306,17 +281,8 @@ final class NodeTypeResolver
return false;
}
if (count($nodeType->getTypes()) !== 2) {
return false;
}
foreach ($nodeType->getTypes() as $type) {
if (is_a($type, $desiredType, true)) {
return true;
}
}
return false;
$bareType = TypeCombinator::removeNull($nodeType);
return is_a($bareType, $desiredType, true);
}
/**
@ -331,21 +297,6 @@ final class NodeTypeResolver
return $typeWithClassName->getClassName();
}
/**
* @param Type[] $desiredTypes
*/
public function isSameObjectTypes(ObjectType $objectType, array $desiredTypes): bool
{
foreach ($desiredTypes as $desiredType) {
$desiredTypeEquals = $desiredType->equals($objectType);
if ($desiredTypeEquals) {
return true;
}
}
return false;
}
public function isMethodStaticCallOrClassMethodObjectType(Node $node, ObjectType $objectType): bool
{
if ($node instanceof MethodCall) {
@ -365,21 +316,6 @@ final class NodeTypeResolver
return $this->isObjectType($classLike, $objectType);
}
public function resolveObjectTypeFromScope(Scope $scope): ?ObjectType
{
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
$className = $classReflection->getName();
if (! $this->reflectionProvider->hasClass($className)) {
return null;
}
return new ObjectType($className, null, $classReflection);
}
private function isUnionTypeable(Type $first, Type $second): bool
{
return ! $first instanceof UnionType && ! $second instanceof UnionType && ! $second instanceof NullType;
@ -501,4 +437,28 @@ final class NodeTypeResolver
return true;
}
private function resolveTernaryType(Ternary $ternary): MixedType|UnionType
{
if ($ternary->if !== null) {
$first = $this->getType($ternary->if);
$second = $this->getType($ternary->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
$condType = $this->getType($ternary->cond);
if ($this->isNullableType($ternary->cond) && $condType instanceof UnionType) {
$first = $condType->getTypes()[0];
$second = $this->getType($ternary->else);
if ($this->isUnionTypeable($first, $second)) {
return new UnionType([$first, $second]);
}
}
return new MixedType();
}
}

View File

@ -314,7 +314,7 @@ parameters:
- '#Cognitive complexity for "Rector\\BetterPhpDocParser\\PhpDocParser\\DoctrineAnnotationDecorator\:\:mergeNestedDoctrineAnnotations\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\BetterPhpDocParser\\Printer\\PhpDocInfoPrinter\:\:printDocChildNode\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:resolve\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:getType\(\)" is \d+, keep it under 9#'
-
message: '#Property with protected modifier is not allowed\. Use interface contract method instead#'

View File

@ -9,12 +9,9 @@ use Rector\CodingStyle\Rector\MethodCall\PreferThisOrSelfMethodCallRector;
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
use Rector\CodingStyle\ValueObject\ReturnArrayClassMethodToYield;
use Rector\Core\Configuration\Option;
use Rector\Core\Rector\AbstractRector;
use Rector\Nette\Set\NetteSetList;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
@ -38,15 +35,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import(NetteSetList::NETTE_UTILS_CODE_QUALITY);
$containerConfigurator->import(PHPUnitSetList::PHPUNIT_CODE_QUALITY);
$configuration = ValueObjectInliner::inline([
new InferParamFromClassMethodReturn(AbstractRector::class, 'refactor', 'getNodeTypes'),
]);
$services = $containerConfigurator->services();
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration,
]]);
// phpunit
$services->set(PreferThisOrSelfMethodCallRector::class)

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\Tests\Php71\Rector\FuncCall\CountOnNullRector\Fixture;
use PhpParser\Node\Stmt\ClassMethod;
final class CountOnClassMethodStmts
{
public function run(ClassMethod $classMethod)
{
return count($classMethod->stmts) === 5;
}
}
?>
-----
<?php
namespace Rector\Tests\Php71\Rector\FuncCall\CountOnNullRector\Fixture;
use PhpParser\Node\Stmt\ClassMethod;
final class CountOnClassMethodStmts
{
public function run(ClassMethod $classMethod)
{
return count((array) $classMethod->stmts) === 5;
}
}
?>

View File

@ -1,44 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SomeClass extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class];
}
public function process(\PhpParser\Node $node)
{
}
}
?>
-----
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SomeClass extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class];
}
/**
* @param \PhpParser\Node\Scalar\String_ $node
*/
public function process(\PhpParser\Node $node)
{
}
}
?>

View File

@ -1,23 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node;
use PhpParser\Node\Expr\AssignOp\Plus;
use PhpParser\Node\Expr\BinaryOp\Plus as BinaryPlus;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SkipAliasAlready extends SomeType
{
public function getNodeTypes(): array
{
return [Plus::class, BinaryPlus::class];
}
/**
* @param Plus|BinaryPlus $node
*/
public function process(Node $node)
{
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SkipCorrect extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class];
}
/**
* @param String_ $node
*/
public function process(\PhpParser\Node $node)
{
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SkipEmptyArray extends SomeType
{
public function getNodeTypes(): array
{
return [];
}
public function process(\PhpParser\Node $node)
{
}
}

View File

@ -1,18 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class SkipSameName extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class];
}
public function process(String_ $node)
{
}
}

View File

@ -1,46 +0,0 @@
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class UnionTypes extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class, LNumber::class];
}
public function process(\PhpParser\Node $node)
{
}
}
?>
-----
<?php
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
class UnionTypes extends SomeType
{
public function getNodeTypes(): array
{
return [String_::class, LNumber::class];
}
/**
* @param \PhpParser\Node\Scalar\LNumber|\PhpParser\Node\Scalar\String_ $node
*/
public function process(\PhpParser\Node $node)
{
}
}
?>

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class InferParamFromClassMethodReturnRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
/**
* @return Iterator<SmartFileInfo>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}

View File

@ -1,10 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source;
abstract class SomeType
{
}

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$configuration = ValueObjectInliner::inline([
new InferParamFromClassMethodReturn(SomeType::class, 'process', 'getNodeTypes'),
]);
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration,
]]);
};

View File

@ -42,7 +42,7 @@ class DFactory {}
class ClassStringArrayItem
{
/**
* @return array<string, mixed>
* @return array<string, mixed[]>
*/
public function getData()
{

View File

@ -40,7 +40,7 @@ class DFactory {}
class ClassStringArrayItem2
{
/**
* @return array<int, class-string>
* @return class-string[]
*/
public function getData()
{

View File

@ -60,7 +60,8 @@ final class ExprBoolCaster
return false;
}
if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) {
$exprType = $this->nodeTypeResolver->getType($expr);
if ($exprType instanceof BooleanType) {
return false;
}

View File

@ -65,8 +65,8 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
$leftStaticType = $this->getStaticType($node->left);
$rightStaticType = $this->getStaticType($node->right);
$leftStaticType = $this->getType($node->left);
$rightStaticType = $this->getType($node->right);
// objects can be different by content
if ($leftStaticType instanceof ObjectType) {

View File

@ -92,7 +92,7 @@ CODE_SAMPLE
/** @var BooleanAnd|BooleanOr $booleanExpr */
$booleanExpr = $expression->expr;
$leftStaticType = $this->getStaticType($booleanExpr->left);
$leftStaticType = $this->getType($booleanExpr->left);
if (! $leftStaticType instanceof BooleanType) {
return null;
}

View File

@ -159,7 +159,7 @@ CODE_SAMPLE
return true;
}
$foreachValueStaticType = $this->getStaticType($foreach->expr);
$foreachValueStaticType = $this->getType($foreach->expr);
if ($foreachValueStaticType instanceof ObjectType) {
return true;
}

View File

@ -143,7 +143,7 @@ CODE_SAMPLE
private function isArrayType(Expr $expr): bool
{
$exprType = $this->getStaticType($expr);
$exprType = $this->getType($expr);
if ($exprType instanceof ObjectType) {
return false;
}

View File

@ -93,7 +93,7 @@ CODE_SAMPLE
$firstArg = $node->args[0];
$firstValue = $firstArg->value;
$firstValueStaticType = $this->getStaticType($firstValue);
$firstValueStaticType = $this->getType($firstValue);
if (! $firstValueStaticType instanceof ConstantArrayType) {
return null;
}

View File

@ -72,7 +72,7 @@ CODE_SAMPLE
return null;
}
$firstArgumentStaticType = $this->getStaticType($node->args[0]->value);
$firstArgumentStaticType = $this->getType($node->args[0]->value);
if (! $firstArgumentStaticType instanceof StringType) {
return null;
}

View File

@ -96,7 +96,9 @@ CODE_SAMPLE
/** @var Arg $secondArg */
$secondArg = $node->args[1];
$valueArgument = $secondArg->value;
if (! $this->nodeTypeResolver->isStaticType($valueArgument, StringType::class)) {
$valueType = $this->getType($valueArgument);
if (! $valueType instanceof StringType) {
return null;
}

View File

@ -189,7 +189,7 @@ CODE_SAMPLE
$defaultValue = $property->props[0]->default;
if ($defaultValue !== null) {
$resolvedTypes[] = $this->getStaticType($defaultValue);
$resolvedTypes[] = $this->getType($defaultValue);
}
$resolveAssignedType = $this->resolveAssignedTypeInStmtsByPropertyName($classLike->stmts, $propertyName);
@ -240,7 +240,7 @@ CODE_SAMPLE
return null;
}
$resolvedTypes[] = $this->getStaticType($node->expr);
$resolvedTypes[] = $this->getType($node->expr);
return null;
});

View File

@ -78,11 +78,14 @@ CODE_SAMPLE
if ($node->expr instanceof Identical) {
$identical = $node->expr;
if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) {
$leftType = $this->getType($identical->left);
if (! $leftType instanceof BooleanType) {
return null;
}
if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) {
$rightType = $this->getType($identical->right);
if (! $rightType instanceof BooleanType) {
return null;
}
@ -94,11 +97,13 @@ CODE_SAMPLE
private function processIdentical(Identical $identical): ?NotIdentical
{
if (! $this->nodeTypeResolver->isStaticType($identical->left, BooleanType::class)) {
$leftType = $this->getType($identical->left);
if (! $leftType instanceof BooleanType) {
return null;
}
if (! $this->nodeTypeResolver->isStaticType($identical->right, BooleanType::class)) {
$rightType = $this->getType($identical->right);
if (! $rightType instanceof BooleanType) {
return null;
}

View File

@ -107,7 +107,7 @@ CODE_SAMPLE
$type = $phpDocInfo->getVarType();
if (! $type instanceof UnionType) {
$type = $this->getObjectType($assign->expr);
$type = $this->getType($assign->expr);
}
if (! $type instanceof UnionType) {

View File

@ -64,14 +64,13 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if ($this->nodeTypeResolver->isStaticType(
$node->left,
BooleanType::class
) && ! $this->valueResolver->isTrueOrFalse($node->left)) {
$leftType = $this->getType($node->left);
if ($leftType instanceof BooleanType && ! $this->valueResolver->isTrueOrFalse($node->left)) {
return $this->processBoolTypeToNotBool($node, $node->left, $node->right);
}
if (! $this->nodeTypeResolver->isStaticType($node->right, BooleanType::class)) {
$rightType = $this->getType($node->right);
if (! $rightType instanceof BooleanType) {
return null;
}

View File

@ -112,7 +112,7 @@ CODE_SAMPLE
return null;
}
$conditionStaticType = $this->getStaticType($conditionNode);
$conditionStaticType = $this->getType($conditionNode);
if ($conditionStaticType instanceof BooleanType || $conditionStaticType instanceof ConstantIntegerType) {
return null;
}
@ -152,11 +152,12 @@ CODE_SAMPLE
return $this->resolveString($isNegated, $expr);
}
if ($this->nodeTypeResolver->isStaticType($expr, IntegerType::class)) {
$exprType = $this->getType($expr);
if ($exprType instanceof IntegerType) {
return $this->resolveInteger($isNegated, $expr);
}
if ($this->nodeTypeResolver->isStaticType($expr, FloatType::class)) {
if ($exprType instanceof FloatType) {
return $this->resolveFloat($isNegated, $expr);
}

View File

@ -95,7 +95,7 @@ CODE_SAMPLE
continue;
}
$propertyFetchVarType = $this->getObjectType($issetVar->var);
$propertyFetchVarType = $this->getType($issetVar->var);
if ($propertyFetchVarType instanceof TypeWithClassName) {
if (! $this->reflectionProvider->hasClass($propertyFetchVarType->getClassName())) {

View File

@ -110,7 +110,7 @@ CODE_SAMPLE
private function refactorPropertyFetch(PropertyFetch $propertyFetch): MethodCall|null
{
$callerType = $this->getObjectType($propertyFetch->var);
$callerType = $this->getType($propertyFetch->var);
if (! $callerType instanceof TypeWithClassName) {
return null;
}
@ -143,7 +143,7 @@ CODE_SAMPLE
private function refactorMagicSet(Expr $expr, PropertyFetch $propertyFetch): MethodCall|null
{
$propertyCallerType = $this->getObjectType($propertyFetch->var);
$propertyCallerType = $this->getType($propertyFetch->var);
if (! $propertyCallerType instanceof ObjectType) {
return null;
}

View File

@ -61,7 +61,8 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (! $this->nodeTypeResolver->isStaticType($node->cond, BooleanType::class)) {
$condType = $this->getType($node->cond);
if (! $condType instanceof BooleanType) {
return null;
}

View File

@ -110,7 +110,8 @@ final class UnnecessaryTernaryExpressionRector extends AbstractRector
private function processTrueIfExpressionWithFalseElseExpression(Expr $expr): Expr
{
if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) {
$exprType = $this->getType($expr);
if ($exprType instanceof BooleanType) {
return $expr;
}
@ -120,14 +121,16 @@ final class UnnecessaryTernaryExpressionRector extends AbstractRector
private function processFalseIfExpressionWithTrueElseExpression(Expr $expr): Expr
{
if ($expr instanceof BooleanNot) {
if ($this->nodeTypeResolver->isStaticType($expr->expr, BooleanType::class)) {
$negatedExprType = $this->getType($expr->expr);
if ($negatedExprType instanceof BooleanType) {
return $expr->expr;
}
return new Bool_($expr->expr);
}
if ($this->nodeTypeResolver->isStaticType($expr, BooleanType::class)) {
$exprType = $this->getType($expr);
if ($exprType instanceof BooleanType) {
return new BooleanNot($expr);
}

View File

@ -76,7 +76,7 @@ CODE_SAMPLE
return null;
}
$constType = $this->getStaticType($node->consts[0]->value);
$constType = $this->getType($node->consts[0]->value);
if ($constType instanceof MixedType) {
return null;
}

View File

@ -84,7 +84,7 @@ CODE_SAMPLE
private function isNullableNonScalarType(Node $node): bool
{
$staticType = $this->getStaticType($node);
$staticType = $this->getType($node);
if ($staticType instanceof MixedType) {
return false;
}

View File

@ -96,7 +96,7 @@ CODE_SAMPLE
return null;
}
$nodeType = $this->getStaticType($node->expr);
$nodeType = $this->getType($node->expr);
if ($nodeType instanceof MixedType) {
return null;
}

View File

@ -149,7 +149,7 @@ CODE_SAMPLE
return null;
}
if (count($classMethod->stmts) !== 1) {
if (count((array) $classMethod->stmts) !== 1) {
return null;
}

View File

@ -71,7 +71,7 @@ CODE_SAMPLE
return null;
}
$conditionStaticType = $this->getStaticType($node->cond);
$conditionStaticType = $this->getType($node->cond);
if (! $conditionStaticType instanceof ConstantBooleanType) {
return null;
}

View File

@ -79,7 +79,7 @@ CODE_SAMPLE
return null;
}
$ifType = $this->getStaticType($node->if);
$ifType = $this->getType($node->if);
if (! $ifType instanceof BooleanType) {
return null;
}

View File

@ -78,7 +78,7 @@ CODE_SAMPLE
return null;
}
$classMethodStatementCount = count($node->stmts);
$classMethodStatementCount = count((array) $node->stmts);
// iterate from bottom to up, so we can merge
for ($i = $classMethodStatementCount - 1; $i >= 0; --$i) {

View File

@ -99,7 +99,7 @@ CODE_SAMPLE
continue;
}
$paramType = $this->getObjectType($paramNode);
$paramType = $this->getType($paramNode);
/** @var string $paramName */
$paramName = $this->getName($paramNode->var);
@ -124,7 +124,7 @@ CODE_SAMPLE
return false;
}
$paramStaticType = $this->getObjectType($param);
$paramStaticType = $this->getType($param);
if (! $paramStaticType instanceof ObjectType) {
return false;
}

View File

@ -131,7 +131,7 @@ CODE_SAMPLE
$paramName = $this->getName($param->var);
$variable = new Variable($paramName);
$paramType = $this->getStaticType($param);
$paramType = $this->getType($param);
$recastedVariable = $this->recastVariabletIfScalarType($variable, $paramType);
if (! $recastedVariable instanceof Cast) {
return null;

View File

@ -127,7 +127,7 @@ CODE_SAMPLE
return true;
}
return ! $this->getStaticType($methodCall->args[0]->value) instanceof StringType;
return ! $this->getType($methodCall->args[0]->value) instanceof StringType;
}
private function updateNode(MethodCall $methodCall): MethodCall

View File

@ -160,7 +160,7 @@ CODE_SAMPLE
return true;
}
$staticType = $this->getStaticType($expr);
$staticType = $this->getType($expr);
$resourceType = new ResourceType();
if ($staticType->equals($resourceType)) {

View File

@ -206,7 +206,7 @@ CODE_SAMPLE
private function isClassTypeWithChildren(StaticCall | MethodCall | FuncCall $expr): bool
{
$callStaticType = $this->getStaticType($expr);
$callStaticType = $this->getType($expr);
$callStaticType = $this->typeUnwrapper->unwrapNullableType($callStaticType);
if (! $callStaticType instanceof ObjectType) {

View File

@ -115,7 +115,7 @@ CODE_SAMPLE
private function isNotThisTypePropertyFetch(Expr $expr): bool
{
if ($expr instanceof PropertyFetch) {
$variableType = $this->getStaticType($expr->var);
$variableType = $this->getType($expr->var);
return ! $variableType instanceof ThisType;
}

View File

@ -110,7 +110,7 @@ CODE_SAMPLE
private function processVariableNum(Continue_ $continue, Variable $numVariable): Continue_ | Break_
{
$staticType = $this->getStaticType($numVariable);
$staticType = $this->getType($numVariable);
if (! $staticType instanceof ConstantType) {
return $continue;
}

View File

@ -108,7 +108,7 @@ CODE_SAMPLE
private function processVariableNum(Break_ | Continue_ $stmt, Variable $numVariable): ?Node
{
$staticType = $this->getStaticType($numVariable);
$staticType = $this->getType($numVariable);
if ($staticType instanceof ConstantType) {
if ($staticType instanceof ConstantIntegerType) {

View File

@ -52,7 +52,8 @@ final class ListSplitStringRector extends AbstractRector implements MinPhpVersio
return null;
}
if (! $this->nodeTypeResolver->isStaticType($node->expr, StringType::class)) {
$exprType = $this->getType($node->expr);
if (! $exprType instanceof StringType) {
return null;
}

View File

@ -105,13 +105,14 @@ CODE_SAMPLE
$node->name = new Identifier(MethodName::CONSTRUCT);
}
if ($node->stmts === null) {
$stmts = $node->stmts;
if ($stmts === null) {
return null;
}
if (count($node->stmts) === 1) {
if (count($stmts) === 1) {
/** @var Expression|Expr $stmt */
$stmt = $node->stmts[0];
$stmt = $stmts[0];
if (! $stmt instanceof Expression) {
return null;
}

View File

@ -130,7 +130,7 @@ CODE_SAMPLE
private function resolveStaticCallClassName(StaticCall $staticCall): ?string
{
if ($staticCall->class instanceof PropertyFetch) {
$objectType = $this->getObjectType($staticCall->class);
$objectType = $this->getType($staticCall->class);
if ($objectType instanceof ObjectType) {
return $objectType->getClassName();
}

View File

@ -117,7 +117,7 @@ CODE_SAMPLE
}
$value = null;
$exprStaticType = $this->getStaticType($expr);
$exprStaticType = $this->getType($expr);
if ($expr instanceof String_) {
$value = $expr->value;

View File

@ -95,7 +95,7 @@ CODE_SAMPLE
}
// this can lead to false positive by phpstan, but that's best we can do
$onlyValueType = $this->getStaticType($countedNode);
$onlyValueType = $this->getType($countedNode);
if ($onlyValueType instanceof ArrayType) {
if (! $this->countableAnalyzer->isCastableArrayType($countedNode)) {
return null;
@ -108,10 +108,9 @@ CODE_SAMPLE
return $this->castToArray($countedNode, $node);
}
if ($this->nodeTypeResolver->isNullableType($countedNode) || $this->nodeTypeResolver->isStaticType(
$countedNode,
NullType::class
)) {
$countedType = $this->getType($countedNode);
if ($this->nodeTypeResolver->isNullableType($countedNode) || $countedType instanceof NullType) {
$identical = new Identical($countedNode, $this->nodeFactory->createNull());
$ternary = new Ternary($identical, new LNumber(0), $node);
// prevent infinity loop re-resolution

View File

@ -103,10 +103,8 @@ CODE_SAMPLE
return null;
}
if (! $this->nodeTypeResolver->isNullableType($firstArgValue) && ! $this->nodeTypeResolver->isStaticType(
$firstArgValue,
NullType::class
)) {
$firstArgType = $this->getType($firstArgValue);
if (! $this->nodeTypeResolver->isNullableType($firstArgValue) && ! $firstArgType instanceof NullType) {
return null;
}

View File

@ -82,7 +82,7 @@ CODE_SAMPLE
return null;
}
$firstArgStaticType = $this->getStaticType($node->args[1]->value);
$firstArgStaticType = $this->getType($node->args[1]->value);
if (! $firstArgStaticType instanceof ObjectType) {
return null;
}

View File

@ -133,7 +133,7 @@ CODE_SAMPLE
return true;
}
$arrayStaticType = $this->getStaticType($expr);
$arrayStaticType = $this->getType($expr);
if ($this->isConstantArrayTypeWithStringKeyType($arrayStaticType)) {
return true;
}

View File

@ -64,7 +64,7 @@ final class MbStrrposEncodingArgumentPositionRector extends AbstractRector imple
return null;
}
$secondArgType = $this->getStaticType($node->args[2]->value);
$secondArgType = $this->getType($node->args[2]->value);
if ($secondArgType instanceof IntegerType) {
return null;
}

View File

@ -149,7 +149,7 @@ CODE_SAMPLE
private function processNullableType(Property $property, Param $param): void
{
if ($this->nodeTypeResolver->isNullableType($property)) {
$objectType = $this->getObjectType($property);
$objectType = $this->getType($property);
$param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($objectType, TypeKind::PARAM());
}
}

View File

@ -235,7 +235,7 @@ CODE_SAMPLE
$this->mirrorComments($classConst, $variable);
$constantType = $this->getStaticType($classConst->consts[0]->value);
$constantType = $this->getType($classConst->consts[0]->value);
$this->varAnnotationManipulator->decorateNodeWithType($classConst, $constantType);
return $classConst;

View File

@ -1,187 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Restoration\Type\ConstantReturnToParamTypeConverter;
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;
/**
* @see \Rector\Tests\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector\InferParamFromClassMethodReturnRectorTest
*/
final class InferParamFromClassMethodReturnRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @api
* @var string
*/
public const INFER_PARAMS_FROM_CLASS_METHOD_RETURNS = 'infer_param_from_class_method_returns';
/**
* @var InferParamFromClassMethodReturn[]
*/
private array $inferParamFromClassMethodReturn = [];
public function __construct(
private ReturnTypeInferer $returnTypeInferer,
private ConstantReturnToParamTypeConverter $constantReturnToParamTypeConverter,
private PhpDocTypeChanger $phpDocTypeChanger
) {
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Change @param doc based on another method return type', [
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function getNodeTypes(): array
{
return [String_::class];
}
public function process(Node $node)
{
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function getNodeTypes(): array
{
return [String_::class];
}
/**
* @param String_ $node
*/
public function process(Node $node)
{
}
}
CODE_SAMPLE
,
[
self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => [
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
],
]
),
]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*/
public function refactor(Node $node): ?Node
{
// must be exactly 1 param
if (count($node->params) !== 1) {
return null;
}
$firstParam = $node->params[0];
$paramName = $this->getName($firstParam);
foreach ($this->inferParamFromClassMethodReturn as $singleInferParamFromClassMethodReturn) {
$returnClassMethod = $this->matchReturnClassMethod($node, $singleInferParamFromClassMethodReturn);
if (! $returnClassMethod instanceof ClassMethod) {
continue;
}
$returnType = $this->returnTypeInferer->inferFunctionLike($returnClassMethod);
$currentPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$paramType = $this->constantReturnToParamTypeConverter->convert($returnType);
if ($paramType instanceof MixedType) {
continue;
}
if ($this->isParamDocTypeEqualToPhpType($firstParam, $paramType)) {
return null;
}
$this->phpDocTypeChanger->changeParamType($currentPhpDocInfo, $paramType, $firstParam, $paramName);
return $node;
}
return null;
}
/**
* @param array<string, InferParamFromClassMethodReturn[]> $configuration
*/
public function configure(array $configuration): void
{
$inferParamsFromClassMethodReturns = $configuration[self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS] ?? [];
Assert::allIsInstanceOf($inferParamsFromClassMethodReturns, InferParamFromClassMethodReturn::class);
$this->inferParamFromClassMethodReturn = $inferParamsFromClassMethodReturns;
}
private function matchReturnClassMethod(
ClassMethod $classMethod,
InferParamFromClassMethodReturn $inferParamFromClassMethodReturn
): ?ClassMethod {
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return null;
}
$classReflection = $scope->getClassReflection();
if (! $classReflection instanceof ClassReflection) {
return null;
}
if (! $classReflection->isSubclassOf($inferParamFromClassMethodReturn->getClass())) {
return null;
}
if (! $this->isName($classMethod->name, $inferParamFromClassMethodReturn->getParamMethod())) {
return null;
}
$classLike = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return null;
}
return $classLike->getMethod($inferParamFromClassMethodReturn->getReturnMethod());
}
private function isParamDocTypeEqualToPhpType(Param $param, Type $paramType): bool
{
$currentParamType = $this->nodeTypeResolver->getStaticType($param);
return $currentParamType->equals($paramType);
}
}

View File

@ -1,87 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Type;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
final class ConstantReturnToParamTypeConverter
{
public function __construct(
private TypeFactory $typeFactory
) {
}
public function convert(Type $type): Type
{
if ($type instanceof UnionType) {
$flattenReturnTypes = TypeUtils::flattenTypes($type);
$unionedTypes = [];
foreach ($flattenReturnTypes as $flattenReturnType) {
if ($flattenReturnType instanceof ArrayType) {
$unionedTypes[] = $flattenReturnType->getItemType();
}
}
$resolvedTypes = [];
foreach ($unionedTypes as $unionedType) {
$resolvedTypes[] = $this->convert($unionedType);
}
return new UnionType($resolvedTypes);
}
if ($type instanceof ConstantStringType) {
return $this->unwrapConstantTypeToObjectType($type);
}
if ($type instanceof ArrayType) {
return $this->unwrapConstantTypeToObjectType($type);
}
return new MixedType();
}
private function unwrapConstantTypeToObjectType(Type $type): Type
{
if ($type instanceof ArrayType) {
return $this->unwrapConstantTypeToObjectType($type->getItemType());
}
if ($type instanceof ConstantStringType) {
return new ObjectType($type->getValue());
}
if ($type instanceof GenericClassStringType && $type->getGenericType() instanceof ObjectType) {
return $type->getGenericType();
}
if ($type instanceof UnionType) {
return $this->unwrapUnionType($type);
}
return new MixedType();
}
private function unwrapUnionType(UnionType $unionType): Type
{
$types = [];
foreach ($unionType->getTypes() as $unionedType) {
$unionType = $this->unwrapConstantTypeToObjectType($unionedType);
if ($unionType !== null) {
$types[] = $unionType;
}
}
return $this->typeFactory->createMixedPassedOrUnionType($types);
}
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\ValueObject;
final class InferParamFromClassMethodReturn
{
public function __construct(
private string $class,
private string $paramMethod,
private string $returnMethod
) {
}
public function getClass(): string
{
return $this->class;
}
public function getParamMethod(): string
{
return $this->paramMethod;
}
public function getReturnMethod(): string
{
return $this->returnMethod;
}
}

View File

@ -105,7 +105,7 @@ CODE_SAMPLE
}
$arg = $node->args[$position];
$argValueType = $this->getStaticType($arg->value);
$argValueType = $this->getType($arg->value);
if (! $argValueType instanceof ClosureType) {
continue;
}

View File

@ -135,7 +135,7 @@ CODE_SAMPLE
continue;
}
$stmtType = $this->getStaticType($stmt);
$stmtType = $this->getType($stmt);
if ($stmtType instanceof NeverType) {
$hasNeverType = true;
}

View File

@ -64,8 +64,7 @@ final class ReturnTypeInferer
);
$isAutoImport = $this->parameterProvider->provideBoolParameter(Option::AUTO_IMPORT_NAMES);
$isAutoImportFullyQuafiedReturn = $this->isAutoImportWithFullyQualifiedReturn($isAutoImport, $functionLike);
if ($isAutoImportFullyQuafiedReturn) {
if ($this->isAutoImportWithFullyQualifiedReturn($isAutoImport, $functionLike)) {
return new MixedType();
}
@ -93,6 +92,7 @@ final class ReturnTypeInferer
// normalize ConstStringType to ClassStringType
$resolvedType = $this->genericClassStringTypeNormalizer->normalize($type);
return $this->resolveTypeWithVoidHandling($functionLike, $resolvedType);
}

View File

@ -323,18 +323,30 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
return $this->nodeTypeResolver->isObjectType($node, $objectType);
}
protected function getStaticType(Node $node): Type
/**
* Use this method for getting expr|node type
*/
protected function getType(Node $node): Type
{
return $this->nodeTypeResolver->getStaticType($node);
return $this->nodeTypeResolver->getType($node);
}
/**
* @deprecated
* Use getStaticType() instead, as single method to get types
* Use @see AbstractRector::getType() instead, as single method to get types
*/
protected function getObjectType(Node $node): Type
{
return $this->nodeTypeResolver->resolve($node);
return $this->nodeTypeResolver->getType($node);
}
/**
* @deprecated
* Use @see AbstractRector::getType() instead, as single method to get types
*/
protected function getStaticType(Node $node): Type
{
return $this->nodeTypeResolver->getType($node);
}
/**
@ -353,15 +365,6 @@ abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorIn
return $this->betterStandardPrinter->print($node);
}
/**
* @deprecated Use FQN PhpVersionProvider service directly instead or implements provideMinPhpVersion, this method will be removed soon
* Or implement \Rector\VersionBonding\Contract\MinPhpVersionInterface
*/
protected function isAtLeastPhpVersion(int $version): bool
{
return $this->phpVersionProvider->isAtLeastPhpVersion($version);
}
protected function mirrorComments(Node $newNode, Node $oldNode): void
{
$newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $oldNode->getAttribute(AttributeKey::PHP_DOC_INFO));