From 82d64192774fdea9e1ddc95e2c1945aa8107ec68 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 15 Sep 2025 21:18:51 +0200 Subject: [PATCH 1/5] Avoid crashing on enum without value --- src/Reflection/ClassReflection.php | 13 ++++++++-- .../Analyser/AnalyserIntegrationTest.php | 9 +++++++ tests/PHPStan/Analyser/data/bug-7927.php | 26 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-7927.php diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index a6404b7253..9142656c2b 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -3,6 +3,7 @@ namespace PHPStan\Reflection; use Attribute; +use LogicException; use PhpParser\Node\Arg; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; @@ -965,7 +966,11 @@ public function getEnumCases(): array foreach ($this->reflection->getCases() as $case) { $valueType = null; if ($case instanceof ReflectionEnumBackedCase) { - $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); + try { + $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); + } catch (LogicException) { + // Enum case does not have a value + } } $caseName = $case->getName(); $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); @@ -992,7 +997,11 @@ public function getEnumCase(string $name): EnumCaseReflection $case = $this->reflection->getCase($name); $valueType = null; if ($case instanceof ReflectionEnumBackedCase) { - $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); + try { + $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); + } catch (LogicException) { + // Enum case does not have a value + } } $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index e7f26496b8..9636fd501f 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1163,6 +1163,15 @@ public function testBug8537(): void $this->assertNoErrors($errors); } + #[RequiresPhp('>= 8.0')] + public function testBug7927(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-7927.php'); + $this->assertCount(2, $errors); + $this->assertSame('Enum case Bug7927\Test::One does not have a value but the enum is backed with the "int" type.', $errors[0]->getMessage()); + $this->assertSame('Enum case Bug7927\Test::Two does not have a value but the enum is backed with the "int" type.', $errors[1]->getMessage()); + } + public function testBug8146(): void { $errors = $this->runAnalyse(__DIR__ . '/data/bug-8146b.php'); diff --git a/tests/PHPStan/Analyser/data/bug-7927.php b/tests/PHPStan/Analyser/data/bug-7927.php new file mode 100644 index 0000000000..b5ed5d572c --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-7927.php @@ -0,0 +1,26 @@ +name}'."); + } +} + +echo doIt($v); From 0787189fdb6990eb86f05a5ca9e79dc930914bc0 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 15 Sep 2025 21:25:18 +0200 Subject: [PATCH 2/5] Debug --- tests/PHPStan/Analyser/AnalyserIntegrationTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 9636fd501f..ec88d167bd 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1163,7 +1163,7 @@ public function testBug8537(): void $this->assertNoErrors($errors); } - #[RequiresPhp('>= 8.0')] + #[RequiresPhp('>= 8.1')] public function testBug7927(): void { $errors = $this->runAnalyse(__DIR__ . '/data/bug-7927.php'); From a608b8cdd224bd18cc301bdd1bf5669b7dc1a21d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 19 Sep 2025 09:23:09 +0200 Subject: [PATCH 3/5] Fix --- src/Reflection/ClassReflection.php | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 9142656c2b..136e4630e4 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -965,12 +965,8 @@ public function getEnumCases(): array $initializerExprContext = InitializerExprContext::fromClassReflection($this); foreach ($this->reflection->getCases() as $case) { $valueType = null; - if ($case instanceof ReflectionEnumBackedCase) { - try { - $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); - } catch (LogicException) { - // Enum case does not have a value - } + if ($case instanceof ReflectionEnumBackedCase && $case->hasValueExpression()) { + $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); } $caseName = $case->getName(); $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); @@ -996,12 +992,8 @@ public function getEnumCase(string $name): EnumCaseReflection $case = $this->reflection->getCase($name); $valueType = null; - if ($case instanceof ReflectionEnumBackedCase) { - try { - $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); - } catch (LogicException) { - // Enum case does not have a value - } + if ($case instanceof ReflectionEnumBackedCase && $case->hasValueExpression()) { + $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); } $attributes = $this->attributeReflectionFactory->fromNativeReflection($case->getAttributes(), InitializerExprContext::fromClass($this->getName(), $this->getFileName())); From 33a4ebb6e37f60f1ddacf1050b823669513dbabe Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 19 Sep 2025 09:24:32 +0200 Subject: [PATCH 4/5] Cs --- src/Reflection/ClassReflection.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index 136e4630e4..ad0acf9dbb 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -3,7 +3,6 @@ namespace PHPStan\Reflection; use Attribute; -use LogicException; use PhpParser\Node\Arg; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; From 78bb13b884697dac36fd79a25ea6cd0430e7f22d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 6 Oct 2025 19:43:42 +0200 Subject: [PATCH 5/5] Fix --- src/Reflection/ClassReflection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Reflection/ClassReflection.php b/src/Reflection/ClassReflection.php index ad0acf9dbb..256dcdd69b 100644 --- a/src/Reflection/ClassReflection.php +++ b/src/Reflection/ClassReflection.php @@ -964,7 +964,7 @@ public function getEnumCases(): array $initializerExprContext = InitializerExprContext::fromClassReflection($this); foreach ($this->reflection->getCases() as $case) { $valueType = null; - if ($case instanceof ReflectionEnumBackedCase && $case->hasValueExpression()) { + if ($case instanceof ReflectionEnumBackedCase && $case->hasBackingValue()) { $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), $initializerExprContext); } $caseName = $case->getName(); @@ -991,7 +991,7 @@ public function getEnumCase(string $name): EnumCaseReflection $case = $this->reflection->getCase($name); $valueType = null; - if ($case instanceof ReflectionEnumBackedCase && $case->hasValueExpression()) { + if ($case instanceof ReflectionEnumBackedCase && $case->hasBackingValue()) { $valueType = $this->initializerExprTypeResolver->getType($case->getValueExpression(), InitializerExprContext::fromClassReflection($this)); }