From 903b5bdb8dd65f973b017261456e741d6e91b51e Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:32:59 +0000 Subject: [PATCH 1/2] Report error when using enum case outside of enum - Added new EnumCaseOutsideEnumRule that reports an error when `case` is used in classes or interfaces instead of enums - New regression test in tests/PHPStan/Rules/EnumCases/data/bug-14252.php - The rule is registered at level 0 and uses the identifier `enum.caseOutsideOfEnum` Closes https://github.com/phpstan/phpstan/issues/14252 --- .../EnumCases/EnumCaseOutsideEnumRule.php | 42 +++++++++++++++++++ .../EnumCases/EnumCaseOutsideEnumRuleTest.php | 35 ++++++++++++++++ .../Rules/EnumCases/data/bug-14252.php | 20 +++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/Rules/EnumCases/EnumCaseOutsideEnumRule.php create mode 100644 tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php create mode 100644 tests/PHPStan/Rules/EnumCases/data/bug-14252.php diff --git a/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php b/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php new file mode 100644 index 0000000000..ba0290e5ea --- /dev/null +++ b/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php @@ -0,0 +1,42 @@ + + */ +#[RegisteredRule(level: 0)] +final class EnumCaseOutsideEnumRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Stmt\EnumCase::class; + } + + public function processNode(Node $node, Scope $scope): array + { + if (!$scope->isInClass()) { + return []; + } + + $classReflection = $scope->getClassReflection(); + if ($classReflection->isEnum()) { + return []; + } + + return [ + RuleErrorBuilder::message('Enum case can only be used in enums.') + ->nonIgnorable() + ->identifier('enum.caseOutsideOfEnum') + ->build(), + ]; + } + +} diff --git a/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php b/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php new file mode 100644 index 0000000000..7c86412167 --- /dev/null +++ b/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php @@ -0,0 +1,35 @@ + + */ +class EnumCaseOutsideEnumRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new EnumCaseOutsideEnumRule(); + } + + #[RequiresPhp('>= 8.1')] + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/bug-14252.php'], [ + [ + 'Enum case can only be used in enums.', + 9, + ], + [ + 'Enum case can only be used in enums.', + 14, + ], + ]); + } + +} diff --git a/tests/PHPStan/Rules/EnumCases/data/bug-14252.php b/tests/PHPStan/Rules/EnumCases/data/bug-14252.php new file mode 100644 index 0000000000..d8f86ec336 --- /dev/null +++ b/tests/PHPStan/Rules/EnumCases/data/bug-14252.php @@ -0,0 +1,20 @@ += 8.1 + +declare(strict_types = 1); + +namespace Bug14252; + +class Foo +{ + case Active; +} + +interface Bar +{ + case Active; +} + +enum Qux +{ + case Active; +} From 1d8cddb23f3c077a07867a912f1e415b6a9c0cc4 Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Thu, 12 Mar 2026 21:09:01 +0000 Subject: [PATCH 2/2] Also report enum case outside of enum in traits Add trait test case as requested in review. Handle the trait scope separately since traits are analyzed in the context of their consumer class. Co-Authored-By: Claude Opus 4.6 --- src/Rules/EnumCases/EnumCaseOutsideEnumRule.php | 9 +++++++++ .../Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php | 4 ++++ tests/PHPStan/Rules/EnumCases/data/bug-14252.php | 10 ++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php b/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php index ba0290e5ea..a771a702f3 100644 --- a/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php +++ b/src/Rules/EnumCases/EnumCaseOutsideEnumRule.php @@ -22,6 +22,15 @@ public function getNodeType(): string public function processNode(Node $node, Scope $scope): array { + if ($scope->isInTrait()) { + return [ + RuleErrorBuilder::message('Enum case can only be used in enums.') + ->nonIgnorable() + ->identifier('enum.caseOutsideOfEnum') + ->build(), + ]; + } + if (!$scope->isInClass()) { return []; } diff --git a/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php b/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php index 7c86412167..f29475c527 100644 --- a/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php +++ b/tests/PHPStan/Rules/EnumCases/EnumCaseOutsideEnumRuleTest.php @@ -29,6 +29,10 @@ public function testRule(): void 'Enum case can only be used in enums.', 14, ], + [ + 'Enum case can only be used in enums.', + 19, + ], ]); } diff --git a/tests/PHPStan/Rules/EnumCases/data/bug-14252.php b/tests/PHPStan/Rules/EnumCases/data/bug-14252.php index d8f86ec336..71b75a453e 100644 --- a/tests/PHPStan/Rules/EnumCases/data/bug-14252.php +++ b/tests/PHPStan/Rules/EnumCases/data/bug-14252.php @@ -14,6 +14,16 @@ interface Bar case Active; } +trait Baz +{ + case Active; +} + +class BazConsumer +{ + use Baz; +} + enum Qux { case Active;