From ff8fcf90ed8c9408ff619c92dcda6953dfb96c69 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 28 Jun 2026 15:05:24 +0200 Subject: [PATCH] [CodingStyle] Deprecate EnumCaseToPascalCaseRector as risky change It does not handle enum usage across the whole context. Better handled via IDE with full context, manually, or a custom rule. --- phpstan.neon | 1 + .../EnumCaseToPascalCaseRectorTest.php | 28 --- .../Fixture/case_on_self.php.inc | 31 ---- .../Fixture/enum_used.php.inc | 39 ---- .../Fixture/enum_used_alias.php.inc | 37 ---- .../Fixture/skip_enum_const.php.inc | 15 -- .../skip_enum_usage_in_other_package.php.inc | 13 -- .../Fixture/skip_existing_pascal.php.inc | 11 -- .../Source/StatusEnum.php | 13 -- .../WithAutoloadPathsTest.php | 28 --- .../config/configured_rule.php | 9 - .../config/with_autoload_configured_rule.php | 10 -- .../Enum_/EnumCaseToPascalCaseRector.php | 168 +----------------- 13 files changed, 9 insertions(+), 394 deletions(-) delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_usage_in_other_package.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Source/StatusEnum.php delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php delete mode 100644 rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php diff --git a/phpstan.neon b/phpstan.neon index 0fb815c1ea8..5a90ac5203a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -412,6 +412,7 @@ parameters: - '#Class "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" is missing @see annotation with test case class reference#' - '#Class "Rector\\Php70\\Rector\\StaticCall\\StaticCallOnNonStaticToInstanceCallRector" is missing @see annotation with test case class reference#' - '#Class "Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector" is missing @see annotation with test case class reference#' + - '#Class "Rector\\CodingStyle\\Rector\\Enum_\\EnumCaseToPascalCaseRector" is missing @see annotation with test case class reference#' # false positive - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php deleted file mode 100644 index df26473efb0..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/EnumCaseToPascalCaseRectorTest.php +++ /dev/null @@ -1,28 +0,0 @@ -doTestFile($filePath, true); - } - - 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/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc deleted file mode 100644 index e07d8d31f14..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/case_on_self.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc deleted file mode 100644 index 4d909765650..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used.php.inc +++ /dev/null @@ -1,39 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc deleted file mode 100644 index 033a08bfa62..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/enum_used_alias.php.inc +++ /dev/null @@ -1,37 +0,0 @@ - ------ - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc deleted file mode 100644 index cebfb93ff2c..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_enum_const.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc deleted file mode 100644 index 075ae7d5801..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/Fixture/skip_existing_pascal.php.inc +++ /dev/null @@ -1,11 +0,0 @@ - diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php deleted file mode 100644 index 1b791d6c8f4..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/WithAutoloadPathsTest.php +++ /dev/null @@ -1,28 +0,0 @@ -doTestFile($filePath, true); - } - - public static function provideData(): Iterator - { - return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/with_autoload_configured_rule.php'; - } -} diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php deleted file mode 100644 index 372d2669825..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/configured_rule.php +++ /dev/null @@ -1,9 +0,0 @@ -withRules([EnumCaseToPascalCaseRector::class]); diff --git a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php b/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php deleted file mode 100644 index 8a4fd8fdc29..00000000000 --- a/rules-tests/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector/config/with_autoload_configured_rule.php +++ /dev/null @@ -1,10 +0,0 @@ -withRules([EnumCaseToPascalCaseRector::class]) - ->withAutoloadPaths([__DIR__ . '/../Source']); diff --git a/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php index e3b48e80ded..3a2563f089f 100644 --- a/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php +++ b/rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php @@ -6,32 +6,18 @@ use PhpParser\Node; use PhpParser\Node\Expr\ClassConstFetch; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name; use PhpParser\Node\Stmt\Enum_; -use PhpParser\Node\Stmt\EnumCase; -use PHPStan\BetterReflection\Reflector\DefaultReflector; -use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound; -use PHPStan\Reflection\ClassReflection; -use Rector\Configuration\Option; -use Rector\Configuration\Parameter\SimpleParameterProvider; -use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider; +use Rector\Configuration\Deprecation\Contract\DeprecatedInterface; +use Rector\Exception\ShouldNotHappenException; use Rector\Rector\AbstractRector; -use Rector\Skipper\FileSystem\PathNormalizer; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** - * @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\EnumCaseToPascalCaseRectorTest - * @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\WithAutoloadPathsTest + * @deprecated as risky change that does not handle enum usage across the whole context. Handle it via IDE with full context, manually, or a custom rule instead. */ -final class EnumCaseToPascalCaseRector extends AbstractRector +final class EnumCaseToPascalCaseRector extends AbstractRector implements DeprecatedInterface { - public function __construct( - private readonly DynamicSourceLocatorProvider $dynamicSourceLocatorProvider, - ) { - } - public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -72,147 +58,9 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node instanceof Enum_) { - return $this->refactorEnum($node); - } - - if ($node instanceof ClassConstFetch) { - return $this->refactorClassConstFetch($node); - } - - return null; - } - - public function refactorEnum(Enum_ $enum): Enum_|null - { - $enumName = $this->getName($enum); - if ($enumName === null) { - return null; - } - - $hasChanged = false; - - foreach ($enum->stmts as $stmt) { - if (! $stmt instanceof EnumCase) { - continue; - } - - $currentName = $stmt->name->toString(); - $pascalCaseName = $this->convertToPascalCase($currentName); - - if ($currentName === $pascalCaseName) { - continue; - } - - $stmt->name = new Identifier($pascalCaseName); - $hasChanged = true; - } - - return $hasChanged ? $enum : null; - } - - private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Node - { - if (! $classConstFetch->class instanceof Name) { - return null; - } - - if (! $classConstFetch->name instanceof Identifier) { - return null; - } - - $constName = $classConstFetch->name->toString(); - $pascalCaseName = $this->convertToPascalCase($constName); - - // short circuit if already in pascal case - if ($constName === $pascalCaseName) { - return null; - } - - $classReflection = $this->nodeTypeResolver->getType($classConstFetch->class) - ->getObjectClassReflections()[0] ?? null; - - if ($classReflection === null || ! $classReflection->isEnum()) { - return null; - } - - if (! $this->isEnumCase($classReflection, $constName, $pascalCaseName)) { - return null; - } - - if ($this->isUsedOutsideOfProject($classConstFetch->class)) { - return null; - } - - $classConstFetch->name = new Identifier($pascalCaseName); - return $classConstFetch; - } - - private function isUsedOutsideOfProject(Name $name): bool - { - if (in_array($name->toString(), ['self', 'static'], true)) { - return false; - } - - $sourceLocator = $this->dynamicSourceLocatorProvider->provide(); - $defaultReflector = new DefaultReflector($sourceLocator); - - try { - $classIdentifier = $defaultReflector->reflectClass($name->toString()); - } catch (IdentifierNotFound) { - // source is outside the paths defined in withPaths(), eg: vendor - return true; - } - - $fileTarget = $classIdentifier->getFileName(); - - // possibly native - if ($fileTarget === null) { - return true; - } - - $autoloadPaths = SimpleParameterProvider::provideArrayParameter(Option::AUTOLOAD_PATHS); - $normalizedFileTarget = PathNormalizer::normalize((string) realpath($fileTarget)); - - foreach ($autoloadPaths as $autoloadPath) { - $normalizedAutoloadPath = PathNormalizer::normalize($autoloadPath); - - if ($autoloadPath === $fileTarget) { - return true; - } - - if (str_starts_with($normalizedFileTarget, $normalizedAutoloadPath . '/')) { - return true; - } - } - - return false; - } - - private function isEnumCase(ClassReflection $classReflection, string $name, string $pascalName): bool - { - // the enum case might have already been renamed, need to check both - if ($classReflection->hasEnumCase($name)) { - return true; - } - - return $classReflection->hasEnumCase($pascalName); - } - - private function convertToPascalCase(string $name): string - { - $parts = explode('_', $name); - return implode( - '', - array_map( - fn ($part): string => - // If part is all uppercase, convert to ucfirst(strtolower()) - // If part is already mixed or PascalCase, keep as is except ucfirst - ctype_upper($part) - ? ucfirst(strtolower($part)) - : ucfirst($part), - $parts - ) - ); + throw new ShouldNotHappenException(sprintf( + '"%s" is deprecated as risky change that does not handle enum usage across the whole context. Handle it via IDE with full context, manually, or a custom rule instead', + self::class + )); } }