Skip to content
20 changes: 20 additions & 0 deletions build/more-enum-adapter-errors.neon
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,23 @@ parameters:
rawMessage: 'Parameter #2 $reflection of method PHPStan\Reflection\ClassReflectionFactory::create() expects ReflectionClass, PHPStan\BetterReflection\Reflection\Adapter\ReflectionClass|PHPStan\BetterReflection\Reflection\Adapter\ReflectionEnum given.'
count: 1
path: ../src/Analyser/NodeScopeResolver.php

-
rawMessage: 'Right side of || is always false.'
count: 1
path: ../src/Reflection/Php/PhpClassReflectionExtension.php

-
rawMessage: 'If condition is always true.'
count: 1
path: ../src/Reflection/Php/PhpClassReflectionExtension.php

-
rawMessage: 'Result of && is always false.'
count: 1
path: ../src/Reflection/Php/PhpClassReflectionExtension.php

-
rawMessage: 'Strict comparison using === between class-string and ''UnitEnum'' will always evaluate to false.'
count: 1
path: ../src/Reflection/Php/PhpClassReflectionExtension.php
47 changes: 33 additions & 14 deletions src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionParameter;
use PHPStan\BetterReflection\Reflection\Adapter\ReflectionProperty;
use PHPStan\Parser\Parser;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDoc\PhpDocInheritanceResolver;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
use PHPStan\PhpDoc\StubPhpDocProvider;
Expand All @@ -38,6 +39,8 @@
use PHPStan\Reflection\SignatureMap\SignatureMapProvider;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\Accessory\AccessoryDecimalIntegerStringType;
use PHPStan\Type\Accessory\AccessoryNonFalsyStringType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
use PHPStan\Type\Constant\ConstantStringType;
Expand All @@ -51,6 +54,7 @@
use PHPStan\Type\Generic\TemplateTypeVariance;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypehintHelper;
Expand Down Expand Up @@ -103,6 +107,7 @@ public function __construct(
private AttributeReflectionFactory $attributeReflectionFactory,
private ParameterAllowedConstantsMapProvider $allowedConstantsMapProvider,
private bool $inferPrivatePropertyTypeFromConstructor,
private PhpVersion $phpVersion,
)
{
}
Expand Down Expand Up @@ -202,32 +207,46 @@ private function createProperty(
));
}

if ($declaringClassReflection->isEnum()) {
$isUnitEnumInterfaceNameProperty = $this->phpVersion->supportsEnums()
&& $propertyName === 'name'
&& $declaringClassName === 'UnitEnum';

if ($declaringClassReflection->isEnum() || $isUnitEnumInterfaceNameProperty) {
if (
$propertyName === 'name'
|| ($declaringClassReflection->isBackedEnum() && $propertyName === 'value')
) {
$types = [];
foreach ($classReflection->getEnumCases() as $name => $case) {
if ($propertyName === 'name') {
$types[] = new ConstantStringType($name);
continue;
}
if ($declaringClassReflection->isEnum()) {
$types = [];
foreach ($classReflection->getEnumCases() as $name => $case) {
if ($propertyName === 'name') {
$types[] = new ConstantStringType($name);
continue;
}

$value = $case->getBackingValueType();
if ($value === null) {
throw new ShouldNotHappenException();
}

$value = $case->getBackingValueType();
if ($value === null) {
throw new ShouldNotHappenException();
$types[] = $value;
}

$types[] = $value;
$phpDocType = TypeCombinator::union(...$types);
$nativeType = new MixedType();
} else {
$phpDocType = TypeCombinator::intersect(
new StringType(),
new AccessoryNonFalsyStringType(),
new AccessoryDecimalIntegerStringType(inverse: true),
);
$nativeType = new StringType();
}

$phpDocType = TypeCombinator::union(...$types);

return new PhpPropertyReflection(
$declaringClassReflection,
null,
new MixedType(),
$nativeType,
$phpDocType,
$phpDocType,
$classReflection->getNativeReflection()->getProperty($propertyName),
Expand Down
43 changes: 43 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-14839.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php // lint >= 8.1

declare(strict_types=1);

namespace Bug14839;

use function PHPStan\Testing\assertType;

enum Foo
{

case A;
case B;

}

enum Bar: string
{

case A = 'a';
case B = 'b';

}

function test(Foo $foo, Bar $bar, \UnitEnum $u, \BackedEnum $b): void
{
assertType("'A'|'B'", $foo->name);
assertType("'A'|'B'", $bar->name);
assertType("'a'|'b'", $bar->value);
assertType('non-decimal-int-string&non-falsy-string', $u->name);
assertType('non-decimal-int-string&non-falsy-string', $b->name);
// `value` stays as its native `int|string`: unlike a case name, a backing value may legitimately be "" or "0".
assertType('int|string', $b->value);
Comment thread
staabm marked this conversation as resolved.
}

/**
* @template T of \UnitEnum
* @param T $enum
*/
function testTemplate($enum): void
{
assertType('non-decimal-int-string&non-falsy-string', $enum->name);
}
Loading