diff --git a/config/set/php85.php b/config/set/php85.php index a81f317de0a..afb09306273 100644 --- a/config/set/php85.php +++ b/config/set/php85.php @@ -11,6 +11,7 @@ use Rector\Php85\Rector\ClassMethod\NullDebugInfoReturnRector; use Rector\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector; use Rector\Php85\Rector\FuncCall\RemoveFinfoBufferContextArgRector; +use Rector\Php85\Rector\Switch_\ColonAfterSwitchCaseRector; use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector; use Rector\Removing\ValueObject\RemoveFuncCallArg; use Rector\Renaming\Rector\Cast\RenameCastRector; @@ -30,6 +31,7 @@ RemoveFinfoBufferContextArgRector::class, NullDebugInfoReturnRector::class, DeprecatedAnnotationToDeprecatedAttributeRector::class, + ColonAfterSwitchCaseRector::class, ] ); diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php new file mode 100644 index 00000000000..be28a0bdf15 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/ColonAfterSwitchCaseRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..07dfd311e52 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/fixture.php.inc @@ -0,0 +1,53 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc new file mode 100644 index 00000000000..63ea8da70e2 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/Fixture/skip_with_colon.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php new file mode 100644 index 00000000000..6f357c18365 --- /dev/null +++ b/rules-tests/Php85/Rector/Switch_/ColonAfterSwitchCaseRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(ColonAfterSwitchCaseRector::class); +}; diff --git a/rules-tests/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector/WrapFuncCallWithPhpVersionIdCheckerRectorTest.php b/rules-tests/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector/WrapFuncCallWithPhpVersionIdCheckerRectorTest.php index 6a3fbf7ad80..03384cc4e38 100644 --- a/rules-tests/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector/WrapFuncCallWithPhpVersionIdCheckerRectorTest.php +++ b/rules-tests/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector/WrapFuncCallWithPhpVersionIdCheckerRectorTest.php @@ -1,5 +1,7 @@ converter->convert($node); + return $this->deprecatedAnnotationToDeprecatedAttributeConverter->convert($node); } public function provideMinPhpVersion(): int diff --git a/rules/Php85/Rector/Const_/DeprecatedAnnotationToDeprecatedAttributeRector.php b/rules/Php85/Rector/Const_/DeprecatedAnnotationToDeprecatedAttributeRector.php index d06031cb0c1..153aced3225 100644 --- a/rules/Php85/Rector/Const_/DeprecatedAnnotationToDeprecatedAttributeRector.php +++ b/rules/Php85/Rector/Const_/DeprecatedAnnotationToDeprecatedAttributeRector.php @@ -19,7 +19,7 @@ final class DeprecatedAnnotationToDeprecatedAttributeRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private readonly DeprecatedAnnotationToDeprecatedAttributeConverter $converter, + private readonly DeprecatedAnnotationToDeprecatedAttributeConverter $deprecatedAnnotationToDeprecatedAttributeConverter, ) { } @@ -52,7 +52,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - return $this->converter->convert($node); + return $this->deprecatedAnnotationToDeprecatedAttributeConverter->convert($node); } public function provideMinPhpVersion(): int diff --git a/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php new file mode 100644 index 00000000000..6cdacd3613e --- /dev/null +++ b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php @@ -0,0 +1,114 @@ +file->getOldTokens(); + + foreach ($node->cases as $key => $case) { + $cond = $case->cond; + + if (! $cond instanceof Expr) { + continue; + } + + $endTokenPos = $cond->getEndTokenPos(); + + if ($endTokenPos < 0) { + continue; + } + + $startCaseStmtsPos = count($case->stmts) === 0 + ? ( + isset($node->cases[$key + 1]) + ? $node->cases[$key + 1]->getStartTokenPos() + : $node->getEndTokenPos() + ) + : $case->stmts[0]->getStartTokenPos(); + + if ($startCaseStmtsPos < 0) { + continue; + } + + $nextTokenPos = $endTokenPos; + while (++$nextTokenPos < $startCaseStmtsPos) { + if (! isset($oldTokens[$nextTokenPos])) { + continue 2; + } + + $nextToken = $oldTokens[$nextTokenPos]; + if (trim($nextToken->text) === '') { + continue; + } + + if ($nextToken->text === ':') { + continue 2; + } + + if ($nextToken->text === ';') { + $hasChanged = true; + $nextToken->text = ':'; + continue 2; + } + } + } + + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::COLON_AFTER_SWITCH_CASE; + } +} diff --git a/rules/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector.php b/rules/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector.php index 850646f0321..87c289f37b9 100644 --- a/rules/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector.php +++ b/rules/Transform/Rector/FuncCall/WrapFuncCallWithPhpVersionIdCheckerRector.php @@ -81,7 +81,11 @@ public function refactor(Node $node): null|Node|int $hasChanged = false; foreach ($node->stmts as $key => $stmt) { - if (! $stmt instanceof Expression || ! $stmt->expr instanceof FuncCall) { + if (! $stmt instanceof Expression) { + continue; + } + + if (! $stmt->expr instanceof FuncCall) { continue; } @@ -121,32 +125,33 @@ public function configure(array $configuration): void $this->wrapFuncCallWithPhpVersionIdCheckers = $configuration; } - private function isWrappedFuncCall(StmtsAwareInterface $node): bool + private function isWrappedFuncCall(StmtsAwareInterface $stmtsAware): bool { - if (! $node instanceof If_) { + if (! $stmtsAware instanceof If_) { return false; } - $phpVersionId = $this->getPhpVersionId($node->cond); - if ($phpVersionId === null) { + $phpVersionId = $this->getPhpVersionId($stmtsAware->cond); + if (!$phpVersionId instanceof Int_) { return false; } - if (count($node->stmts) !== 1) { + if (count($stmtsAware->stmts) !== 1) { return false; } - $childStmt = $node->stmts[0]; + $childStmt = $stmtsAware->stmts[0]; if (! $childStmt instanceof Expression || ! $childStmt->expr instanceof FuncCall) { return false; } foreach ($this->wrapFuncCallWithPhpVersionIdCheckers as $wrapFuncCallWithPhpVersionIdChecker) { - if ( - $this->getName($childStmt->expr) !== $wrapFuncCallWithPhpVersionIdChecker->getFunctionName() - || $phpVersionId->value !== $wrapFuncCallWithPhpVersionIdChecker->getPhpVersionId() - ) { + if ($this->getName($childStmt->expr) !== $wrapFuncCallWithPhpVersionIdChecker->getFunctionName()) { + continue; + } + + if ($phpVersionId->value !== $wrapFuncCallWithPhpVersionIdChecker->getPhpVersionId()) { continue; } diff --git a/src/PhpAttribute/DeprecatedAnnotationToDeprecatedAttributeConverter.php b/src/PhpAttribute/DeprecatedAnnotationToDeprecatedAttributeConverter.php index 19401e28b09..3cdfb86e86e 100644 --- a/src/PhpAttribute/DeprecatedAnnotationToDeprecatedAttributeConverter.php +++ b/src/PhpAttribute/DeprecatedAnnotationToDeprecatedAttributeConverter.php @@ -25,7 +25,7 @@ use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory; -final class DeprecatedAnnotationToDeprecatedAttributeConverter +final readonly class DeprecatedAnnotationToDeprecatedAttributeConverter { /** * @see https://regex101.com/r/qNytVk/1 @@ -40,10 +40,10 @@ final class DeprecatedAnnotationToDeprecatedAttributeConverter private const START_STAR_SPACED_REGEX = '#^ *\*#ms'; public function __construct( - private readonly PhpDocTagRemover $phpDocTagRemover, - private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, - private readonly DocBlockUpdater $docBlockUpdater, - private readonly PhpDocInfoFactory $phpDocInfoFactory, + private PhpDocTagRemover $phpDocTagRemover, + private PhpAttributeGroupFactory $phpAttributeGroupFactory, + private DocBlockUpdater $docBlockUpdater, + private PhpDocInfoFactory $phpDocInfoFactory, ) { } diff --git a/src/ValueObject/PhpVersionFeature.php b/src/ValueObject/PhpVersionFeature.php index 4cde90e8564..a37b164c45b 100644 --- a/src/ValueObject/PhpVersionFeature.php +++ b/src/ValueObject/PhpVersionFeature.php @@ -786,4 +786,10 @@ final class PhpVersionFeature * @var int */ public const DEPRECATED_ATTRIBUTE_ON_CONSTANT = PhpVersion::PHP_85; + + /** + * @see https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_semicolon_after_case_in_switch_statement + * @var int + */ + public const COLON_AFTER_SWITCH_CASE = PhpVersion::PHP_85; }