diff --git a/src/Rules/AttributesCheck.php b/src/Rules/AttributesCheck.php index 205e3994d4..31da561a70 100644 --- a/src/Rules/AttributesCheck.php +++ b/src/Rules/AttributesCheck.php @@ -93,11 +93,14 @@ public function check( $attributeClassName = SprintfHelper::escapeFormatString($attributeClass->getDisplayName()); + $nodeAttributes = $attribute->getAttributes(); + $nodeAttributes['isAttribute'] = true; + $parameterErrors = $this->functionCallParametersCheck->check( ParametersAcceptorSelector::selectSingle($attributeConstructor->getVariants()), $scope, $attributeConstructor->getDeclaringClass()->isBuiltin(), - new New_($attribute->name, $attribute->args, $attribute->getAttributes()), + new New_($attribute->name, $attribute->args, $nodeAttributes), [ 'Attribute class ' . $attributeClassName . ' constructor invoked with %d parameter, %d required.', 'Attribute class ' . $attributeClassName . ' constructor invoked with %d parameters, %d required.', diff --git a/src/Rules/FunctionCallParametersCheck.php b/src/Rules/FunctionCallParametersCheck.php index f672293d66..aee6a284bb 100644 --- a/src/Rules/FunctionCallParametersCheck.php +++ b/src/Rules/FunctionCallParametersCheck.php @@ -169,7 +169,7 @@ public function check( ]; } - if ($hasNamedArguments && !$this->phpVersion->supportsNamedArguments()) { + if ($hasNamedArguments && !$this->phpVersion->supportsNamedArguments() && !$funcCall->getAttribute('isAttribute', false)) { $errors[] = RuleErrorBuilder::message('Named arguments are supported only on PHP 8.0 and later.')->line($funcCall->getLine())->nonIgnorable()->build(); } diff --git a/tests/PHPStan/Rules/Methods/MethodAttributesRuleTest.php b/tests/PHPStan/Rules/Methods/MethodAttributesRuleTest.php index badcc06643..3b77608fc9 100644 --- a/tests/PHPStan/Rules/Methods/MethodAttributesRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MethodAttributesRuleTest.php @@ -18,6 +18,9 @@ class MethodAttributesRuleTest extends RuleTestCase { + /** @var int */ + private $phpVersion; + protected function getRule(): Rule { $reflectionProvider = $this->createReflectionProvider(); @@ -27,7 +30,7 @@ protected function getRule(): Rule new FunctionCallParametersCheck( new RuleLevelHelper($reflectionProvider, true, false, true, false), new NullsafeCheck(), - new PhpVersion(80000), + new PhpVersion($this->phpVersion), new UnresolvableTypeHelper(), true, true, @@ -45,6 +48,8 @@ public function testRule(): void $this->markTestSkipped('Test requires PHP 8.0.'); } + $this->phpVersion = 80000; + $this->analyse([__DIR__ . '/data/method-attributes.php'], [ [ 'Attribute class MethodAttributes\Foo does not have the method target.', @@ -53,4 +58,14 @@ public function testRule(): void ]); } + public function testBug5898(): void + { + if (!self::$useStaticReflectionProvider && PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Test requires PHP 8.0.'); + } + + $this->phpVersion = 70400; + $this->analyse([__DIR__ . '/data/bug-5898.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-5898.php b/tests/PHPStan/Rules/Methods/data/bug-5898.php new file mode 100644 index 0000000000..cbf5e3cb47 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-5898.php @@ -0,0 +1,25 @@ +