mirror of
https://github.com/rectorphp/rector.git
synced 2024-09-04 22:52:16 +00:00
[Downgrade PHP 7.2] Improve covariant (#6207)
This commit is contained in:
parent
91377902b2
commit
e425c11d8d
29
.github/workflows/tests_debug.yaml
vendored
Normal file
29
.github/workflows/tests_debug.yaml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Tests Debug
|
||||
|
||||
on:
|
||||
pull_request: null
|
||||
|
||||
env:
|
||||
# see https://github.com/composer/composer/issues/9368#issuecomment-718112361
|
||||
COMPOSER_ROOT_VERSION: "dev-main"
|
||||
|
||||
jobs:
|
||||
tests_debug:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: PHP ${{ matrix.php }} tests for ${{ matrix.path }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.4
|
||||
coverage: none
|
||||
|
||||
- uses: "ramsey/composer-install@v1"
|
||||
|
||||
# this should pass :)
|
||||
- run: vendor/bin/phpunit rules-tests/DowngradePhp72/Rector/Class_/DowngradeParameterTypeWideningRector/DowngradeParameterTypeWideningRectorTest.php --filter test#4-7
|
||||
|
||||
- run: vendor/bin/phpunit rules-tests/DowngradePhp72/Rector/Class_/DowngradeParameterTypeWideningRector/DowngradeParameterTypeWideningRectorTest.php
|
@ -7,14 +7,14 @@ interface SomeContainerInterface
|
||||
public function has(string $id);
|
||||
}
|
||||
|
||||
class SomeContainer implements SomeContainerInterface
|
||||
final class SomeContainerBuilder extends SomeUniqueContainer
|
||||
{
|
||||
public function has($id)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
final class SomeContainerBuilder extends SomeContainer
|
||||
class SomeUniqueContainer implements SomeContainerInterface
|
||||
{
|
||||
public function has($id)
|
||||
{
|
||||
@ -35,14 +35,14 @@ interface SomeContainerInterface
|
||||
public function has($id);
|
||||
}
|
||||
|
||||
class SomeContainer implements SomeContainerInterface
|
||||
final class SomeContainerBuilder extends SomeUniqueContainer
|
||||
{
|
||||
public function has($id)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
final class SomeContainerBuilder extends SomeContainer
|
||||
class SomeUniqueContainer implements SomeContainerInterface
|
||||
{
|
||||
public function has($id)
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ class B implements A
|
||||
}
|
||||
}
|
||||
|
||||
class C implements A
|
||||
final class MostChild implements A
|
||||
{
|
||||
public function test(array $input)
|
||||
{
|
||||
@ -42,7 +42,7 @@ class B implements A
|
||||
}
|
||||
}
|
||||
|
||||
class C implements A
|
||||
final class MostChild implements A
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $input
|
||||
|
@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\DowngradePhp72\Tests\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
trait StreamDecoratorTrait
|
||||
{
|
||||
protected function createStream()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartStream
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
protected function createStream(array $elements)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
?>
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
final class SomeContainer implements ContainerInterface
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
final class SomeContainer implements AnyContainerInterface
|
||||
{
|
||||
use ServiceLocatorTrait;
|
||||
}
|
||||
@ -12,7 +14,7 @@ trait ServiceLocatorTrait
|
||||
}
|
||||
}
|
||||
|
||||
interface ContainerInterface
|
||||
interface AnyContainerInterface
|
||||
{
|
||||
public function has(string $name);
|
||||
}
|
||||
@ -21,21 +23,29 @@ interface ContainerInterface
|
||||
-----
|
||||
<?php
|
||||
|
||||
final class SomeContainer implements ContainerInterface
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
final class SomeContainer implements AnyContainerInterface
|
||||
{
|
||||
use ServiceLocatorTrait;
|
||||
}
|
||||
|
||||
trait ServiceLocatorTrait
|
||||
{
|
||||
public function has(string $name)
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
interface ContainerInterface
|
||||
interface AnyContainerInterface
|
||||
{
|
||||
public function has(string $name);
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function has($name);
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
final class AnotherContainer implements AnotherContainerInterface
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
final class YetAnotherContainer implements AnotherContainerInterface
|
||||
{
|
||||
use AnotherServiceLocatorTrait;
|
||||
}
|
||||
@ -21,7 +23,9 @@ interface AnotherContainerInterface
|
||||
-----
|
||||
<?php
|
||||
|
||||
final class AnotherContainer implements AnotherContainerInterface
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
final class YetAnotherContainer implements AnotherContainerInterface
|
||||
{
|
||||
use AnotherServiceLocatorTrait;
|
||||
}
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
interface SomeInterface
|
||||
interface WhateverInterface
|
||||
{
|
||||
public function test(string $input);
|
||||
}
|
||||
|
||||
abstract class AbstractSomeAncestorClass implements SomeInterface
|
||||
abstract class AbstractSomeAncestorClass implements WhateverInterface
|
||||
{
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ class SomeChildClass extends AbstractSomeAncestorClass
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
interface SomeInterface
|
||||
interface WhateverInterface
|
||||
{
|
||||
/**
|
||||
* @param string $input
|
||||
@ -33,7 +33,7 @@ interface SomeInterface
|
||||
public function test($input);
|
||||
}
|
||||
|
||||
abstract class AbstractSomeAncestorClass implements SomeInterface
|
||||
abstract class AbstractSomeAncestorClass implements WhateverInterface
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\SomeContainerInterface;
|
||||
use Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source\SomeExternalContainerInterface;
|
||||
|
||||
class KeepInterfaceDonwgraded implements SomeContainerInterface
|
||||
class KeepInterfaceDonwgraded implements SomeExternalContainerInterface
|
||||
{
|
||||
public function get($id)
|
||||
{
|
||||
|
@ -2,14 +2,40 @@
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
interface SkipNothingHappens
|
||||
interface JustToBeSure
|
||||
{
|
||||
public function test(array $input);
|
||||
}
|
||||
|
||||
final class SkipNothingHappensClass implements SkipNothingHappens
|
||||
final class UpdateBoth implements JustToBeSure
|
||||
{
|
||||
public function test(array $input)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
interface JustToBeSure
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $input
|
||||
*/
|
||||
public function test($input);
|
||||
}
|
||||
|
||||
final class UpdateBoth implements JustToBeSure
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $input
|
||||
*/
|
||||
public function test($input)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
@ -8,10 +8,6 @@ use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface;
|
||||
|
||||
final class SkipRequiredRemoval implements ParamTypeInfererInterface
|
||||
{
|
||||
public function autowireSomething(\PHPStan\Type\Type $input)
|
||||
{
|
||||
}
|
||||
|
||||
public function inferParam(Param $param): Type
|
||||
{
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
trait StreamDecoratorTrait
|
||||
{
|
||||
protected function createStream()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartStream
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
protected function createStream(array $elements)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Fixture;
|
||||
|
||||
trait StreamDecoratorTrait
|
||||
{
|
||||
protected function createStream()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartStream
|
||||
{
|
||||
use StreamDecoratorTrait;
|
||||
|
||||
/**
|
||||
* @param mixed[] $elements
|
||||
*/
|
||||
protected function createStream($elements)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\Class_\DowngradeParameterTypeWideningRector\Source;
|
||||
|
||||
interface SomeContainerInterface
|
||||
interface SomeExternalContainerInterface
|
||||
{
|
||||
public function get(string $id);
|
||||
}
|
@ -5,8 +5,8 @@ declare(strict_types=1);
|
||||
namespace Rector\DowngradePhp72\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
@ -29,23 +29,24 @@ final class ClassLikeWithTraitsClassMethodResolver
|
||||
*/
|
||||
public function resolve(Class_ $class): array
|
||||
{
|
||||
$classMethods = $class->getMethods();
|
||||
|
||||
$scope = $class->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return $classMethods;
|
||||
return [];
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return $classMethods;
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($classReflection->getTraits() as $traitClassReflection) {
|
||||
$trait = $this->nodeRepository->findTrait($traitClassReflection->getName());
|
||||
if ($trait instanceof Trait_) {
|
||||
$classMethods = array_merge($classMethods, $trait->getMethods());
|
||||
$classMethods = [];
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
|
||||
$classLike = $this->nodeRepository->findClassLike($ancestorClassReflection->getName());
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classMethods = array_merge($classMethods, $classLike->getMethods());
|
||||
}
|
||||
|
||||
return $classMethods;
|
||||
|
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DowngradePhp72\NodeAnalyzer;
|
||||
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class ParentChildClassMethodTypeResolver
|
||||
{
|
||||
/**
|
||||
* @var NativeTypeClassTreeResolver
|
||||
*/
|
||||
private $nativeTypeClassTreeResolver;
|
||||
|
||||
public function __construct(NativeTypeClassTreeResolver $nativeTypeClassTreeResolver)
|
||||
{
|
||||
$this->nativeTypeClassTreeResolver = $nativeTypeClassTreeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string, Type>
|
||||
*/
|
||||
public function resolve(
|
||||
ClassReflection $classReflection,
|
||||
string $methodName,
|
||||
int $position,
|
||||
Scope $scope
|
||||
): array {
|
||||
$parameterTypesByClassName = [];
|
||||
|
||||
// include types of class scope in case of trait
|
||||
if ($classReflection->isTrait()) {
|
||||
$parameterTypesByInterfaceName = $this->resolveInterfaceTypeByClassName($scope, $methodName, $position);
|
||||
$parameterTypesByClassName = array_merge($parameterTypesByClassName, $parameterTypesByInterfaceName);
|
||||
}
|
||||
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
|
||||
if (! $ancestorClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameterType = $this->nativeTypeClassTreeResolver->resolveParameterReflectionType(
|
||||
$ancestorClassReflection,
|
||||
$methodName,
|
||||
$position
|
||||
);
|
||||
|
||||
$parameterTypesByClassName[$ancestorClassReflection->getName()] = $parameterType;
|
||||
}
|
||||
|
||||
return $parameterTypesByClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string, Type>
|
||||
*/
|
||||
private function resolveInterfaceTypeByClassName(Scope $scope, string $methodName, int $position): array
|
||||
{
|
||||
$typesByClassName = [];
|
||||
|
||||
$currentClassReflection = $scope->getClassReflection();
|
||||
if (! $currentClassReflection instanceof ClassReflection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($currentClassReflection->getInterfaces() as $interfaceClassReflection) {
|
||||
if (! $interfaceClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameterType = $this->nativeTypeClassTreeResolver->resolveParameterReflectionType(
|
||||
$interfaceClassReflection,
|
||||
$methodName,
|
||||
$position
|
||||
);
|
||||
|
||||
$typesByClassName[$interfaceClassReflection->getName()] = $parameterType;
|
||||
}
|
||||
|
||||
return $typesByClassName;
|
||||
}
|
||||
}
|
60
rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php
Normal file
60
rules/DowngradePhp72/PhpDoc/NativeParamToPhpDocDecorator.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DowngradePhp72\PhpDoc;
|
||||
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
|
||||
final class NativeParamToPhpDocDecorator
|
||||
{
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var PhpDocTypeChanger
|
||||
*/
|
||||
private $phpDocTypeChanger;
|
||||
|
||||
public function __construct(
|
||||
PhpDocInfoFactory $phpDocInfoFactory,
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
PhpDocTypeChanger $phpDocTypeChanger
|
||||
) {
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
}
|
||||
|
||||
public function decorate(ClassMethod $classMethod, Param $param): void
|
||||
{
|
||||
if ($param->type === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
|
||||
|
||||
$paramName = $this->nodeNameResolver->getName($param);
|
||||
$mappedCurrentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
|
||||
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $mappedCurrentParamType, $param, $paramName);
|
||||
}
|
||||
}
|
@ -11,15 +11,15 @@ use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\ChangesReporting\ValueObject\RectorWithLineChange;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\Application\File;
|
||||
use Rector\DowngradePhp72\NodeAnalyzer\ClassLikeWithTraitsClassMethodResolver;
|
||||
use Rector\DowngradePhp72\NodeAnalyzer\NativeTypeClassTreeResolver;
|
||||
use Rector\DowngradePhp72\NodeAnalyzer\ParentChildClassMethodTypeResolver;
|
||||
use Rector\DowngradePhp72\PhpDoc\NativeParamToPhpDocDecorator;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
@ -31,36 +31,36 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
*/
|
||||
final class DowngradeParameterTypeWideningRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var PhpDocTypeChanger
|
||||
*/
|
||||
private $phpDocTypeChanger;
|
||||
|
||||
/**
|
||||
* @var NativeTypeClassTreeResolver
|
||||
*/
|
||||
private $nativeTypeClassTreeResolver;
|
||||
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
/**
|
||||
* @var ClassLikeWithTraitsClassMethodResolver
|
||||
*/
|
||||
private $classLikeWithTraitsClassMethodResolver;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var ParentChildClassMethodTypeResolver
|
||||
*/
|
||||
private $parentChildClassMethodTypeResolver;
|
||||
|
||||
/**
|
||||
* @var NativeParamToPhpDocDecorator
|
||||
*/
|
||||
private $nativeParamToPhpDocDecorator;
|
||||
|
||||
public function __construct(
|
||||
PhpDocTypeChanger $phpDocTypeChanger,
|
||||
NativeTypeClassTreeResolver $nativeTypeClassTreeResolver,
|
||||
TypeFactory $typeFactory,
|
||||
ClassLikeWithTraitsClassMethodResolver $classLikeWithTraitsClassMethodResolver
|
||||
ClassLikeWithTraitsClassMethodResolver $classLikeWithTraitsClassMethodResolver,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
ParentChildClassMethodTypeResolver $parentChildClassMethodTypeResolver,
|
||||
NativeParamToPhpDocDecorator $nativeParamToPhpDocDecorator
|
||||
) {
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
$this->nativeTypeClassTreeResolver = $nativeTypeClassTreeResolver;
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->classLikeWithTraitsClassMethodResolver = $classLikeWithTraitsClassMethodResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->parentChildClassMethodTypeResolver = $parentChildClassMethodTypeResolver;
|
||||
$this->nativeParamToPhpDocDecorator = $nativeParamToPhpDocDecorator;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
@ -109,9 +109,16 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$classMethods = $this->classLikeWithTraitsClassMethodResolver->resolve($node);
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isEmptyClassReflection($scope)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethods = $this->classLikeWithTraitsClassMethodResolver->resolve($node);
|
||||
foreach ($classMethods as $classMethod) {
|
||||
$this->refactorClassMethod($classMethod, $scope);
|
||||
}
|
||||
@ -122,42 +129,50 @@ CODE_SAMPLE
|
||||
/**
|
||||
* The topmost class is the source of truth, so we go only down to avoid up/down collission
|
||||
*/
|
||||
private function refactorParamForSelfAndSiblings(ClassMethod $classMethod, int $position, Scope $classScope): void
|
||||
private function refactorParamForSelfAndSiblings(ClassMethod $classMethod, int $position, Scope $scope): void
|
||||
{
|
||||
$classReflection = $classScope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if ($class === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($classReflection->getAncestors()) === 1) {
|
||||
$className = $this->getName($class);
|
||||
if ($className === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
// Remove the types in:
|
||||
// - all ancestors + their descendant classes
|
||||
// - all implemented interfaces + their implementing classes
|
||||
$parameterTypesByParentClassLikes = $this->resolveParameterTypesByClassLike(
|
||||
$parameterTypesByParentClassLikes = $this->parentChildClassMethodTypeResolver->resolve(
|
||||
$classReflection,
|
||||
$methodName,
|
||||
$position
|
||||
$position,
|
||||
$scope
|
||||
);
|
||||
|
||||
// we need at least 2 methods to have a possible conflict
|
||||
if (count($parameterTypesByParentClassLikes) < 2) {
|
||||
return;
|
||||
// skip classes we cannot change
|
||||
foreach (array_keys($parameterTypesByParentClassLikes) as $className) {
|
||||
$classLike = $this->nodeRepository->findClassLike($className);
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$uniqueParameterTypes = $this->typeFactory->uniquateTypes($parameterTypesByParentClassLikes);
|
||||
|
||||
// we need at least 2 unique types
|
||||
if (count($uniqueParameterTypes) === 1) {
|
||||
// we need at least 2 types = 2 occurances of same method
|
||||
if (count($parameterTypesByParentClassLikes) <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->refactorClassWithAncestorsAndChildren($classReflection, $methodName, $position);
|
||||
$this->refactorParameters($parameterTypesByParentClassLikes, $methodName, $position);
|
||||
}
|
||||
|
||||
private function removeParamTypeFromMethod(
|
||||
@ -184,10 +199,11 @@ CODE_SAMPLE
|
||||
}
|
||||
|
||||
// Add the current type in the PHPDoc
|
||||
$this->addPHPDocParamTypeToMethod($classMethod, $param);
|
||||
$this->nativeParamToPhpDocDecorator->decorate($classMethod, $param);
|
||||
|
||||
// Remove the type
|
||||
$param->type = null;
|
||||
$param->setAttribute(AttributeKey::ORIGINAL_NODE, null);
|
||||
|
||||
// file from another file
|
||||
$file = $param->getAttribute(AttributeKey::FILE);
|
||||
@ -197,111 +213,6 @@ CODE_SAMPLE
|
||||
}
|
||||
}
|
||||
|
||||
private function removeParamTypeFromMethodForChildren(
|
||||
string $parentClassName,
|
||||
string $methodName,
|
||||
int $position
|
||||
): void {
|
||||
$childrenClassLikes = $this->nodeRepository->findClassesAndInterfacesByType($parentClassName);
|
||||
foreach ($childrenClassLikes as $childClassLike) {
|
||||
$childClassName = $childClassLike->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($childClassName === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$childClassMethod = $this->nodeRepository->findClassMethod($childClassName, $methodName);
|
||||
if (! $childClassMethod instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->removeParamTypeFromMethod($childClassLike, $position, $childClassMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the current param type in the PHPDoc
|
||||
*/
|
||||
private function addPHPDocParamTypeToMethod(ClassMethod $classMethod, Param $param): void
|
||||
{
|
||||
if ($param->type === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
|
||||
|
||||
$paramName = $this->getName($param);
|
||||
$mappedCurrentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
|
||||
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $mappedCurrentParamType, $param, $paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string, Type>
|
||||
*/
|
||||
private function resolveParameterTypesByClassLike(
|
||||
ClassReflection $classReflection,
|
||||
string $methodName,
|
||||
int $position
|
||||
): array {
|
||||
$parameterTypesByParentClassLikes = [];
|
||||
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
|
||||
if ($ancestorClassReflection->isTrait()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $ancestorClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameterType = $this->nativeTypeClassTreeResolver->resolveParameterReflectionType(
|
||||
$ancestorClassReflection,
|
||||
$methodName,
|
||||
$position
|
||||
);
|
||||
$parameterTypesByParentClassLikes[$ancestorClassReflection->getName()] = $parameterType;
|
||||
}
|
||||
|
||||
return $parameterTypesByParentClassLikes;
|
||||
}
|
||||
|
||||
private function refactorClassWithAncestorsAndChildren(
|
||||
ClassReflection $classReflection,
|
||||
string $methodName,
|
||||
int $position
|
||||
): void {
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassRelection) {
|
||||
$classLike = $this->nodeRepository->findClassLike($ancestorClassRelection->getName());
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$currentClassMethod = $classLike->getMethod($methodName);
|
||||
if (! $currentClassMethod instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$className = $this->getName($classLike);
|
||||
if ($className === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* If it doesn't find the method, it's because the method
|
||||
* lives somewhere else.
|
||||
* For instance, in test "interface_on_parent_class.php.inc",
|
||||
* the ancestorClassReflection abstract class is also retrieved
|
||||
* as containing the method, but it does not: it is
|
||||
* in its implemented interface. That happens because
|
||||
* `ReflectionMethod` doesn't allow to do do the distinction.
|
||||
* The interface is also retrieve though, so that method
|
||||
* will eventually be refactored.
|
||||
*/
|
||||
|
||||
$this->removeParamTypeFromMethod($classLike, $position, $currentClassMethod);
|
||||
$this->removeParamTypeFromMethodForChildren($className, $methodName, $position);
|
||||
}
|
||||
}
|
||||
|
||||
private function refactorClassMethod(ClassMethod $classMethod, Scope $classScope): void
|
||||
{
|
||||
if ($classMethod->isMagic()) {
|
||||
@ -316,4 +227,37 @@ CODE_SAMPLE
|
||||
$this->refactorParamForSelfAndSiblings($classMethod, (int) $position, $classScope);
|
||||
}
|
||||
}
|
||||
|
||||
private function isEmptyClassReflection(Scope $scope): bool
|
||||
{
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return count($classReflection->getAncestors()) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<class-string, Type> $parameterTypesByParentClassLikes
|
||||
*/
|
||||
private function refactorParameters(
|
||||
array $parameterTypesByParentClassLikes,
|
||||
string $methodName,
|
||||
int $paramPosition
|
||||
): void {
|
||||
foreach (array_keys($parameterTypesByParentClassLikes) as $className) {
|
||||
$classLike = $this->nodeRepository->findClassLike($className);
|
||||
if (! $classLike instanceof ClassLike) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$classMethod = $classLike->getMethod($methodName);
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->removeParamTypeFromMethod($classLike, $paramPosition, $classMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user