diff --git a/conf/config.level0.neon b/conf/config.level0.neon index 94180093b8..e7f78dc3e2 100644 --- a/conf/config.level0.neon +++ b/conf/config.level0.neon @@ -31,6 +31,7 @@ rules: - PHPStan\Rules\Classes\InvalidPromotedPropertiesRule - PHPStan\Rules\Classes\NewStaticRule - PHPStan\Rules\Classes\NonClassAttributeClassRule + - PHPStan\Rules\Classes\TraitAttributeClassRule - PHPStan\Rules\Exceptions\ThrowExpressionRule - PHPStan\Rules\Functions\ArrowFunctionReturnNullsafeByRefRule - PHPStan\Rules\Functions\CallToFunctionParametersRule diff --git a/src/Rules/Classes/TraitAttributeClassRule.php b/src/Rules/Classes/TraitAttributeClassRule.php new file mode 100644 index 0000000000..454d063a3a --- /dev/null +++ b/src/Rules/Classes/TraitAttributeClassRule.php @@ -0,0 +1,37 @@ + + */ +class TraitAttributeClassRule implements Rule +{ + + public function getNodeType(): string + { + return Node\Stmt\Trait_::class; + } + + public function processNode(Node $node, Scope $scope): array + { + foreach ($node->attrGroups as $attrGroup) { + foreach ($attrGroup->attrs as $attr) { + $name = $attr->name->toLowerString(); + if ($name === 'attribute') { + return [ + RuleErrorBuilder::message('Trait cannot be an Attribute class.')->build(), + ]; + } + } + } + + return []; + } + +} diff --git a/tests/PHPStan/Rules/Classes/TraitAttributeClassRuleTest.php b/tests/PHPStan/Rules/Classes/TraitAttributeClassRuleTest.php new file mode 100644 index 0000000000..fc08dcfe74 --- /dev/null +++ b/tests/PHPStan/Rules/Classes/TraitAttributeClassRuleTest.php @@ -0,0 +1,32 @@ + + */ +class TraitAttributeClassRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return new TraitAttributeClassRule(); + } + + public function testRule(): void + { + if (!self::$useStaticReflectionProvider && PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Test requires PHP 8.0.'); + } + $this->analyse([__DIR__ . '/data/non-class-attribute-class.php'], [ + [ + 'Trait cannot be an Attribute class.', + 11, + ], + ]); + } + +}