diff --git a/src/Rules/Classes/ClassAttributesRule.php b/src/Rules/Classes/ClassAttributesRule.php index 319379ab2a..afe9786db0 100644 --- a/src/Rules/Classes/ClassAttributesRule.php +++ b/src/Rules/Classes/ClassAttributesRule.php @@ -5,11 +5,12 @@ use Attribute; use PhpParser\Node; use PHPStan\Analyser\Scope; +use PHPStan\Node\InClassNode; use PHPStan\Rules\AttributesCheck; use PHPStan\Rules\Rule; /** - * @implements Rule + * @implements Rule */ final class ClassAttributesRule implements Rule { @@ -20,14 +21,16 @@ public function __construct(private AttributesCheck $attributesCheck) public function getNodeType(): string { - return Node\Stmt\ClassLike::class; + return InClassNode::class; } public function processNode(Node $node, Scope $scope): array { + $classLikeNode = $node->getOriginalNode(); + return $this->attributesCheck->check( $scope, - $node->attrGroups, + $classLikeNode->attrGroups, Attribute::TARGET_CLASS, 'class', ); diff --git a/tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php b/tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php index 6fa6252277..f0deeee108 100644 --- a/tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php +++ b/tests/PHPStan/Rules/Classes/ClassAttributesRuleTest.php @@ -22,6 +22,10 @@ class ClassAttributesRuleTest extends RuleTestCase { + private bool $checkExplicitMixed = false; + + private bool $checkImplicitMixed = false; + protected function getRule(): Rule { $reflectionProvider = $this->createReflectionProvider(); @@ -29,7 +33,7 @@ protected function getRule(): Rule new AttributesCheck( $reflectionProvider, new FunctionCallParametersCheck( - new RuleLevelHelper($reflectionProvider, true, false, true, false, false, true, false), + new RuleLevelHelper($reflectionProvider, true, false, true, $this->checkExplicitMixed, $this->checkImplicitMixed, true, false), new NullsafeCheck(), new PhpVersion(80000), new UnresolvableTypeHelper(), @@ -148,4 +152,20 @@ public function testAllowDynamicPropertiesAttribute(): void $this->analyse([__DIR__ . '/data/allow-dynamic-properties-attribute.php'], []); } + public function testBug12011(): void + { + if (PHP_VERSION_ID < 80300) { + $this->markTestSkipped('Test requires PHP 8.3.'); + } + + $this->checkExplicitMixed = true; + $this->checkImplicitMixed = true; + $this->analyse([__DIR__ . '/data/bug-12011.php'], [ + [ + 'Parameter #1 $name of attribute class Bug12011\Table constructor expects string|null, int given.', + 23, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Classes/data/bug-12011.php b/tests/PHPStan/Rules/Classes/data/bug-12011.php new file mode 100644 index 0000000000..feb1795d58 --- /dev/null +++ b/tests/PHPStan/Rules/Classes/data/bug-12011.php @@ -0,0 +1,27 @@ +