mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-01 17:00:51 +00:00
[PHP 8.1] Add ReturnNeverTypeRector (#6283)
This commit is contained in:
parent
bbba5304ad
commit
2037d55d6b
11
config/set/php81.php
Normal file
11
config/set/php81.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ReturnNeverTypeRector::class);
|
||||
};
|
|
@ -573,6 +573,11 @@ final class SetList implements SetListInterface
|
|||
*/
|
||||
public const PHP_80 = __DIR__ . '/../../../config/set/php80.php';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const PHP_81 = __DIR__ . '/../../../config/set/php81.php';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class DieSome
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
echo 100;
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class DieSome
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
echo 100;
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class ExitSome
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
echo 100;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class ExitSome
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
echo 100;
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
|
||||
final class ImproveVoid
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
|
||||
final class ImproveVoid
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
function run($key)
|
||||
{
|
||||
if ($key) {
|
||||
echo 100;
|
||||
exit;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class SkipNeverAlready
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
throw new InvalidException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
|
||||
final class SkipParentProtected implements SomeInterfaceWithReturnType
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
}
|
||||
|
||||
interface SomeInterfaceWithReturnType
|
||||
{
|
||||
public function run(): void;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class SkipReturn
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class SkipYield
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
yield 1;
|
||||
exit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
throw new InvalidException();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\Fixture;
|
||||
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
throw new InvalidException();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ReturnNeverTypeRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\ValueObject\PhpVersion;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersion::PHP_81);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ReturnNeverTypeRector::class);
|
||||
};
|
|
@ -97,6 +97,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ArgumentDefaultValueReplacer[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$replacedArguments = $configuration[self::REPLACED_ARGUMENTS] ?? [];
|
||||
|
|
|
@ -95,6 +95,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, SwapFuncCallArguments[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$functionArgumentSwaps = $configuration[self::FUNCTION_ARGUMENT_SWAPS] ?? [];
|
||||
|
|
|
@ -30,6 +30,42 @@ final class ParentClassMethodTypeOverrideGuard
|
|||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
}
|
||||
|
||||
public function hasParentMethodOutsideVendor(ClassMethod $classMethod): bool
|
||||
{
|
||||
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodName = $classMethod->name->toString();
|
||||
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
|
||||
if ($classReflection === $ancestorClassReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $ancestorClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClassMethodReflection = $ancestorClassReflection->getMethod($methodName, $scope);
|
||||
$parentClassMethod = $this->nodeRepository->findClassMethodByMethodReflection(
|
||||
$parentClassMethodReflection
|
||||
);
|
||||
|
||||
if (! $parentClassMethod instanceof ClassMethod) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isReturnTypeChangeAllowed(ClassMethod $classMethod): bool
|
||||
{
|
||||
// make sure return type is not protected by parent contract
|
||||
|
@ -45,7 +81,11 @@ final class ParentClassMethodTypeOverrideGuard
|
|||
);
|
||||
|
||||
// if null, we're unable to override → skip it
|
||||
return $parentClassMethod !== null;
|
||||
if (! $parentClassMethod instanceof ClassMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $parentClassMethod->returnType === null;
|
||||
}
|
||||
|
||||
private function getParentClassMethod(ClassMethod $classMethod): ?MethodReflection
|
||||
|
@ -63,7 +103,11 @@ final class ParentClassMethodTypeOverrideGuard
|
|||
return null;
|
||||
}
|
||||
|
||||
foreach ($classReflection->getParents() as $parentClassReflection) {
|
||||
foreach ($classReflection->getAncestors() as $parentClassReflection) {
|
||||
if ($classReflection === $parentClassReflection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $parentClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -119,6 +119,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, NormalToFluent[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$callsToFluent = $configuration[self::CALLS_TO_FLUENT] ?? [];
|
||||
|
|
|
@ -141,7 +141,7 @@ CODE_SAMPLE
|
|||
|
||||
private function shouldSkip(Return_ $return, ClassMethod $classMethod): bool
|
||||
{
|
||||
if (! $this->parentClassMethodTypeOverrideGuard->isReturnTypeChangeAllowed($classMethod)) {
|
||||
if ($this->parentClassMethodTypeOverrideGuard->hasParentMethodOutsideVendor($classMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ final class AddMethodParentCallRector extends AbstractRector implements Configur
|
|||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $methodsByParentTypes = [];
|
||||
private $methodByParentTypes = [];
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
|
@ -90,7 +90,7 @@ CODE_SAMPLE
|
|||
/** @var string $className */
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
foreach ($this->methodsByParentTypes as $type => $method) {
|
||||
foreach ($this->methodByParentTypes as $type => $method) {
|
||||
if (! $this->isObjectType($classLike, new ObjectType($type))) {
|
||||
continue;
|
||||
}
|
||||
|
@ -112,9 +112,12 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->methodsByParentTypes = $configuration[self::METHODS_BY_PARENT_TYPES] ?? [];
|
||||
$this->methodByParentTypes = $configuration[self::METHODS_BY_PARENT_TYPES] ?? [];
|
||||
}
|
||||
|
||||
private function shouldSkipMethod(ClassMethod $classMethod, string $method): bool
|
||||
|
|
|
@ -27,7 +27,7 @@ final class ReservedObjectRector extends AbstractRector implements ConfigurableR
|
|||
public const RESERVED_KEYWORDS_TO_REPLACEMENTS = 'reserved_keywords_to_replacements';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $reservedKeywordsToReplacements = [];
|
||||
|
||||
|
@ -78,6 +78,9 @@ CODE_SAMPLE
|
|||
return $this->processName($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->reservedKeywordsToReplacements = $configuration[self::RESERVED_KEYWORDS_TO_REPLACEMENTS] ?? [];
|
||||
|
|
|
@ -104,6 +104,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->reservedNamesToNewOnes = $configuration[self::RESERVED_NAMES_TO_NEW_ONES] ?? [];
|
||||
|
|
|
@ -86,6 +86,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ArgumentRemover[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$removedArguments = $configuration[self::REMOVED_ARGUMENTS] ?? [];
|
||||
|
|
|
@ -22,7 +22,7 @@ final class RemoveInterfacesRector extends AbstractRector implements Configurabl
|
|||
public const INTERFACES_TO_REMOVE = 'interfaces_to_remove';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var class-string[]
|
||||
*/
|
||||
private $interfacesToRemove = [];
|
||||
|
||||
|
@ -75,6 +75,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, class-string[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->interfacesToRemove = $configuration[self::INTERFACES_TO_REMOVE] ?? [];
|
||||
|
|
|
@ -99,6 +99,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->traitsToRemove = $configuration[self::TRAITS_TO_REMOVE] ?? [];
|
||||
|
|
|
@ -80,6 +80,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, RemoveFuncCallArg[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$removedFunctionArguments = $configuration[self::REMOVED_FUNCTION_ARGUMENTS] ?? [];
|
||||
|
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace Rector\RemovingStatic\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
|
@ -13,14 +12,11 @@ use PhpParser\Node\Expr\Variable;
|
|||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Naming\Naming\PropertyNaming;
|
||||
use Symplify\Astral\ValueObject\NodeBuilder\MethodBuilder;
|
||||
use Symplify\Astral\ValueObject\NodeBuilder\ParamBuilder;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
|
@ -36,7 +32,7 @@ final class StaticTypeToSetterInjectionRector extends AbstractRector implements
|
|||
public const STATIC_TYPES = 'static_types';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var array<class-string|int, class-string>
|
||||
*/
|
||||
private $staticTypes = [];
|
||||
|
||||
|
@ -134,6 +130,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<class-string|int, class-string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->staticTypes = $configuration[self::STATIC_TYPES] ?? [];
|
||||
|
@ -160,14 +159,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
$variableName = $this->propertyNaming->fqnToVariableName($objectType);
|
||||
|
||||
$paramBuilder = new ParamBuilder($variableName);
|
||||
$paramBuilder->setType(new FullyQualified($staticType));
|
||||
$param = $paramBuilder->getNode();
|
||||
|
||||
$assign = $this->nodeFactory->createPropertyAssignment($variableName);
|
||||
|
||||
$setEntityFactoryMethod = $this->createSetEntityFactoryClassMethod($variableName, $param, $assign);
|
||||
$setEntityFactoryMethod = $this->nodeFactory->createSetterClassMethod($variableName, $objectType);
|
||||
|
||||
$entityFactoryProperty = $this->nodeFactory->createPrivateProperty($variableName);
|
||||
|
||||
|
@ -190,20 +182,4 @@ CODE_SAMPLE
|
|||
|
||||
return $this->isObjectType($node->class, $objectType);
|
||||
}
|
||||
|
||||
private function createSetEntityFactoryClassMethod(
|
||||
string $variableName,
|
||||
Param $param,
|
||||
Assign $assign
|
||||
): ClassMethod {
|
||||
$setMethodName = 'set' . ucfirst($variableName);
|
||||
|
||||
$setEntityFactoryMethodBuilder = new MethodBuilder($setMethodName);
|
||||
$setEntityFactoryMethodBuilder->makePublic();
|
||||
$setEntityFactoryMethodBuilder->addParam($param);
|
||||
$setEntityFactoryMethodBuilder->setReturnType('void');
|
||||
$setEntityFactoryMethodBuilder->addStmt($assign);
|
||||
|
||||
return $setEntityFactoryMethodBuilder->getNode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, RenameAnnotation[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$renamedAnnotationsInTypes = $configuration[self::RENAMED_ANNOTATIONS_IN_TYPES] ?? [];
|
||||
|
|
|
@ -122,6 +122,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, PseudoNamespaceToNamespace[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$namespacePrefixesWithExcludedClasses = $configuration[self::NAMESPACE_PREFIXES_WITH_EXCLUDED_CLASSES] ?? [];
|
||||
|
|
|
@ -73,6 +73,9 @@ final class RenamePropertyRector extends AbstractRector implements ConfigurableR
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, RenameProperty[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$renamedProperties = $configuration[self::RENAMED_PROPERTIES] ?? [];
|
||||
|
|
|
@ -13,6 +13,7 @@ use Rector\Core\Rector\AbstractRector;
|
|||
use Rector\Renaming\ValueObject\RenameStaticMethod;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\Renaming\Rector\StaticCall\RenameStaticMethodRector\RenameStaticMethodRectorTest
|
||||
|
@ -90,9 +91,14 @@ final class RenameStaticMethodRector extends AbstractRector implements Configura
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, RenameStaticMethod[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->staticMethodRenames = $configuration[self::OLD_TO_NEW_METHODS_BY_CLASSES] ?? [];
|
||||
$oldToNewMethodsByClasses = $configuration[self::OLD_TO_NEW_METHODS_BY_CLASSES];
|
||||
Assert::allIsInstanceOf($oldToNewMethodsByClasses, RenameStaticMethod::class);
|
||||
$this->staticMethodRenames = $oldToNewMethodsByClasses;
|
||||
}
|
||||
|
||||
private function rename(StaticCall $staticCall, RenameStaticMethod $renameStaticMethod): StaticCall
|
||||
|
|
|
@ -127,6 +127,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<class-string, class-string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->classToInstantiateByType = $configuration[self::CLASS_TO_INSTANTIATE_BY_TYPE] ?? [];
|
||||
|
|
|
@ -112,6 +112,9 @@ CODE_SAMPLE
|
|||
return new MethodCall($arrayDimFetch->var, $dimFetchAssignToMethodCall->getAddMethod(), $node->expr->args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, DimFetchAssignToMethodCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$dimFetchAssignToMethodCalls = $configuration[self::DIM_FETCH_ASSIGN_TO_METHOD_CALL] ?? [];
|
||||
|
|
|
@ -94,6 +94,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, PropertyAssignToMethodCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$propertyAssignsToMethodCalls = $configuration[self::PROPERTY_ASSIGNS_TO_METHODS_CALLS] ?? [];
|
||||
|
|
|
@ -94,6 +94,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, WrapReturn[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$typeMethodWraps = $configuration[self::TYPE_METHOD_WRAPS] ?? [];
|
||||
|
|
|
@ -28,7 +28,7 @@ final class MergeInterfacesRector extends AbstractRector implements Configurable
|
|||
public const OLD_TO_NEW_INTERFACES = 'old_to_new_interfaces';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $oldToNewInterfaces = [];
|
||||
|
||||
|
@ -89,6 +89,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->oldToNewInterfaces = $configuration[self::OLD_TO_NEW_INTERFACES] ?? [];
|
||||
|
|
|
@ -95,6 +95,9 @@ CODE_SAMPLE
|
|||
return $this->refactorMethodCall($methodCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, MethodCallToReturn[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$methodCallWraps = $configuration[self::METHOD_CALL_WRAPS] ?? [];
|
||||
|
|
|
@ -86,6 +86,9 @@ CODE_SAMPLE
|
|||
return new ConstFetch(new Name($this->functionsToConstants[$functionName]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->functionsToConstants = $configuration[self::FUNCTIONS_TO_CONSTANTS] ?? [];
|
||||
|
|
|
@ -71,6 +71,9 @@ final class FuncCallToStaticCallRector extends AbstractRector implements Configu
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, FuncCallToStaticCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$funcCallsToStaticCalls = $configuration[self::FUNC_CALLS_TO_STATIC_CALLS] ?? [];
|
||||
|
|
|
@ -88,6 +88,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, UnsetAndIssetToMethodCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$issetUnsetToMethodCalls = $configuration[self::ISSET_UNSET_TO_METHOD_CALL] ?? [];
|
||||
|
|
|
@ -84,6 +84,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, MethodCallToAnotherMethodCallWithArguments[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$methodCallRenamesWithAddedArguments = $configuration[self::METHOD_CALL_RENAMES_WITH_ADDED_ARGUMENTS] ?? [];
|
||||
|
|
|
@ -84,6 +84,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, array<string, string>> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->methodCallToPropertyFetchCollection = $configuration[self::METHOD_CALL_TO_PROPERTY_FETCHES] ?? [];
|
||||
|
|
|
@ -107,6 +107,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, MethodCallToStaticCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$methodCallsToStaticCalls = $configuration[self::METHOD_CALLS_TO_STATIC_CALLS] ?? [];
|
||||
|
|
|
@ -91,6 +91,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ReplaceParentCallByPropertyCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->parentCallToProperties = $configuration[self::PARENT_CALLS_TO_PROPERTIES] ?? [];
|
||||
|
|
|
@ -176,6 +176,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ServiceGetterToConstructorInjection[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$methodCallToServices = $configuration[self::METHOD_CALL_TO_SERVICES] ?? [];
|
||||
|
|
|
@ -87,6 +87,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, NewToStaticCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$typeToStaticCalls = $configuration[self::TYPE_TO_STATIC_CALLS] ?? [];
|
||||
|
|
|
@ -81,6 +81,9 @@ final class StaticCallToFuncCallRector extends AbstractRector implements Configu
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, StaticCallToFuncCall[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$staticCallsToFunctions = $configuration[self::STATIC_CALLS_TO_FUNCTIONS] ?? [];
|
||||
|
|
|
@ -88,6 +88,9 @@ CODE_SAMPLE
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, StringToClassConstant[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$stringToClassConstants = $configuration[self::STRINGS_TO_CLASS_CONSTANTS] ?? [];
|
||||
|
|
|
@ -104,6 +104,9 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, AddReturnTypeDeclaration[]> $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$methodReturnTypes = $configuration[self::METHOD_RETURN_TYPES] ?? [];
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Yield_;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\Stmt\Throw_;
|
||||
use PHPStan\Type\NeverType;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\Defluent\ConflictGuard\ParentClassMethodTypeOverrideGuard;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @changelog https://wiki.php.net/rfc/noreturn_type
|
||||
*
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector\ReturnNeverTypeRectorTest
|
||||
*/
|
||||
final class ReturnNeverTypeRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ParentClassMethodTypeOverrideGuard
|
||||
*/
|
||||
private $parentClassMethodTypeOverrideGuard;
|
||||
|
||||
public function __construct(ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard)
|
||||
{
|
||||
$this->parentClassMethodTypeOverrideGuard = $parentClassMethodTypeOverrideGuard;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add "never" type for methods that never return anything', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
throw new InvalidException();
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(): never
|
||||
{
|
||||
throw new InvalidException();
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class, Function_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::NEVER_TYPE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$returns = $this->betterNodeFinder->findInstanceOf($node, Return_::class);
|
||||
if ($returns !== []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$notNeverNodes = $this->betterNodeFinder->findInstancesOf($node, [Yield_::class]);
|
||||
if ($notNeverNodes !== []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$neverNodes = $this->betterNodeFinder->findInstancesOf($node, [Node\Expr\Throw_::class, Throw_::class]);
|
||||
$hasNeverFuncCall = $this->resolveHasNeverFuncCall($node);
|
||||
if ($neverNodes === [] && ! $hasNeverFuncCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node instanceof ClassMethod && ! $this->parentClassMethodTypeOverrideGuard->isReturnTypeChangeAllowed(
|
||||
$node
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->returnType = new Name('never');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $functionLike
|
||||
*/
|
||||
private function resolveHasNeverFuncCall(FunctionLike $functionLike): bool
|
||||
{
|
||||
$hasNeverType = false;
|
||||
|
||||
foreach ((array) $functionLike->stmts as $stmt) {
|
||||
if ($stmt instanceof Expression) {
|
||||
$stmt = $stmt->expr;
|
||||
}
|
||||
|
||||
if ($stmt instanceof Stmt) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stmtType = $this->getStaticType($stmt);
|
||||
if ($stmtType instanceof NeverType) {
|
||||
$hasNeverType = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $hasNeverType;
|
||||
}
|
||||
}
|
|
@ -122,6 +122,23 @@ final class BetterNodeFinder
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of Node
|
||||
* @param array<class-string<T>> $types
|
||||
* @param Node|Node[]|Stmt[] $nodes
|
||||
* @return T[]
|
||||
*/
|
||||
public function findInstancesOf($nodes, array $types): array
|
||||
{
|
||||
$foundInstances = [];
|
||||
foreach ($types as $type) {
|
||||
$currentFoundInstances = $this->findInstanceOf($nodes, $type);
|
||||
$foundInstances = array_merge($foundInstances, $currentFoundInstances);
|
||||
}
|
||||
|
||||
return $foundInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of Node
|
||||
* @param class-string<T> $type
|
||||
|
|
|
@ -400,9 +400,7 @@ final class NodeFactory
|
|||
|
||||
public function createGetterClassMethod(string $propertyName, Type $type): ClassMethod
|
||||
{
|
||||
$getterMethod = 'get' . ucfirst($propertyName);
|
||||
|
||||
$methodBuilder = new MethodBuilder($getterMethod);
|
||||
$methodBuilder = new MethodBuilder('get' . ucfirst($propertyName));
|
||||
$methodBuilder->makePublic();
|
||||
|
||||
$propertyFetch = new PropertyFetch(new Variable(self::THIS), $propertyName);
|
||||
|
@ -420,13 +418,13 @@ final class NodeFactory
|
|||
|
||||
public function createSetterClassMethod(string $propertyName, Type $type): ClassMethod
|
||||
{
|
||||
$getterMethod = 'set' . ucfirst($propertyName);
|
||||
$methodBuilder = new MethodBuilder('set' . ucfirst($propertyName));
|
||||
$methodBuilder->makePublic();
|
||||
|
||||
$variable = new Variable($propertyName);
|
||||
|
||||
$methodBuilder = new MethodBuilder($getterMethod);
|
||||
$methodBuilder->makePublic();
|
||||
$methodBuilder->addParam(new Param($variable));
|
||||
$param = $this->createParamWithType($variable, $type);
|
||||
$methodBuilder->addParam($param);
|
||||
|
||||
$propertyFetch = new PropertyFetch(new Variable(self::THIS), $propertyName);
|
||||
$assign = new Assign($propertyFetch, $variable);
|
||||
|
@ -753,4 +751,13 @@ final class NodeFactory
|
|||
/** @var BooleanAnd $booleanAnd */
|
||||
return $booleanAnd;
|
||||
}
|
||||
|
||||
private function createParamWithType(Variable $variable, Type $type): Param
|
||||
{
|
||||
$param = new Param($variable);
|
||||
|
||||
$phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type);
|
||||
$param->type = $phpParserTypeNode;
|
||||
return $param;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,12 @@ final class PhpVersion
|
|||
*/
|
||||
public const PHP_80 = 80000;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @var int
|
||||
*/
|
||||
public const PHP_81 = 81000;
|
||||
|
||||
/**
|
||||
* @api
|
||||
* @var int
|
||||
|
|
|
@ -194,4 +194,10 @@ final class PhpVersionFeature
|
|||
* @var int
|
||||
*/
|
||||
public const ATTRIBUTES = PhpVersion::PHP_80;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/noreturn_type
|
||||
* @var int
|
||||
*/
|
||||
public const NEVER_TYPE = PhpVersion::PHP_81;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user