mirror of https://github.com/rectorphp/rector.git
Updated Rector to commit 9bcded03ea2db7b01183aad2407e3f7490be9531
9bcded03ea
[Experiment] Rework child classes detection on DynamicSourceLocatorProvider (#5735)
This commit is contained in:
parent
7141ae6e56
commit
e9ca0b2469
|
@ -19,12 +19,12 @@ final class VersionResolver
|
|||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const PACKAGE_VERSION = '8281a06bc8432d9d7f8e20842e3961df99a277bc';
|
||||
public const PACKAGE_VERSION = '9bcded03ea2db7b01183aad2407e3f7490be9531';
|
||||
/**
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const RELEASE_DATE = '2024-04-24 08:54:03';
|
||||
public const RELEASE_DATE = '2024-04-25 10:20:45';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
|
|
@ -289,6 +289,9 @@ final class LazyContainerFactory
|
|||
$phpStanServicesFactory = $container->make(PHPStanServicesFactory::class);
|
||||
return $phpStanServicesFactory->createDynamicSourceLocatorProvider();
|
||||
});
|
||||
$rectorConfig->afterResolving(DynamicSourceLocatorProvider::class, static function (DynamicSourceLocatorProvider $dynamicSourceLocatorProvider, Container $container) : void {
|
||||
$dynamicSourceLocatorProvider->autowire($container->make(ReflectionProvider::class));
|
||||
});
|
||||
// resetables
|
||||
$rectorConfig->tag(DynamicSourceLocatorProvider::class, ResetableInterface::class);
|
||||
$rectorConfig->tag(RenamedClassesDataCollector::class, ResetableInterface::class);
|
||||
|
|
|
@ -9,6 +9,7 @@ use PhpParser\Node\Stmt\Interface_;
|
|||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\Util\Reflection\PrivatesAccessor;
|
||||
final class FamilyRelationsAnalyzer
|
||||
{
|
||||
/**
|
||||
|
@ -21,10 +22,35 @@ final class FamilyRelationsAnalyzer
|
|||
* @var \Rector\NodeNameResolver\NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
public function __construct(ReflectionProvider $reflectionProvider, NodeNameResolver $nodeNameResolver)
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\Util\Reflection\PrivatesAccessor
|
||||
*/
|
||||
private $privatesAccessor;
|
||||
public function __construct(ReflectionProvider $reflectionProvider, NodeNameResolver $nodeNameResolver, PrivatesAccessor $privatesAccessor)
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->privatesAccessor = $privatesAccessor;
|
||||
}
|
||||
/**
|
||||
* @return ClassReflection[]
|
||||
*/
|
||||
public function getChildrenOfClassReflection(ClassReflection $desiredClassReflection) : array
|
||||
{
|
||||
if ($desiredClassReflection->isFinalByKeyword()) {
|
||||
return [];
|
||||
}
|
||||
/** @var ClassReflection[] $classReflections */
|
||||
$classReflections = $this->privatesAccessor->getPrivateProperty($this->reflectionProvider, 'classes');
|
||||
$childrenClassReflections = [];
|
||||
foreach ($classReflections as $classReflection) {
|
||||
if (!$classReflection->isSubclassOf($desiredClassReflection->getName())) {
|
||||
continue;
|
||||
}
|
||||
$childrenClassReflections[] = $classReflection;
|
||||
}
|
||||
return $childrenClassReflections;
|
||||
}
|
||||
/**
|
||||
* @api
|
||||
|
|
|
@ -3,11 +3,17 @@
|
|||
declare (strict_types=1);
|
||||
namespace Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider;
|
||||
|
||||
use PHPStan\BetterReflection\Identifier\IdentifierType;
|
||||
use PHPStan\BetterReflection\Reflector\DefaultReflector;
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
|
||||
use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
|
||||
use PHPStan\Broker\ClassNotFoundException;
|
||||
use PHPStan\File\CouldNotReadFileException;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\NewOptimizedDirectorySourceLocator;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedDirectorySourceLocatorFactory;
|
||||
use PHPStan\Reflection\BetterReflection\SourceLocator\OptimizedSingleFileSourceLocator;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use Rector\Contract\DependencyInjection\ResetableInterface;
|
||||
use Rector\Testing\PHPUnit\StaticPHPUnitEnvironment;
|
||||
/**
|
||||
|
@ -37,11 +43,19 @@ final class DynamicSourceLocatorProvider implements ResetableInterface
|
|||
* @var \PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator|null
|
||||
*/
|
||||
private $aggregateSourceLocator;
|
||||
/**
|
||||
* @var \PHPStan\Reflection\ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
public function __construct(FileNodesFetcher $fileNodesFetcher, OptimizedDirectorySourceLocatorFactory $optimizedDirectorySourceLocatorFactory)
|
||||
{
|
||||
$this->fileNodesFetcher = $fileNodesFetcher;
|
||||
$this->optimizedDirectorySourceLocatorFactory = $optimizedDirectorySourceLocatorFactory;
|
||||
}
|
||||
public function autowire(ReflectionProvider $reflectionProvider) : void
|
||||
{
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
public function setFilePath(string $filePath) : void
|
||||
{
|
||||
$this->filePaths = [$filePath];
|
||||
|
@ -74,8 +88,9 @@ final class DynamicSourceLocatorProvider implements ResetableInterface
|
|||
foreach ($this->directories as $directory) {
|
||||
$sourceLocators[] = $this->optimizedDirectorySourceLocatorFactory->createByDirectory($directory);
|
||||
}
|
||||
$this->aggregateSourceLocator = new AggregateSourceLocator($sourceLocators);
|
||||
return $this->aggregateSourceLocator;
|
||||
$aggregateSourceLocator = $this->aggregateSourceLocator = new AggregateSourceLocator($sourceLocators);
|
||||
$this->collectClasses($aggregateSourceLocator, $sourceLocators);
|
||||
return $aggregateSourceLocator;
|
||||
}
|
||||
public function isPathsEmpty() : bool
|
||||
{
|
||||
|
@ -90,4 +105,33 @@ final class DynamicSourceLocatorProvider implements ResetableInterface
|
|||
$this->directories = [];
|
||||
$this->aggregateSourceLocator = null;
|
||||
}
|
||||
/**
|
||||
* @param OptimizedSingleFileSourceLocator[]|NewOptimizedDirectorySourceLocator[] $sourceLocators
|
||||
*/
|
||||
private function collectClasses(AggregateSourceLocator $aggregateSourceLocator, array $sourceLocators) : void
|
||||
{
|
||||
if ($sourceLocators === []) {
|
||||
return;
|
||||
}
|
||||
// no need to collect classes on single file, will auto collected
|
||||
if (\count($sourceLocators) === 1 && $sourceLocators[0] instanceof OptimizedSingleFileSourceLocator) {
|
||||
return;
|
||||
}
|
||||
$reflector = new DefaultReflector($aggregateSourceLocator);
|
||||
$identifierClass = new IdentifierType(IdentifierType::IDENTIFIER_CLASS);
|
||||
foreach ($sourceLocators as $sourceLocator) {
|
||||
// trigger collect "classes" on get class on locate identifier
|
||||
try {
|
||||
$reflections = $sourceLocator->locateIdentifiersByType($reflector, $identifierClass);
|
||||
foreach ($reflections as $reflection) {
|
||||
// make 'classes' collection
|
||||
try {
|
||||
$this->reflectionProvider->getClass($reflection->getName());
|
||||
} catch (ClassNotFoundException $exception) {
|
||||
}
|
||||
}
|
||||
} catch (CouldNotReadFileException $exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,24 +3,40 @@
|
|||
declare (strict_types=1);
|
||||
namespace Rector\VendorLocker\NodeVendorLocker;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\FunctionVariantWithPhpDocs;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer;
|
||||
use Rector\FileSystem\FilePathHelper;
|
||||
use Rector\NodeAnalyzer\MagicClassMethodAnalyzer;
|
||||
use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper;
|
||||
use Rector\Reflection\ReflectionResolver;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard;
|
||||
final class ClassMethodReturnTypeOverrideGuard
|
||||
{
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer
|
||||
*/
|
||||
private $familyRelationsAnalyzer;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\Reflection\ReflectionResolver
|
||||
*/
|
||||
private $reflectionResolver;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer
|
||||
*/
|
||||
private $returnTypeInferer;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\ParentClassMethodTypeOverrideGuard
|
||||
|
@ -36,9 +52,11 @@ final class ClassMethodReturnTypeOverrideGuard
|
|||
* @var \Rector\NodeAnalyzer\MagicClassMethodAnalyzer
|
||||
*/
|
||||
private $magicClassMethodAnalyzer;
|
||||
public function __construct(ReflectionResolver $reflectionResolver, ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, FilePathHelper $filePathHelper, MagicClassMethodAnalyzer $magicClassMethodAnalyzer)
|
||||
public function __construct(FamilyRelationsAnalyzer $familyRelationsAnalyzer, ReflectionResolver $reflectionResolver, ReturnTypeInferer $returnTypeInferer, ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, FilePathHelper $filePathHelper, MagicClassMethodAnalyzer $magicClassMethodAnalyzer)
|
||||
{
|
||||
$this->familyRelationsAnalyzer = $familyRelationsAnalyzer;
|
||||
$this->reflectionResolver = $reflectionResolver;
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->parentClassMethodTypeOverrideGuard = $parentClassMethodTypeOverrideGuard;
|
||||
$this->filePathHelper = $filePathHelper;
|
||||
$this->magicClassMethodAnalyzer = $magicClassMethodAnalyzer;
|
||||
|
@ -65,7 +83,37 @@ final class ClassMethodReturnTypeOverrideGuard
|
|||
if (!$this->isReturnTypeChangeAllowed($classMethod, $scope)) {
|
||||
return \true;
|
||||
}
|
||||
return $classMethod->isFinal();
|
||||
if ($classMethod->isFinal()) {
|
||||
return \false;
|
||||
}
|
||||
$childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection);
|
||||
if ($childrenClassReflections === []) {
|
||||
return \false;
|
||||
}
|
||||
if ($classMethod->returnType instanceof Node) {
|
||||
return \true;
|
||||
}
|
||||
$returnType = $this->returnTypeInferer->inferFunctionLike($classMethod);
|
||||
return $this->hasChildrenDifferentTypeClassMethod($classMethod, $childrenClassReflections, $returnType);
|
||||
}
|
||||
/**
|
||||
* @param ClassReflection[] $childrenClassReflections
|
||||
*/
|
||||
private function hasChildrenDifferentTypeClassMethod(ClassMethod $classMethod, array $childrenClassReflections, Type $returnType) : bool
|
||||
{
|
||||
$methodName = $classMethod->name->toString();
|
||||
foreach ($childrenClassReflections as $childClassReflection) {
|
||||
$methodReflection = $childClassReflection->getNativeMethod($methodName);
|
||||
if (!$methodReflection instanceof PhpMethodReflection) {
|
||||
continue;
|
||||
}
|
||||
$parametersAcceptor = ParametersAcceptorSelector::combineAcceptors($methodReflection->getVariants());
|
||||
$childReturnType = $parametersAcceptor->getNativeReturnType();
|
||||
if (!$returnType->isSuperTypeOf($childReturnType)->yes()) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
private function isReturnTypeChangeAllowed(ClassMethod $classMethod, Scope $scope) : bool
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue