Updated Rector to commit befe096ca10f7476efeb082fb7c736ba7bd07af5

befe096ca1 [phpstan] tidy up bin files, group complex errors + Cleanup AddTypeToConstRector (#5405)
This commit is contained in:
Tomas Votruba 2023-12-31 20:56:31 +00:00
parent 6e1ac30f0c
commit 1731eeac5d
12 changed files with 113 additions and 140 deletions

View File

@ -64,7 +64,6 @@ abstract class AbstractRectorTestCase extends \Rector\Testing\PHPUnit\AbstractLa
protected function setUp() : void
{
$this->includePreloadFilesAndScoperAutoload();
@\ini_set('memory_limit', '-1');
$configFile = $this->provideConfigFilePath();
// cleanup all registered rectors, so you can use only the new ones
$rectorConfig = self::getContainer();
@ -109,9 +108,6 @@ abstract class AbstractRectorTestCase extends \Rector\Testing\PHPUnit\AbstractLa
FileSystem::delete($this->inputFilePath);
}
}
/**
* @return Iterator<<string>>
*/
protected static function yieldFilesFromDirectory(string $directory, string $suffix = '*.php.inc') : Iterator
{
return FixtureFileFinder::yieldDirectory($directory, $suffix);

View File

@ -5,6 +5,9 @@ namespace Rector\Testing\PHPUnit\ValueObject;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\ValueObject\ProcessResult;
/**
* @api used in tests
*/
final class RectorTestResult
{
/**

View File

@ -96,12 +96,7 @@ CODE_SAMPLE
{
// allow class strings to be part of class const arrays, as probably on purpose
if ($node instanceof ClassConst) {
$this->traverseNodesWithCallable($node->consts, static function (Node $subNode) {
if ($subNode instanceof String_) {
$subNode->setAttribute(self::IS_UNDER_CLASS_CONST, \true);
}
return null;
});
$this->decorateClassConst($node);
return null;
}
// keep allowed string as condition
@ -174,4 +169,13 @@ CODE_SAMPLE
}
return \false;
}
private function decorateClassConst(ClassConst $classConst) : void
{
$this->traverseNodesWithCallable($classConst->consts, static function (Node $subNode) {
if ($subNode instanceof String_) {
$subNode->setAttribute(self::IS_UNDER_CLASS_CONST, \true);
}
return null;
});
}
}

View File

@ -23,12 +23,11 @@ final class SwitchExprsResolver
$newSwitch = clone $switch;
$condAndExpr = [];
$collectionEmptyCasesCond = [];
if (!$this->areCasesValid($newSwitch)) {
return [];
}
$this->moveDefaultCaseToLast($newSwitch);
foreach ($newSwitch->cases as $key => $case) {
\assert(\is_int($key));
if (!$this->isValidCase($case)) {
return [];
}
if ($case->stmts !== []) {
continue;
}
@ -127,4 +126,13 @@ final class SwitchExprsResolver
// default value
return !$case->cond instanceof Expr;
}
private function areCasesValid(Switch_ $newSwitch) : bool
{
foreach ($newSwitch->cases as $case) {
if (!$this->isValidCase($case)) {
return \false;
}
}
return \true;
}
}

View File

@ -112,14 +112,10 @@ CODE_SAMPLE
*/
public function refactorWithScope(Node $node, Scope $scope) : ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
$hasChanged = \false;
if ($node->isReadonly()) {
return null;
}
// skip "clone $this" cases, as can create unexpected write to local constructor property
if ($this->hasCloneThis($node)) {
return null;
}
foreach ($node->getMethods() as $classMethod) {
foreach ($classMethod->params as $param) {
$justChanged = $this->refactorParam($node, $classMethod, $param, $scope);
@ -222,6 +218,14 @@ CODE_SAMPLE
});
return $isAssigned;
}
private function shouldSkip(Class_ $class) : bool
{
if ($class->isReadonly()) {
return \true;
}
// skip "clone $this" cases, as can create unexpected write to local constructor property
return $this->hasCloneThis($class);
}
private function hasCloneThis(Class_ $class) : bool
{
return (bool) $this->betterNodeFinder->findFirst($class, function (Node $node) : bool {

View File

@ -9,17 +9,13 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\DNumber;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MissingConstantFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Exception\FullyQualifiedNameNotAutoloadedException;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@ -62,36 +58,32 @@ CODE_SAMPLE
/**
* @param Class_ $node
*/
public function refactor(Node $node) : ?\PhpParser\Node\Stmt\Class_
public function refactor(Node $node) : ?Class_
{
$className = $this->getName($node);
if (!\is_string($className)) {
return null;
}
if ($node->isAbstract()) {
return null;
}
$consts = \array_filter($node->stmts, static function (Node $node) : bool {
return $node instanceof ClassConst;
});
if ($consts === []) {
$classConsts = $node->getConstants();
if ($classConsts === []) {
return null;
}
try {
$parents = $this->getParents($node);
$implementations = $this->getImplementations($node);
$traits = $this->getTraits($node);
} catch (FullyQualifiedNameNotAutoloadedException $exception) {
return null;
}
$changes = \false;
foreach ($consts as $const) {
$parentClassReflections = $this->getParentReflections($className);
$hasChanged = \false;
foreach ($classConsts as $classConst) {
$valueType = null;
// If a type is set, skip
if ($const->type !== null) {
if ($classConst->type !== null) {
continue;
}
foreach ($const->consts as $constNode) {
if ($this->shouldSkipDueToInheritance($constNode, $parents, $implementations, $traits)) {
foreach ($classConst->consts as $constNode) {
if ($this->isConstGuardedByParents($constNode, $parentClassReflections)) {
continue;
}
if ($this->canBeInheritied($const, $node)) {
if ($this->canBeInherited($classConst, $node)) {
continue;
}
$valueType = $this->findValueType($constNode->value);
@ -99,10 +91,10 @@ CODE_SAMPLE
if (!($valueType ?? null) instanceof Identifier) {
continue;
}
$const->type = $valueType;
$changes = \true;
$classConst->type = $valueType;
$hasChanged = \true;
}
if (!$changes) {
if (!$hasChanged) {
return null;
}
return $node;
@ -112,22 +104,14 @@ CODE_SAMPLE
return PhpVersionFeature::TYPED_CLASS_CONSTANTS;
}
/**
* @param ClassReflection[] $parents
* @param ClassReflection[] $implementations
* @param ClassReflection[] $traits
* @param ClassReflection[] $parentClassReflections
*/
public function shouldSkipDueToInheritance(Const_ $const, array $parents, array $implementations, array $traits) : bool
public function isConstGuardedByParents(Const_ $const, array $parentClassReflections) : bool
{
foreach ([$parents, $implementations, $traits] as $inheritance) {
foreach ($inheritance as $inheritanceItem) {
if ($const->name->name === '') {
continue;
}
try {
$inheritanceItem->getConstant($const->name->name);
return \true;
} catch (MissingConstantFromReflectionException $exception) {
}
$constantName = $this->getName($const);
foreach ($parentClassReflections as $parentClassReflection) {
if ($parentClassReflection->hasConstant($constantName)) {
return \true;
}
}
return \false;
@ -157,54 +141,17 @@ CODE_SAMPLE
/**
* @return ClassReflection[]
*/
private function getParents(Class_ $class) : array
private function getParentReflections(string $className) : array
{
$parents = \array_filter([$class->extends]);
return \array_map(function (Name $name) : ClassReflection {
if (!$name instanceof FullyQualified) {
throw new FullyQualifiedNameNotAutoloadedException($name);
}
if ($this->reflectionProvider->hasClass($name->toString())) {
return $this->reflectionProvider->getClass($name->toString());
}
throw new FullyQualifiedNameNotAutoloadedException($name);
}, $parents);
}
/**
* @return ClassReflection[]
*/
private function getImplementations(Class_ $class) : array
{
return \array_map(function (Name $name) : ClassReflection {
if (!$name instanceof FullyQualified) {
throw new FullyQualifiedNameNotAutoloadedException($name);
}
if ($this->reflectionProvider->hasClass($name->toString())) {
return $this->reflectionProvider->getClass($name->toString());
}
throw new FullyQualifiedNameNotAutoloadedException($name);
}, $class->implements);
}
/**
* @return ClassReflection[]
*/
private function getTraits(Class_ $class) : array
{
$traits = [];
foreach ($class->getTraitUses() as $traitUse) {
$traits = \array_merge($traits, $traitUse->traits);
if (!$this->reflectionProvider->hasClass($className)) {
return [];
}
return \array_map(function (Name $name) : ClassReflection {
if (!$name instanceof FullyQualified) {
throw new FullyQualifiedNameNotAutoloadedException($name);
}
if ($this->reflectionProvider->hasClass($name->toString())) {
return $this->reflectionProvider->getClass($name->toString());
}
throw new FullyQualifiedNameNotAutoloadedException($name);
}, $traits);
$currentClassReflection = $this->reflectionProvider->getClass($className);
return \array_filter($currentClassReflection->getAncestors(), static function (ClassReflection $classReflection) use($currentClassReflection) : bool {
return $currentClassReflection !== $classReflection;
});
}
private function canBeInheritied(ClassConst $classConst, Class_ $class) : bool
private function canBeInherited(ClassConst $classConst, Class_ $class) : bool
{
return !$class->isFinal() && !$classConst->isPrivate();
}

View File

@ -121,18 +121,9 @@ CODE_SAMPLE
}
/** @var array<StaticCall|MethodCall|FuncCall> $callers */
$callers = $this->betterNodeFinder->findInstancesOf($classMethod, [StaticCall::class, MethodCall::class, FuncCall::class]);
foreach ($classMethod->params as $param) {
if ($this->shouldSkipParam($param, $classMethod)) {
continue;
}
foreach ($callers as $caller) {
$paramType = $this->callerParamMatcher->matchCallParamType($caller, $param, $scope);
if ($paramType === null) {
continue;
}
$this->mirrorParamType($param, $paramType);
$hasChanged = \true;
}
$hasClassMethodChanged = $this->refactorClassMethod($classMethod, $callers, $scope);
if ($hasClassMethodChanged) {
$hasChanged = \true;
}
}
if ($hasChanged) {
@ -172,4 +163,25 @@ CODE_SAMPLE
}
return !$this->paramTypeAddGuard->isLegal($param, $classMethod);
}
/**
* @param array<StaticCall|MethodCall|FuncCall> $callers
*/
private function refactorClassMethod(ClassMethod $classMethod, array $callers, Scope $scope) : bool
{
$hasChanged = \false;
foreach ($classMethod->params as $param) {
if ($this->shouldSkipParam($param, $classMethod)) {
continue;
}
foreach ($callers as $caller) {
$paramType = $this->callerParamMatcher->matchCallParamType($caller, $param, $scope);
if ($paramType === null) {
continue;
}
$this->mirrorParamType($param, $paramType);
$hasChanged = \true;
}
}
return $hasChanged;
}
}

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '0ba47067e827e46c7fc17b1a53cb4418080551d3';
public const PACKAGE_VERSION = 'befe096ca10f7476efeb082fb7c736ba7bd07af5';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-12-31 20:52:35';
public const RELEASE_DATE = '2023-12-31 20:54:26';
/**
* @var int
*/

View File

@ -1,19 +0,0 @@
<?php
declare (strict_types=1);
namespace Rector\Core\Exception;
use PhpParser\Node\Name;
use RuntimeException;
final class FullyQualifiedNameNotAutoloadedException extends RuntimeException
{
/**
* @var \PhpParser\Node\Name
*/
protected $name;
public function __construct(Name $name)
{
$this->name = $name;
parent::__construct(\sprintf('%s was not autoloaded', $name->toString()));
}
}

View File

@ -0,0 +1,18 @@
<?php
declare (strict_types=1);
namespace Rector\Core\FileSystem;
use RectorPrefix202312\Nette\Utils\FileSystem;
use RectorPrefix202312\Nette\Utils\Json;
final class JsonFileSystem
{
/**
* @return array<string, mixed>
*/
public static function readFilePath(string $filePath) : array
{
$fileContents = FileSystem::read($filePath);
return Json::decode($fileContents, Json::FORCE_ARRAY);
}
}

View File

@ -1206,7 +1206,6 @@ return array(
'Rector\\Core\\Error\\ExceptionCorrector' => $baseDir . '/src/Error/ExceptionCorrector.php',
'Rector\\Core\\Exception\\Cache\\CachingException' => $baseDir . '/src/Exception/Cache/CachingException.php',
'Rector\\Core\\Exception\\Configuration\\InvalidConfigurationException' => $baseDir . '/src/Exception/Configuration/InvalidConfigurationException.php',
'Rector\\Core\\Exception\\FullyQualifiedNameNotAutoloadedException' => $baseDir . '/src/Exception/FullyQualifiedNameNotAutoloadedException.php',
'Rector\\Core\\Exception\\NotImplementedYetException' => $baseDir . '/src/Exception/NotImplementedYetException.php',
'Rector\\Core\\Exception\\Reflection\\MissingPrivatePropertyException' => $baseDir . '/src/Exception/Reflection/MissingPrivatePropertyException.php',
'Rector\\Core\\Exception\\ShouldNotHappenException' => $baseDir . '/src/Exception/ShouldNotHappenException.php',
@ -1216,6 +1215,7 @@ return array(
'Rector\\Core\\FileSystem\\FilesFinder' => $baseDir . '/src/FileSystem/FilesFinder.php',
'Rector\\Core\\FileSystem\\FilesystemTweaker' => $baseDir . '/src/FileSystem/FilesystemTweaker.php',
'Rector\\Core\\FileSystem\\InitFilePathsResolver' => $baseDir . '/src/FileSystem/InitFilePathsResolver.php',
'Rector\\Core\\FileSystem\\JsonFileSystem' => $baseDir . '/src/FileSystem/JsonFileSystem.php',
'Rector\\Core\\NodeAnalyzer\\ArgsAnalyzer' => $baseDir . '/src/NodeAnalyzer/ArgsAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\BinaryOpAnalyzer' => $baseDir . '/src/NodeAnalyzer/BinaryOpAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\CallAnalyzer' => $baseDir . '/src/NodeAnalyzer/CallAnalyzer.php',

View File

@ -1424,7 +1424,6 @@ class ComposerStaticInit8f2270509893f1162053010eaa6e09a5
'Rector\\Core\\Error\\ExceptionCorrector' => __DIR__ . '/../..' . '/src/Error/ExceptionCorrector.php',
'Rector\\Core\\Exception\\Cache\\CachingException' => __DIR__ . '/../..' . '/src/Exception/Cache/CachingException.php',
'Rector\\Core\\Exception\\Configuration\\InvalidConfigurationException' => __DIR__ . '/../..' . '/src/Exception/Configuration/InvalidConfigurationException.php',
'Rector\\Core\\Exception\\FullyQualifiedNameNotAutoloadedException' => __DIR__ . '/../..' . '/src/Exception/FullyQualifiedNameNotAutoloadedException.php',
'Rector\\Core\\Exception\\NotImplementedYetException' => __DIR__ . '/../..' . '/src/Exception/NotImplementedYetException.php',
'Rector\\Core\\Exception\\Reflection\\MissingPrivatePropertyException' => __DIR__ . '/../..' . '/src/Exception/Reflection/MissingPrivatePropertyException.php',
'Rector\\Core\\Exception\\ShouldNotHappenException' => __DIR__ . '/../..' . '/src/Exception/ShouldNotHappenException.php',
@ -1434,6 +1433,7 @@ class ComposerStaticInit8f2270509893f1162053010eaa6e09a5
'Rector\\Core\\FileSystem\\FilesFinder' => __DIR__ . '/../..' . '/src/FileSystem/FilesFinder.php',
'Rector\\Core\\FileSystem\\FilesystemTweaker' => __DIR__ . '/../..' . '/src/FileSystem/FilesystemTweaker.php',
'Rector\\Core\\FileSystem\\InitFilePathsResolver' => __DIR__ . '/../..' . '/src/FileSystem/InitFilePathsResolver.php',
'Rector\\Core\\FileSystem\\JsonFileSystem' => __DIR__ . '/../..' . '/src/FileSystem/JsonFileSystem.php',
'Rector\\Core\\NodeAnalyzer\\ArgsAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/ArgsAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\BinaryOpAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/BinaryOpAnalyzer.php',
'Rector\\Core\\NodeAnalyzer\\CallAnalyzer' => __DIR__ . '/../..' . '/src/NodeAnalyzer/CallAnalyzer.php',