diff --git a/composer.json b/composer.json index 1d59fcc58301..c6e383051b30 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "doctrine/annotations": "^1.11", "doctrine/inflector": "^2.0", "jean85/pretty-package-versions": "^1.5.1|^2.0.1", - "nette/robot-loader": "^3.2", + "nette/robot-loader": "^3.2 <=3.3.1", "nette/utils": "^3.2", "nikic/php-parser": "^4.10.4", "phpstan/phpdoc-parser": "^0.4.9", diff --git a/config/services.php b/config/services.php index 93df62fb3842..a8ce7683bee3 100644 --- a/config/services.php +++ b/config/services.php @@ -21,6 +21,7 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; use Symfony\Component\Filesystem\Filesystem; +use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; use Symplify\PackageBuilder\Console\Command\CommandNaming; use Symplify\PackageBuilder\Console\Style\SymfonyStyleFactory; use Symplify\PackageBuilder\Parameter\ParameterProvider; @@ -56,7 +57,7 @@ $services->alias(SymfonyApplication::class, ConsoleApplication::class); $services->set(NoRectorsLoadedReporter::class); - $services->set(\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser::class); + $services->set(SimpleCallableNodeTraverser::class); $services->set(TextDescriptor::class); @@ -77,6 +78,7 @@ $services->set(PrivatesCaller::class); $services->set(FinderSanitizer::class); $services->set(FileSystemFilter::class); + $services->set(ParameterProvider::class) ->arg('$container', service('service_container')); diff --git a/config/set/type-declaration.php b/config/set/type-declaration.php index db6255e2b4c8..aa64d6c7736b 100644 --- a/config/set/type-declaration.php +++ b/config/set/type-declaration.php @@ -2,20 +2,15 @@ declare(strict_types=1); -use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector; -use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector; -use Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector; -use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector; -use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector; -use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector; -use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - -return static function (ContainerConfigurator $containerConfigurator): void { +return static function ( + \Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator $containerConfigurator +): void { $services = $containerConfigurator->services(); - $services->set(ParamTypeDeclarationRector::class); - $services->set(ReturnTypeDeclarationRector::class); - $services->set(PropertyTypeDeclarationRector::class); - $services->set(AddClosureReturnTypeRector::class); - $services->set(AddArrayParamDocTypeRector::class); - $services->set(AddArrayReturnDocTypeRector::class); + $services->set(\Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector::class); + $services->set(\Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector::class); + $services->set(\Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector::class); + $services->set(\Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector::class); + $services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector::class); + $services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector::class); + // $services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromCallersRector::class); }; diff --git a/packages/node-collector/src/NodeCollector/NodeRepository.php b/packages/node-collector/src/NodeCollector/NodeRepository.php index c8c6ab4dfff2..f596a9af8739 100644 --- a/packages/node-collector/src/NodeCollector/NodeRepository.php +++ b/packages/node-collector/src/NodeCollector/NodeRepository.php @@ -49,7 +49,6 @@ use ReflectionMethod; /** - * @rector-doc * This service contains all the parsed nodes. E.g. all the functions, method call, classes, static calls etc. * It's useful in case of context analysis, e.g. find all the usage of class method to detect, if the method is used. */ @@ -377,7 +376,6 @@ public function findCallsByClassMethod(ClassMethod $classMethod): array /** @var string $method */ $method = $classMethod->getAttribute(AttributeKey::METHOD_NAME); - return $this->findCallsByClassAndMethod($class, $method); } diff --git a/packages/phpstan-static-type-mapper/src/TypeAnalyzer/UnionTypeCommonTypeNarrower.php b/packages/phpstan-static-type-mapper/src/TypeAnalyzer/UnionTypeCommonTypeNarrower.php index b21e4f973ab8..aa599001db4d 100644 --- a/packages/phpstan-static-type-mapper/src/TypeAnalyzer/UnionTypeCommonTypeNarrower.php +++ b/packages/phpstan-static-type-mapper/src/TypeAnalyzer/UnionTypeCommonTypeNarrower.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp; +use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\Reflection\ClassReflection; @@ -27,6 +28,7 @@ final class UnionTypeCommonTypeNarrower * @var array, array>> */ private const PRIORITY_TYPES = [ + FunctionLike::class => [FunctionLike::class], BinaryOp::class => [BinaryOp::class, Expr::class], Expr::class => [Node::class, Expr::class], Stmt::class => [Node::class, Stmt::class], diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ArrayTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ArrayTypeMapper.php index 7f0ec3fb0a6d..c65533c97be1 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ArrayTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ArrayTypeMapper.php @@ -75,6 +75,9 @@ public function autowireArrayTypeMapper( $this->reflectionProvider = $reflectionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return ArrayType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/BooleanTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/BooleanTypeMapper.php index 768c2d591d54..ecafc1e75d41 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/BooleanTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/BooleanTypeMapper.php @@ -27,6 +27,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return BooleanType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/CallableTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/CallableTypeMapper.php index 2e6055788bb2..d6949162dd35 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/CallableTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/CallableTypeMapper.php @@ -31,6 +31,9 @@ public function autowireCallableTypeMapper(PHPStanStaticTypeMapper $phpStanStati $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return CallableType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ClassStringTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ClassStringTypeMapper.php index 5f471dcdcff5..024bb712b1bd 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ClassStringTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ClassStringTypeMapper.php @@ -23,6 +23,9 @@ final class ClassStringTypeMapper implements TypeMapperInterface, PHPStanStaticT */ private $phpStanStaticTypeMapper; + /** + * @return class-string + */ public function getNodeClass(): string { return ClassStringType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ClosureTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ClosureTypeMapper.php index f878351600c1..7049cf6239ae 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ClosureTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ClosureTypeMapper.php @@ -32,6 +32,9 @@ public function __construct(CallableTypeMapper $callableTypeMapper) $this->callableTypeMapper = $callableTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return ClosureType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/FloatTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/FloatTypeMapper.php index fe99fe8217c5..9d4f34c08502 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/FloatTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/FloatTypeMapper.php @@ -27,6 +27,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return FloatType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/HasOffsetTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/HasOffsetTypeMapper.php index aca10df61a30..cc10c44b45cc 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/HasOffsetTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/HasOffsetTypeMapper.php @@ -15,6 +15,9 @@ final class HasOffsetTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return HasOffsetType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/IntegerTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/IntegerTypeMapper.php index 100f6e44dadc..3b1dbf3ca0de 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/IntegerTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/IntegerTypeMapper.php @@ -27,6 +27,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return IntegerType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/IntersectionTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/IntersectionTypeMapper.php index 893041ed0f25..caf3033cf95e 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/IntersectionTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/IntersectionTypeMapper.php @@ -27,6 +27,9 @@ public function autowireIntersectionTypeMapper(PHPStanStaticTypeMapper $phpStanS $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return IntersectionType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/IterableTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/IterableTypeMapper.php index 822d2a1594e9..1518d54ec967 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/IterableTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/IterableTypeMapper.php @@ -44,6 +44,9 @@ public function autowireIterableTypeMapper(PHPStanStaticTypeMapper $phpStanStati $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return IterableType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/MixedTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/MixedTypeMapper.php index 9fb9f555a5ad..d0a123a5b569 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/MixedTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/MixedTypeMapper.php @@ -14,6 +14,9 @@ final class MixedTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return MixedType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/NeverTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/NeverTypeMapper.php index e3385da8746b..83228e37096a 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/NeverTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/NeverTypeMapper.php @@ -13,6 +13,9 @@ final class NeverTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return NeverType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/NonEmptyArrayTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/NonEmptyArrayTypeMapper.php index 8b49c4965e96..485bf2b8989e 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/NonEmptyArrayTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/NonEmptyArrayTypeMapper.php @@ -15,6 +15,9 @@ final class NonEmptyArrayTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return NonEmptyArrayType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/NullTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/NullTypeMapper.php index ac5e5cc18632..3dc79f66fc02 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/NullTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/NullTypeMapper.php @@ -16,6 +16,9 @@ final class NullTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return NullType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectTypeMapper.php index b9368c3a86a4..4116d6dfcde8 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectTypeMapper.php @@ -42,6 +42,9 @@ public function __construct(ReflectionProvider $reflectionProvider) $this->reflectionProvider = $reflectionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return ObjectType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectWithoutClassTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectWithoutClassTypeMapper.php index 3645a02232e8..be844df25895 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectWithoutClassTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ObjectWithoutClassTypeMapper.php @@ -36,6 +36,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return ObjectWithoutClassType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ParentStaticTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ParentStaticTypeMapper.php index 5b388e991a48..1892865cc21d 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ParentStaticTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ParentStaticTypeMapper.php @@ -15,6 +15,9 @@ final class ParentStaticTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return ParentStaticType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ResourceTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ResourceTypeMapper.php index 2dc43f3b6691..424fb30b2807 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ResourceTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ResourceTypeMapper.php @@ -14,6 +14,9 @@ final class ResourceTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return ResourceType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/SelfObjectTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/SelfObjectTypeMapper.php index 44e9f6ba1a8e..d36517e60f0c 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/SelfObjectTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/SelfObjectTypeMapper.php @@ -15,6 +15,9 @@ final class SelfObjectTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return SelfObjectType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/StaticTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/StaticTypeMapper.php index eb6fd08b51ed..40971206bcf5 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/StaticTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/StaticTypeMapper.php @@ -31,6 +31,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return StaticType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/StrictMixedTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/StrictMixedTypeMapper.php index ae6fb36a0f15..a8cf36b47074 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/StrictMixedTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/StrictMixedTypeMapper.php @@ -19,6 +19,9 @@ final class StrictMixedTypeMapper implements TypeMapperInterface */ private const MIXED = 'mixed'; + /** + * @return class-string + */ public function getNodeClass(): string { return StrictMixedType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/StringTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/StringTypeMapper.php index da0791091632..6dd41552ebea 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/StringTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/StringTypeMapper.php @@ -27,6 +27,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return StringType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/ThisTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/ThisTypeMapper.php index ddc4cff83719..29139329ed46 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/ThisTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/ThisTypeMapper.php @@ -15,6 +15,9 @@ final class ThisTypeMapper implements TypeMapperInterface { + /** + * @return class-string + */ public function getNodeClass(): string { return ThisType::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/TypeWithClassNameTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/TypeWithClassNameTypeMapper.php index 551c5b5401ba..07bad8af1cd8 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/TypeWithClassNameTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/TypeWithClassNameTypeMapper.php @@ -24,6 +24,9 @@ public function __construct(StringTypeMapper $stringTypeMapper) $this->stringTypeMapper = $stringTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return TypeWithClassName::class; diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/UnionTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/UnionTypeMapper.php index a340c4e2ec0a..c5a423f717a6 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/UnionTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/UnionTypeMapper.php @@ -85,6 +85,9 @@ public function autowireUnionTypeMapper(PHPStanStaticTypeMapper $phpStanStaticTy $this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper; } + /** + * @return class-string + */ public function getNodeClass(): string { return UnionType::class; @@ -107,7 +110,6 @@ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode } $unionTypesNodes = array_unique($unionTypesNodes); - return new AttributeAwareUnionTypeNode($unionTypesNodes); } diff --git a/packages/phpstan-static-type-mapper/src/TypeMapper/VoidTypeMapper.php b/packages/phpstan-static-type-mapper/src/TypeMapper/VoidTypeMapper.php index 731242f89836..96058756f7a3 100644 --- a/packages/phpstan-static-type-mapper/src/TypeMapper/VoidTypeMapper.php +++ b/packages/phpstan-static-type-mapper/src/TypeMapper/VoidTypeMapper.php @@ -31,6 +31,9 @@ public function __construct(PhpVersionProvider $phpVersionProvider) $this->phpVersionProvider = $phpVersionProvider; } + /** + * @return class-string + */ public function getNodeClass(): string { return VoidType::class; diff --git a/packages/post-rector/src/Application/PostFileProcessor.php b/packages/post-rector/src/Application/PostFileProcessor.php index 4152170a49b9..6ef077d0e8dd 100644 --- a/packages/post-rector/src/Application/PostFileProcessor.php +++ b/packages/post-rector/src/Application/PostFileProcessor.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\NodeTraverser; use Rector\Core\Exception\ShouldNotHappenException; +use Rector\Core\Logging\CurrentRectorProvider; use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider; use Rector\PostRector\Contract\Rector\PostRectorInterface; use Symplify\Skipper\Skipper\Skipper; @@ -29,14 +30,24 @@ final class PostFileProcessor */ private $currentFileInfoProvider; + /** + * @var CurrentRectorProvider + */ + private $currentRectorProvider; + /** * @param PostRectorInterface[] $postRectors */ - public function __construct(Skipper $skipper, CurrentFileInfoProvider $currentFileInfoProvider, array $postRectors) - { + public function __construct( + Skipper $skipper, + CurrentFileInfoProvider $currentFileInfoProvider, + CurrentRectorProvider $currentRectorProvider, + array $postRectors + ) { $this->postRectors = $this->sortByPriority($postRectors); $this->skipper = $skipper; $this->currentFileInfoProvider = $currentFileInfoProvider; + $this->currentRectorProvider = $currentRectorProvider; } /** @@ -50,6 +61,8 @@ public function traverse(array $nodes): array continue; } + $this->currentRectorProvider->changeCurrentRector($postRector); + $nodeTraverser = new NodeTraverser(); $nodeTraverser->addVisitor($postRector); $nodes = $nodeTraverser->traverse($nodes); diff --git a/phpstan.neon b/phpstan.neon index fae34b271dc5..d8bdd2092901 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -47,7 +47,6 @@ parameters: - stubs reportUnmatchedIgnoredErrors: false - checkGenericClassInNonGenericObjectType: false excludes_analyse: diff --git a/rules/arguments/src/NodeAnalyzer/ArgumentAddingScope.php b/rules/arguments/src/NodeAnalyzer/ArgumentAddingScope.php index c905a00298bb..444e7e131464 100644 --- a/rules/arguments/src/NodeAnalyzer/ArgumentAddingScope.php +++ b/rules/arguments/src/NodeAnalyzer/ArgumentAddingScope.php @@ -4,11 +4,10 @@ namespace Rector\Arguments\NodeAnalyzer; -use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Name; -use PhpParser\Node\Stmt\ClassMethod; use Rector\Arguments\ValueObject\ArgumentAdder; use Rector\NodeNameResolver\NodeNameResolver; @@ -40,26 +39,21 @@ public function __construct(NodeNameResolver $nodeNameResolver) } /** - * @param ClassMethod|MethodCall|StaticCall $node + * @param MethodCall|StaticCall $expr */ - public function isInCorrectScope(Node $node, ArgumentAdder $argumentAdder): bool + public function isInCorrectScope(Expr $expr, ArgumentAdder $argumentAdder): bool { if ($argumentAdder->getScope() === null) { return true; } $scope = $argumentAdder->getScope(); - - if ($node instanceof ClassMethod) { - return $scope === self::SCOPE_CLASS_METHOD; - } - - if ($node instanceof StaticCall) { - if (! $node->class instanceof Name) { + if ($expr instanceof StaticCall) { + if (! $expr->class instanceof Name) { return false; } - if ($this->nodeNameResolver->isName($node->class, 'parent')) { + if ($this->nodeNameResolver->isName($expr->class, 'parent')) { return $scope === self::SCOPE_PARENT_CALL; } diff --git a/rules/arguments/src/Rector/ClassMethod/ArgumentAdderRector.php b/rules/arguments/src/Rector/ClassMethod/ArgumentAdderRector.php index 143e2b4ed70b..3f77fae6d35c 100644 --- a/rules/arguments/src/Rector/ClassMethod/ArgumentAdderRector.php +++ b/rules/arguments/src/Rector/ClassMethod/ArgumentAdderRector.php @@ -212,6 +212,7 @@ private function shouldSkipParameter(Node $node, ArgumentAdder $argumentAdder): return $this->isName($node->params[$position], $argumentName); } + // already added? if (! isset($node->args[$position])) { // is correct scope? diff --git a/rules/arguments/src/Rector/ClassMethod/ArgumentDefaultValueReplacerRector.php b/rules/arguments/src/Rector/ClassMethod/ArgumentDefaultValueReplacerRector.php index c21712ed860a..41674d517e02 100644 --- a/rules/arguments/src/Rector/ClassMethod/ArgumentDefaultValueReplacerRector.php +++ b/rules/arguments/src/Rector/ClassMethod/ArgumentDefaultValueReplacerRector.php @@ -8,6 +8,7 @@ use PhpParser\BuilderHelpers; use PhpParser\Node; use PhpParser\Node\Arg; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\ClassMethod; @@ -120,23 +121,23 @@ private function processReplaces(Node $node, ArgumentDefaultValueReplacer $argum } /** - * @param MethodCall|StaticCall $node + * @param MethodCall|StaticCall $expr */ - private function processArgs(Node $node, ArgumentDefaultValueReplacer $argumentDefaultValueReplacer): void + private function processArgs(Expr $expr, ArgumentDefaultValueReplacer $argumentDefaultValueReplacer): void { $position = $argumentDefaultValueReplacer->getPosition(); - $argValue = $this->valueResolver->getValue($node->args[$position]->value); + $argValue = $this->valueResolver->getValue($expr->args[$position]->value); if (is_scalar( $argumentDefaultValueReplacer->getValueBefore() ) && $argValue === $argumentDefaultValueReplacer->getValueBefore()) { - $node->args[$position] = $this->normalizeValueToArgument($argumentDefaultValueReplacer->getValueAfter()); + $expr->args[$position] = $this->normalizeValueToArgument($argumentDefaultValueReplacer->getValueAfter()); } elseif (is_array($argumentDefaultValueReplacer->getValueBefore())) { - $newArgs = $this->processArrayReplacement($node->args, $argumentDefaultValueReplacer); + $newArgs = $this->processArrayReplacement($expr->args, $argumentDefaultValueReplacer); if ($newArgs) { - $node->args = $newArgs; + $expr->args = $newArgs; } } } diff --git a/rules/autodiscovery/src/Analyzer/ValueObjectClassAnalyzer.php b/rules/autodiscovery/src/Analyzer/ValueObjectClassAnalyzer.php index f2a1799878cf..466fab3fb382 100644 --- a/rules/autodiscovery/src/Analyzer/ValueObjectClassAnalyzer.php +++ b/rules/autodiscovery/src/Analyzer/ValueObjectClassAnalyzer.php @@ -17,7 +17,7 @@ final class ValueObjectClassAnalyzer { /** - * @var bool[] + * @var array */ private $valueObjectStatusByClassName = []; @@ -98,7 +98,7 @@ public function isValueObjectClass(Class_ $class): bool return true; } - private function analyseWithoutConstructor(Class_ $class, ?string $className): bool + private function analyseWithoutConstructor(Class_ $class, string $className): bool { // A. has all properties with serialize? if ($this->hasAllPropertiesWithSerialize($class)) { diff --git a/rules/code-quality-strict/src/Rector/Variable/MoveVariableDeclarationNearReferenceRector.php b/rules/code-quality-strict/src/Rector/Variable/MoveVariableDeclarationNearReferenceRector.php index 94cb540d604f..a0baecba2656 100644 --- a/rules/code-quality-strict/src/Rector/Variable/MoveVariableDeclarationNearReferenceRector.php +++ b/rules/code-quality-strict/src/Rector/Variable/MoveVariableDeclarationNearReferenceRector.php @@ -309,7 +309,7 @@ private function getCountFound(Node $node, Variable $variable): int /** * @param array $multiNodes */ - private function getSameVarName(array $multiNodes, Node $node): ?Variable + private function getSameVarName(array $multiNodes, Variable $variable): ?Variable { foreach ($multiNodes as $multiNode) { if ($multiNode === null) { @@ -317,12 +317,12 @@ private function getSameVarName(array $multiNodes, Node $node): ?Variable } /** @var Variable|null $found */ - $found = $this->betterNodeFinder->findFirst($multiNode, function (Node $n) use ($node): bool { + $found = $this->betterNodeFinder->findFirst($multiNode, function (Node $n) use ($variable): bool { $n = $this->mayBeArrayDimFetch($n); if (! $n instanceof Variable) { return false; } - return $this->isName($n, (string) $this->getName($node)); + return $this->isName($n, (string) $this->getName($variable)); }); if ($found !== null) { diff --git a/rules/code-quality/src/NodeAnalyzer/ClassLikeAnalyzer.php b/rules/code-quality/src/NodeAnalyzer/ClassLikeAnalyzer.php index 7333a1858f6d..d816f27e8e00 100644 --- a/rules/code-quality/src/NodeAnalyzer/ClassLikeAnalyzer.php +++ b/rules/code-quality/src/NodeAnalyzer/ClassLikeAnalyzer.php @@ -4,7 +4,7 @@ namespace Rector\CodeQuality\NodeAnalyzer; -use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\Class_; use Rector\NodeNameResolver\NodeNameResolver; final class ClassLikeAnalyzer @@ -22,11 +22,11 @@ public function __construct(NodeNameResolver $nodeNameResolver) /** * @return string[] */ - public function resolvePropertyNames(ClassLike $classLike): array + public function resolvePropertyNames(Class_ $class): array { $propertyNames = []; - foreach ($classLike->getProperties() as $property) { + foreach ($class->getProperties() as $property) { $propertyNames[] = $this->nodeNameResolver->getName($property); } diff --git a/rules/code-quality/src/NodeAnalyzer/ForAnalyzer.php b/rules/code-quality/src/NodeAnalyzer/ForAnalyzer.php index c4d24b5193c8..ec839166dbe3 100644 --- a/rules/code-quality/src/NodeAnalyzer/ForAnalyzer.php +++ b/rules/code-quality/src/NodeAnalyzer/ForAnalyzer.php @@ -16,7 +16,6 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\For_; use PhpParser\Node\Stmt\Unset_; -use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\NodeManipulator\AssignManipulator; use Rector\Core\PhpParser\Comparing\NodeComparator; use Rector\Core\PhpParser\Node\BetterNodeFinder; @@ -137,7 +136,7 @@ function (Node $node): bool { ); } - public function isAssignmentWithArrayDimFetchAsVariableInsideForStatements(For_ $for, ?string $keyValueName): bool + public function isAssignmentWithArrayDimFetchAsVariableInsideForStatements(For_ $for, string $keyValueName): bool { return (bool) $this->betterNodeFinder->findFirst( $for->stmts, @@ -150,10 +149,6 @@ function (Node $node) use ($keyValueName): bool { return false; } - if ($keyValueName === null) { - throw new ShouldNotHappenException(); - } - $arrayDimFetch = $node->var; if ($arrayDimFetch->dim === null) { return false; diff --git a/rules/code-quality/src/NodeFactory/AnonymousFunctionFactory.php b/rules/code-quality/src/NodeFactory/AnonymousFunctionFactory.php index fe264e970132..16be3d73ff64 100644 --- a/rules/code-quality/src/NodeFactory/AnonymousFunctionFactory.php +++ b/rules/code-quality/src/NodeFactory/AnonymousFunctionFactory.php @@ -4,7 +4,7 @@ namespace Rector\CodeQuality\NodeFactory; -use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\ClosureUse; use PhpParser\Node\Expr\MethodCall; @@ -50,9 +50,9 @@ public function __construct( } /** - * @param Variable|PropertyFetch $node + * @param Variable|PropertyFetch $expr */ - public function create(PhpMethodReflection $phpMethodReflection, Node $node): Closure + public function create(PhpMethodReflection $phpMethodReflection, Expr $expr): Closure { /** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */ $functionVariantWithPhpDoc = $phpMethodReflection->getVariants()[0]; @@ -62,7 +62,7 @@ public function create(PhpMethodReflection $phpMethodReflection, Node $node): Cl $anonymousFunction->params = $newParams; - $innerMethodCall = new MethodCall($node, $phpMethodReflection->getName()); + $innerMethodCall = new MethodCall($expr, $phpMethodReflection->getName()); $innerMethodCall->args = $this->nodeFactory->createArgsFromParams($newParams); if (! $functionVariantWithPhpDoc->getReturnType() instanceof MixedType) { @@ -80,8 +80,8 @@ public function create(PhpMethodReflection $phpMethodReflection, Node $node): Cl $anonymousFunction->stmts[] = new Expression($innerMethodCall); } - if ($node instanceof Variable && ! $this->nodeNameResolver->isName($node, 'this')) { - $anonymousFunction->uses[] = new ClosureUse($node); + if ($expr instanceof Variable && ! $this->nodeNameResolver->isName($expr, 'this')) { + $anonymousFunction->uses[] = new ClosureUse($expr); } return $anonymousFunction; diff --git a/rules/code-quality/src/Rector/Assign/SplitListAssignToSeparateLineRector.php b/rules/code-quality/src/Rector/Assign/SplitListAssignToSeparateLineRector.php index fcdc37a610f8..481b986925db 100644 --- a/rules/code-quality/src/Rector/Assign/SplitListAssignToSeparateLineRector.php +++ b/rules/code-quality/src/Rector/Assign/SplitListAssignToSeparateLineRector.php @@ -101,13 +101,13 @@ private function shouldSkip(Assign $assign): bool } /** - * @param Array_|List_ $node + * @param Array_|List_ $expr * @return Assign[] */ - private function createStandaloneAssigns(Node $node, Array_ $rightArray): array + private function createStandaloneAssigns(Expr $expr, Array_ $rightArray): array { $standaloneAssigns = []; - foreach ($node->items as $key => $leftArrayItem) { + foreach ($expr->items as $key => $leftArrayItem) { if ($leftArrayItem === null) { continue; } diff --git a/rules/coding-style/src/ClassNameImport/ShortNameResolver.php b/rules/coding-style/src/ClassNameImport/ShortNameResolver.php index fe281a9c3569..743dbf6d3143 100644 --- a/rules/coding-style/src/ClassNameImport/ShortNameResolver.php +++ b/rules/coding-style/src/ClassNameImport/ShortNameResolver.php @@ -180,7 +180,7 @@ private function resolveForStmts(array $stmts): array /** * @param Node[] $stmts - * @return string[] + * @return array */ private function resolveFromDocBlocks(array $stmts): array { @@ -192,6 +192,7 @@ private function resolveFromDocBlocks(array $stmts): array $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) { + /** @var PhpDocChildNode $phpDocChildNode */ $shortTagName = $this->resolveShortTagNameFromPhpDocChildNode($phpDocChildNode); if ($shortTagName === null) { continue; diff --git a/rules/downgrade-php74/src/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php b/rules/downgrade-php74/src/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php index c32eb7a44d01..2bd7703cdc1c 100644 --- a/rules/downgrade-php74/src/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php +++ b/rules/downgrade-php74/src/Rector/ClassMethod/DowngradeContravariantArgumentTypeRector.php @@ -95,7 +95,23 @@ public function contraVariantArguments($type) ]); } - public function isNullableParam(Param $param, FunctionLike $functionLike): bool + /** + * @param ClassMethod|Function_ $node + */ + public function refactor(Node $node): ?Node + { + if ($node->params === []) { + return null; + } + + foreach ($node->params as $param) { + $this->refactorParam($param, $node); + } + + return null; + } + + private function isNullableParam(Param $param, FunctionLike $functionLike): bool { if ($param->variadic) { return false; @@ -119,22 +135,6 @@ public function isNullableParam(Param $param, FunctionLike $functionLike): bool return $this->getDifferentParamTypeFromAncestorClass($param, $functionLike) !== null; } - /** - * @param ClassMethod|Function_ $node - */ - public function refactor(Node $node): ?Node - { - if ($node->params === []) { - return null; - } - - foreach ($node->params as $param) { - $this->refactorParam($param, $node); - } - - return null; - } - private function getDifferentParamTypeFromAncestorClass(Param $param, FunctionLike $functionLike): ?string { $scope = $functionLike->getAttribute(AttributeKey::SCOPE); diff --git a/rules/type-declaration/src/NodeAnalyzer/CallTypesResolver.php b/rules/type-declaration/src/NodeAnalyzer/CallTypesResolver.php new file mode 100644 index 000000000000..23d4a4ede3d1 --- /dev/null +++ b/rules/type-declaration/src/NodeAnalyzer/CallTypesResolver.php @@ -0,0 +1,163 @@ +nodeTypeResolver = $nodeTypeResolver; + $this->typeFactory = $typeFactory; + } + + /** + * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls + * @return Type[] + */ + public function resolveStrictTypesFromCalls(array $calls): array + { + return $this->resolveTypesFromCalls($calls, self::STRICTNESS_TYPE_DECLARATION); + } + + /** + * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls + * @return Type[] + */ + public function resolveWeakTypesFromCalls(array $calls): array + { + return $this->resolveTypesFromCalls($calls, self::STRICTNESS_DOCBLOCK); + } + + /** + * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls + * @return Type[] + */ + private function resolveTypesFromCalls(array $calls, string $strictnessLevel): array + { + $staticTypesByArgumentPosition = []; + + foreach ($calls as $call) { + if (! $call instanceof StaticCall && ! $call instanceof MethodCall) { + continue; + } + + foreach ($call->args as $position => $arg) { + $argValueType = $this->resolveArgValueType($strictnessLevel, $arg); + $staticTypesByArgumentPosition[$position][] = $argValueType; + } + } + + // unite to single type + return $this->unionToSingleType($staticTypesByArgumentPosition); + } + + private function resolveArgValueType(string $strictnessLevel, Arg $arg): Type + { + if ($strictnessLevel === self::STRICTNESS_TYPE_DECLARATION) { + $argValueType = $this->nodeTypeResolver->getNativeType($arg->value); + } else { + $argValueType = $this->nodeTypeResolver->resolve($arg->value); + } + + // "self" in another object is not correct, this make it independent + return $this->correctSelfType($argValueType); + } + + private function correctSelfType(Type $argValueType): Type + { + if ($argValueType instanceof ThisType) { + return new ObjectType($argValueType->getClassName()); + } + + return $argValueType; + } + + /** + * @param array $staticTypesByArgumentPosition + * @return array + */ + private function unionToSingleType(array $staticTypesByArgumentPosition): array + { + $staticTypeByArgumentPosition = []; + foreach ($staticTypesByArgumentPosition as $position => $staticTypes) { + $unionedType = $this->typeFactory->createMixedPassedOrUnionType($staticTypes); + + // narrow parents to most child type + $unionedType = $this->narrowParentObjectTreeToSingleObjectChildType($unionedType); + $staticTypeByArgumentPosition[$position] = $unionedType; + } + + return $staticTypeByArgumentPosition; + } + + private function narrowParentObjectTreeToSingleObjectChildType(Type $type): Type + { + if (! $type instanceof UnionType) { + return $type; + } + + if (! $this->isTypeWithClassNameOnly($type)) { + return $type; + } + + /** @var TypeWithClassName $firstUnionedType */ + $firstUnionedType = $type->getTypes()[0]; + + foreach ($type->getTypes() as $unionedType) { + if (! $unionedType instanceof TypeWithClassName) { + return $type; + } + + if (! is_a($firstUnionedType->getClassName(), $unionedType->getClassName(), true)) { + return $type; + } + } + + return $firstUnionedType; + } + + private function isTypeWithClassNameOnly(UnionType $unionType): bool + { + foreach ($unionType->getTypes() as $unionedType) { + if (! $unionedType instanceof TypeWithClassName) { + return false; + } + } + + return true; + } +} diff --git a/rules/type-declaration/src/NodeAnalyzer/ClassMethodParamTypeCompleter.php b/rules/type-declaration/src/NodeAnalyzer/ClassMethodParamTypeCompleter.php new file mode 100644 index 000000000000..7ae27768d607 --- /dev/null +++ b/rules/type-declaration/src/NodeAnalyzer/ClassMethodParamTypeCompleter.php @@ -0,0 +1,88 @@ +staticTypeMapper = $staticTypeMapper; + $this->classMethodParamVendorLockResolver = $classMethodParamVendorLockResolver; + } + + /** + * @param array $classParameterTypes + */ + public function complete(ClassMethod $classMethod, array $classParameterTypes): ?ClassMethod + { + $hasChanged = false; + + foreach ($classParameterTypes as $position => $argumentStaticType) { + if ($this->shouldSkipArgumentStaticType($classMethod, $argumentStaticType, $position)) { + continue; + } + + $phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($argumentStaticType); + if ($phpParserTypeNode === null) { + continue; + } + + // update parameter + $classMethod->params[$position]->type = $phpParserTypeNode; + $hasChanged = true; + } + + if ($hasChanged) { + return $classMethod; + } + + return null; + } + + private function shouldSkipArgumentStaticType( + ClassMethod $classMethod, + Type $argumentStaticType, + int $position + ): bool { + if ($argumentStaticType instanceof MixedType) { + return true; + } + + if (! isset($classMethod->params[$position])) { + return true; + } + + if ($this->classMethodParamVendorLockResolver->isVendorLocked($classMethod, $position)) { + return true; + } + + $parameter = $classMethod->params[$position]; + if ($parameter->type === null) { + return false; + } + + $parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type); + // already completed → skip + return $parameterStaticType->equals($argumentStaticType); + } +} diff --git a/rules/type-declaration/src/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php b/rules/type-declaration/src/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php index 42c724296952..0113f269029a 100644 --- a/rules/type-declaration/src/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php +++ b/rules/type-declaration/src/Rector/ClassMethod/AddMethodCallBasedStrictParamTypeRector.php @@ -5,16 +5,10 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; -use PhpParser\Node\Expr\MethodCall; -use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Stmt\ClassMethod; -use PHPStan\Type\MixedType; -use PHPStan\Type\ObjectType; -use PHPStan\Type\ThisType; -use PHPStan\Type\Type; use Rector\Core\Rector\AbstractRector; -use Rector\NodeCollector\ValueObject\ArrayCallable; -use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; +use Rector\TypeDeclaration\NodeAnalyzer\CallTypesResolver; +use Rector\TypeDeclaration\NodeAnalyzer\ClassMethodParamTypeCompleter; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -26,13 +20,21 @@ final class AddMethodCallBasedStrictParamTypeRector extends AbstractRector { /** - * @var TypeFactory + * @var CallTypesResolver */ - private $typeFactory; + private $callTypesResolver; - public function __construct(TypeFactory $typeFactory) - { - $this->typeFactory = $typeFactory; + /** + * @var ClassMethodParamTypeCompleter + */ + private $classMethodParamTypeCompleter; + + public function __construct( + CallTypesResolver $callTypesResolver, + ClassMethodParamTypeCompleter $classMethodParamTypeCompleter + ) { + $this->callTypesResolver = $callTypesResolver; + $this->classMethodParamTypeCompleter = $classMethodParamTypeCompleter; } public function getRuleDefinition(): RuleDefinition @@ -99,86 +101,13 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $classMethodCalls = $this->nodeRepository->findCallsByClassMethod($node); - $classParameterTypes = $this->getCallTypesByPosition($classMethodCalls); - - foreach ($classParameterTypes as $position => $argumentStaticType) { - if ($this->shouldSkipArgumentStaticType($node, $argumentStaticType, $position)) { - continue; - } - - $phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($argumentStaticType); - if ($phpParserTypeNode === null) { - continue; - } - - // update parameter - $node->params[$position]->type = $phpParserTypeNode; - } - - return $node; - } - - /** - * @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls - * @return Type[] - */ - private function getCallTypesByPosition(array $calls): array - { - $staticTypesByArgumentPosition = []; - - foreach ($calls as $call) { - if (! $call instanceof StaticCall && ! $call instanceof MethodCall) { - continue; - } - - foreach ($call->args as $position => $arg) { - $argValueType = $this->nodeTypeResolver->getNativeType($arg->value); - - // "self" in another object is not correct, this make it independent - $argValueType = $this->correctSelfType($argValueType); - $staticTypesByArgumentPosition[$position][] = $argValueType; - } - } - - // unite to single type - $staticTypeByArgumentPosition = []; - foreach ($staticTypesByArgumentPosition as $position => $staticTypes) { - $staticTypeByArgumentPosition[$position] = $this->typeFactory->createMixedPassedOrUnionType($staticTypes); - } - - return $staticTypeByArgumentPosition; - } - - private function shouldSkipArgumentStaticType( - ClassMethod $classMethod, - Type $argumentStaticType, - int $position - ): bool { - if ($argumentStaticType instanceof MixedType) { - return true; - } - - if (! isset($classMethod->params[$position])) { - return true; - } - - $parameter = $classMethod->params[$position]; - if ($parameter->type === null) { - return false; + if ($node->params === []) { + return null; } - $parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type); - // already completed → skip - return $parameterStaticType->equals($argumentStaticType); - } - - private function correctSelfType(Type $argValueType): Type - { - if ($argValueType instanceof ThisType) { - return new ObjectType($argValueType->getClassName()); - } + $classMethodCalls = $this->nodeRepository->findCallsByClassMethod($node); + $classMethodParameterTypes = $this->callTypesResolver->resolveStrictTypesFromCalls($classMethodCalls); - return $argValueType; + return $this->classMethodParamTypeCompleter->complete($node, $classMethodParameterTypes); } } diff --git a/rules/type-declaration/src/Rector/ClassMethod/AddParamTypeFromCallersRector.php b/rules/type-declaration/src/Rector/ClassMethod/AddParamTypeFromCallersRector.php new file mode 100644 index 000000000000..8faac6187cae --- /dev/null +++ b/rules/type-declaration/src/Rector/ClassMethod/AddParamTypeFromCallersRector.php @@ -0,0 +1,105 @@ +callTypesResolver = $callTypesResolver; + $this->classMethodParamTypeCompleter = $classMethodParamTypeCompleter; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Add param type based on called types in that particular method', [ + new CodeSample( + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function run(Return_ $return) + { + $this->print($return); + } + + public function print($return) + { + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +final class SomeClass +{ + public function run(Return_ $return) + { + $this->print($return); + } + + public function print(Return_ $return) + { + } +} +CODE_SAMPLE + + ), + ]); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if ($node->params === []) { + return null; + } + + $calls = $this->nodeRepository->findCallsByClassMethod($node); + if ($calls === []) { + return null; + } + + $classMethodParameterTypes = $this->callTypesResolver->resolveWeakTypesFromCalls($calls); + return $this->classMethodParamTypeCompleter->complete($node, $classMethodParameterTypes); + } +} diff --git a/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/AddParamTypeFromCallersRectorTest.php b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/AddParamTypeFromCallersRectorTest.php new file mode 100644 index 000000000000..4a4352ecb229 --- /dev/null +++ b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/AddParamTypeFromCallersRectorTest.php @@ -0,0 +1,30 @@ +doTestFileInfo($fileInfo); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + protected function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_by_interface.php.inc b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_by_interface.php.inc new file mode 100644 index 000000000000..a6ecac606c51 --- /dev/null +++ b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_by_interface.php.inc @@ -0,0 +1,18 @@ +print($return); + } + + public function print(\PhpParser\Node $return) + { + } +} diff --git a/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_function_like.php.inc b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_function_like.php.inc new file mode 100644 index 000000000000..1515802d1405 --- /dev/null +++ b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/skip_function_like.php.inc @@ -0,0 +1,22 @@ +print($functionLike); + } + + public function print(\PhpParser\Node\FunctionLike $functionLike) + { + } +} diff --git a/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/some_class.php.inc b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/some_class.php.inc new file mode 100644 index 000000000000..dd37541b71b8 --- /dev/null +++ b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Fixture/some_class.php.inc @@ -0,0 +1,39 @@ +print($return); + } + + public function print($return) + { + } +} + +?> +----- +print($return); + } + + public function print(\PhpParser\Node\Stmt\Return_ $return) + { + } +} + +?> diff --git a/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Source/SomeInterface.php b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Source/SomeInterface.php new file mode 100644 index 000000000000..b27a9479a48d --- /dev/null +++ b/rules/type-declaration/tests/Rector/ClassMethod/AddParamTypeFromCallersRector/Source/SomeInterface.php @@ -0,0 +1,12 @@ +parameters(); + $parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1); + + $services = $containerConfigurator->services(); + $services->set(AddParamTypeFromCallersRector::class); +}; diff --git a/src/NodeAnalyzer/CallAnalyzer.php b/src/NodeAnalyzer/CallAnalyzer.php index ce7a4def1eeb..1be9e723b58d 100644 --- a/src/NodeAnalyzer/CallAnalyzer.php +++ b/src/NodeAnalyzer/CallAnalyzer.php @@ -4,7 +4,6 @@ namespace Rector\Core\NodeAnalyzer; -use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BooleanNot; @@ -19,21 +18,21 @@ final class CallAnalyzer */ private const OBJECT_CALLS = [MethodCall::class, NullsafeMethodCall::class, StaticCall::class]; - public function isObjectCall(Node $node): bool + public function isObjectCall(Expr $expr): bool { - if ($node instanceof BooleanNot) { - $node = $node->expr; + if ($expr instanceof BooleanNot) { + $expr = $expr->expr; } - if ($node instanceof BinaryOp) { - $isObjectCallLeft = $this->isObjectCall($node->left); - $isObjectCallRight = $this->isObjectCall($node->right); + if ($expr instanceof BinaryOp) { + $isObjectCallLeft = $this->isObjectCall($expr->left); + $isObjectCallRight = $this->isObjectCall($expr->right); return $isObjectCallLeft || $isObjectCallRight; } foreach (self::OBJECT_CALLS as $objectCall) { - if (is_a($node, $objectCall, true)) { + if (is_a($expr, $objectCall, true)) { return true; } }