Skip to content

Commit 5b75d94

Browse files
committed
fix: EnumCaseToPascalCaseRector bugs
1 parent f4003bb commit 5b75d94

3 files changed

Lines changed: 84 additions & 28 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\Fixture;
4+
5+
enum CaseOnSelf: string
6+
{
7+
case PENDING = 'pending';
8+
9+
function isPending(): bool
10+
{
11+
return $this === self::PENDING;
12+
}
13+
}
14+
15+
?>
16+
-----
17+
<?php
18+
19+
namespace Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\Fixture;
20+
21+
enum CaseOnSelf: string
22+
{
23+
case Pending = 'pending';
24+
25+
function isPending(): bool
26+
{
27+
return $this === self::Pending;
28+
}
29+
}
30+
31+
?>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\Fixture;
4+
5+
enum SkipEnumConst
6+
{
7+
const FOO = 'foo';
8+
9+
case Pending;
10+
11+
function test(): void
12+
{
13+
echo self::FOO;
14+
}
15+
}

rules/CodingStyle/Rector/Enum_/EnumCaseToPascalCaseRector.php

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44

55
namespace Rector\CodingStyle\Rector\Enum_;
66

7+
use PHPStan\BetterReflection\Reflector\DefaultReflector;
8+
use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
9+
use PHPStan\Reflection\ClassReflection;
710
use PhpParser\Node;
811
use PhpParser\Node\Expr\ClassConstFetch;
912
use PhpParser\Node\Identifier;
1013
use PhpParser\Node\Name;
1114
use PhpParser\Node\Stmt\Enum_;
1215
use PhpParser\Node\Stmt\EnumCase;
13-
use PHPStan\BetterReflection\Reflection\ReflectionEnum;
14-
use PHPStan\BetterReflection\Reflector\DefaultReflector;
15-
use PHPStan\BetterReflection\Reflector\Exception\IdentifierNotFound;
16-
use PHPStan\Reflection\ReflectionProvider;
1716
use Rector\Configuration\Option;
1817
use Rector\Configuration\Parameter\SimpleParameterProvider;
1918
use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider;
@@ -24,11 +23,11 @@
2423

2524
/**
2625
* @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\EnumCaseToPascalCaseRectorTest
26+
* @see \Rector\Tests\CodingStyle\Rector\Enum_\EnumCaseToPascalCaseRector\WithAutoloadPathsTest
2727
*/
2828
final class EnumCaseToPascalCaseRector extends AbstractRector
2929
{
3030
public function __construct(
31-
private readonly ReflectionProvider $reflectionProvider,
3231
private readonly DynamicSourceLocatorProvider $dynamicSourceLocatorProvider,
3332
) {
3433
}
@@ -122,43 +121,54 @@ private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Nod
122121
return null;
123122
}
124123

125-
if ($this->nodeTypeResolver->getType($classConstFetch->class)->isEnum()->no()) {
124+
$constName = $classConstFetch->name->toString();
125+
$pascalCaseName = $this->convertToPascalCase($constName);
126+
127+
// short circuit if already in pascal case
128+
if ($constName === $pascalCaseName) {
126129
return null;
127130
}
128131

129-
$constName = $classConstFetch->name->toString();
132+
$classReflection = $this->nodeTypeResolver->getType($classConstFetch->class)
133+
->getObjectClassReflections()[0] ?? null;
134+
135+
if ($classReflection === null || ! $classReflection->isEnum()) {
136+
return null;
137+
}
130138

131-
// Skip "class" constant
132-
if ($constName === 'class') {
139+
if (! $this->isEnumCase($classReflection, $constName, $pascalCaseName)) {
133140
return null;
134141
}
135142

136-
$enumClassName = $classConstFetch->class->toString();
137-
if (! $this->reflectionProvider->hasClass($enumClassName)) {
143+
if ($this->isUsedOutsideOfProject($classConstFetch->class)) {
138144
return null;
139145
}
140146

147+
$classConstFetch->name = new Identifier($pascalCaseName);
148+
return $classConstFetch;
149+
}
150+
151+
private function isUsedOutsideOfProject(Name $name): bool
152+
{
153+
if (in_array($name->toString(), ['self', 'static'], true)) {
154+
return false;
155+
}
156+
141157
$sourceLocator = $this->dynamicSourceLocatorProvider->provide();
142158
$defaultReflector = new DefaultReflector($sourceLocator);
143159

144160
try {
145-
$classIdentifier = $defaultReflector->reflectClass($classConstFetch->class->toString());
161+
$classIdentifier = $defaultReflector->reflectClass($name->toString());
146162
} catch (IdentifierNotFound) {
147163
// source is outside the paths defined in withPaths(), eg: vendor
148-
return null;
149-
}
150-
151-
// ensure exactly ReflectionEnum
152-
if (! $classIdentifier instanceof ReflectionEnum) {
153-
return null;
164+
return true;
154165
}
155166

156-
// ensure not part of definition in ->withAutoloadPaths()
157167
$fileTarget = $classIdentifier->getFileName();
158168

159169
// possibly native
160170
if ($fileTarget === null) {
161-
return null;
171+
return true;
162172
}
163173

164174
$autoloadPaths = SimpleParameterProvider::provideArrayParameter(Option::AUTOLOAD_PATHS);
@@ -168,21 +178,21 @@ private function refactorClassConstFetch(ClassConstFetch $classConstFetch): ?Nod
168178
$normalizedAutoloadPath = PathNormalizer::normalize($autoloadPath);
169179

170180
if ($autoloadPath === $fileTarget) {
171-
return null;
181+
return true;
172182
}
173183

174184
if (str_starts_with($normalizedFileTarget, $normalizedAutoloadPath . '/')) {
175-
return null;
185+
return true;
176186
}
177187
}
178188

179-
$pascalCaseName = $this->convertToPascalCase($constName);
180-
if ($constName !== $pascalCaseName) {
181-
$classConstFetch->name = new Identifier($pascalCaseName);
182-
return $classConstFetch;
183-
}
189+
return false;
190+
}
184191

185-
return null;
192+
private function isEnumCase(ClassReflection $classReflection, string $name, string $pascalName): bool
193+
{
194+
// the enum case might have already been renamed, need to check both
195+
return $classReflection->hasEnumCase($name) || $classReflection->hasEnumCase($pascalName);
186196
}
187197

188198
private function convertToPascalCase(string $name): string

0 commit comments

Comments
 (0)