mirror of https://github.com/rectorphp/rector.git
Updated Rector to commit befe096ca10f7476efeb082fb7c736ba7bd07af5
befe096ca1
[phpstan] tidy up bin files, group complex errors + Cleanup AddTypeToConstRector (#5405)
This commit is contained in:
parent
6e1ac30f0c
commit
1731eeac5d
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue