Skip to content

Commit 3adc91d

Browse files
committedMar 6, 2023
Fix value-of for multiple enums
1 parent dda95b4 commit 3adc91d

File tree

4 files changed

+68
-22
lines changed

4 files changed

+68
-22
lines changed
 

‎src/PhpDoc/TypeNodeResolver.php

+12-22
Original file line numberDiff line numberDiff line change
@@ -628,32 +628,22 @@ private function resolveGenericTypeNode(GenericTypeNode $typeNode, NameScope $na
628628
return new ErrorType();
629629
} elseif ($mainTypeName === 'value-of') {
630630
if (count($genericTypes) === 1) { // value-of<ValueType>
631-
$genericTypeObjectClassNames = $genericTypes[0]->getObjectClassNames();
632-
if (count($genericTypeObjectClassNames) > 1) {
633-
throw new ShouldNotHappenException();
634-
}
635-
636-
if ($genericTypeObjectClassNames !== []) {
637-
if ($this->getReflectionProvider()->hasClass($genericTypeObjectClassNames[0])) {
638-
$classReflection = $this->getReflectionProvider()->getClass($genericTypeObjectClassNames[0]);
639-
640-
if ($classReflection->isBackedEnum()) {
641-
$cases = [];
642-
foreach ($classReflection->getEnumCases() as $enumCaseReflection) {
643-
$backingType = $enumCaseReflection->getBackingValueType();
644-
if ($backingType === null) {
645-
continue;
646-
}
647-
648-
$cases[] = $backingType;
649-
}
650-
651-
return TypeCombinator::union(...$cases);
631+
$genericType = $genericTypes[0];
632+
if ($genericType->isEnum()->yes()) {
633+
$valueTypes = [];
634+
foreach ($genericType->getEnumCases() as $enumCase) {
635+
$valueType = $enumCase->getBackingValueType();
636+
if ($valueType === null) {
637+
continue;
652638
}
639+
640+
$valueTypes[] = $valueType;
653641
}
642+
643+
return TypeCombinator::union(...$valueTypes);
654644
}
655645

656-
$type = new ValueOfType($genericTypes[0]);
646+
$type = new ValueOfType($genericType);
657647
return $type->isResolvable() ? $type->resolve() : $type;
658648
}
659649

‎src/Type/Enum/EnumCaseObjectType.php

+20
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,26 @@ public function getProperty(string $propertyName, ClassMemberAccessAnswerer $sco
136136
return parent::getProperty($propertyName, $scope);
137137
}
138138

139+
public function getBackingValueType(): ?Type
140+
{
141+
$classReflection = $this->getClassReflection();
142+
if ($classReflection === null) {
143+
return null;
144+
}
145+
146+
if (!$classReflection->isBackedEnum()) {
147+
return null;
148+
}
149+
150+
if ($classReflection->hasEnumCase($this->enumCaseName)) {
151+
$enumCase = $classReflection->getEnumCase($this->enumCaseName);
152+
153+
return $enumCase->getBackingValueType();
154+
}
155+
156+
return null;
157+
}
158+
139159
public function generalize(GeneralizePrecision $precision): Type
140160
{
141161
return new parent($this->getClassName(), null, $this->getClassReflection());

‎tests/PHPStan/Analyser/AnalyserIntegrationTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,12 @@ public function testSkipCheckNoGenericClasses(): void
11541154
$this->assertSame('Method SkipCheckNoGenericClasses\Foo::doFoo() has parameter $i with generic class LimitIterator but does not specify its types: TKey, TValue, TIterator', $errors[0]->getMessage());
11551155
}
11561156

1157+
public function testBug8983(): void
1158+
{
1159+
$errors = $this->runAnalyse(__DIR__ . '/data/bug-8983.php');
1160+
$this->assertNoErrors($errors);
1161+
}
1162+
11571163
/**
11581164
* @param string[]|null $allAnalysedFiles
11591165
* @return Error[]
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Bug8983;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
enum Enum1: string
8+
{
9+
10+
case FOO = 'foo';
11+
12+
}
13+
14+
enum Enum2: string
15+
{
16+
17+
case BAR = 'bar';
18+
19+
}
20+
21+
class Foo
22+
{
23+
24+
/** @param value-of<Enum1|Enum2> $bar */
25+
public function doFoo($bar): void
26+
{
27+
assertType("'bar'|'foo'", $bar);
28+
}
29+
30+
}

0 commit comments

Comments
 (0)
Please sign in to comment.