Skip to content

Commit 9da3f6d

Browse files
committed
#767: Detect enum becoming non-enum and vice-versa as BC break
1 parent 61231a8 commit 9da3f6d

File tree

2 files changed

+47
-13
lines changed

2 files changed

+47
-13
lines changed

src/DetectChanges/BCBreak/ClassBased/CasesChanged.php

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@
1616

1717
class CasesChanged implements ClassBased
1818
{
19-
public function __invoke(ReflectionClass $fromClass, ReflectionClass $toEnum): Changes
19+
public function __invoke(ReflectionClass $fromClass, ReflectionClass $toClass): Changes
2020
{
21-
if (! $fromClass instanceof ReflectionEnum) {
21+
$fromEnumName = $fromClass->getName();
22+
$fromKind = $this->kindOf($fromClass);
23+
$toKind = $this->kindOf($toClass);
24+
25+
if (! $fromClass instanceof ReflectionEnum && ! $toClass instanceof ReflectionEnum) {
2226
return Changes::empty();
2327
}
2428

25-
if (! $toEnum instanceof ReflectionEnum) {
26-
return Changes::empty();
29+
if (! $fromClass instanceof ReflectionEnum && $toClass instanceof ReflectionEnum) {
30+
return Changes::fromList(Change::changed("$fromKind " . $fromEnumName . " became enum"));
2731
}
2832

29-
$fromEnumName = $fromClass->getName();
33+
if ($fromClass instanceof ReflectionEnum && ! $toClass instanceof ReflectionEnum) {
34+
return Changes::fromList(Change::changed("enum " . $fromEnumName . " became " . $toKind));
35+
}
3036

3137
$addedCases = array_filter(
32-
$toEnum->getCases(),
38+
$toClass->getCases(),
3339
static function (ReflectionEnumCase $case) use ($fromClass): bool {
3440
if (self::isInternalDocComment($case->getDocComment())) {
3541
return false;
@@ -42,17 +48,17 @@ static function (ReflectionEnumCase $case) use ($fromClass): bool {
4248

4349
$removedCases = array_filter(
4450
$fromClass->getCases(),
45-
static function (ReflectionEnumCase $case) use ($toEnum): bool {
51+
static function (ReflectionEnumCase $case) use ($toClass): bool {
4652
if (self::isInternalDocComment($case->getDocComment())) {
4753
return false;
4854
}
4955

50-
return ! $toEnum->hasCase($case->getName());
56+
return ! $toClass->hasCase($case->getName());
5157
},
5258
);
5359

5460
$internalisedCases = array_filter(
55-
$toEnum->getCases(),
61+
$toClass->getCases(),
5662
static function (ReflectionEnumCase $case) use ($fromClass) {
5763
if (! self::isInternalDocComment($case->getDocComment())) {
5864
return false;
@@ -105,4 +111,22 @@ private static function isInternalDocComment(string|null $comment): bool
105111
return $comment !== null
106112
&& Regex\matches($comment, '/\s+@internal\s+/');
107113
}
114+
115+
/** @psalm-return 'enum'|'interface'|'trait'|'class' */
116+
private function kindOf(ReflectionClass $reflectionClass): string
117+
{
118+
if ($reflectionClass->isEnum()) {
119+
return 'enum';
120+
}
121+
122+
if ($reflectionClass->isInterface()) {
123+
return 'interface';
124+
}
125+
126+
if ($reflectionClass->isTrait()) {
127+
return 'trait';
128+
}
129+
130+
return 'class';
131+
}
108132
}

test/unit/DetectChanges/BCBreak/ClassBased/EnumCasesChangedTest.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function testDiffs(
3838
);
3939
}
4040

41-
public function testReturnsNoChangesIfOldEnumIsNotEnum(): void
41+
public function testReturnsClassBecameEnumError(): void
4242
{
4343
// EnumCasesChanged should not be called when the old symbol is not an Enum. If it does it will
4444
// just return an empty list.
@@ -48,10 +48,15 @@ public function testReturnsNoChangesIfOldEnumIsNotEnum(): void
4848
ReflectionClass::createFromName(DummyEnum::class),
4949
);
5050

51-
$this->assertSame(0, $changes->count());
51+
$this->assertEquals(
52+
[
53+
Change::changed("class stdClass became enum")
54+
],
55+
\iterator_to_array($changes)
56+
);
5257
}
5358

54-
public function testReturnsNoChangesIfNewEnumIsNotEnum(): void
59+
public function testReturnsEnumBecameClassError(): void
5560
{
5661
// EnumCasesChanged should not be called when the old symbol is not an Enum. If it does it will
5762
// just return an empty list.
@@ -61,7 +66,12 @@ public function testReturnsNoChangesIfNewEnumIsNotEnum(): void
6166
ReflectionClass::createFromName(stdClass::class),
6267
);
6368

64-
$this->assertSame(0, $changes->count());
69+
$this->assertEquals(
70+
[
71+
Change::changed("enum RoaveTest\BackwardCompatibility\DetectChanges\BCBreak\ClassBased\DummyEnum became class")
72+
],
73+
\iterator_to_array($changes)
74+
);
6575
}
6676

6777
/**

0 commit comments

Comments
 (0)