From 9aad9a8d61694508cdc70a1e93abd43921b12aba Mon Sep 17 00:00:00 2001 From: Mark Scherer Date: Thu, 6 Aug 2026 13:44:47 +0200 Subject: [PATCH] Stop the function-name sniffs rewriting attribute names An attribute name sits in front of a parenthesis exactly like a call does, so the five function-name sniffs matched it. An attribute happening to share a name with a function alias was reported, and phpcbf rewrote it: an attribute class Pos became current(), which does not compile. The previous-token guard did not help. It rejects T_FUNCTION, T_OBJECT_OPERATOR, T_NEW and T_DOUBLE_COLON, none of which precede an attribute name, and in a grouped attribute the second name follows a comma rather than the opener. Tokens between an attribute opener and its closer all carry the opener, so a single check in the shared lookup covers every one of them, grouped names included. This existed for plain names before fully qualified ones were added, which widened it to the leading-backslash form. The fixtures gain both. --- PhpCollective/Sniffs/AbstractSniffs/AbstractSniff.php | 7 +++++++ tests/_data/RemoveFunctionAlias/after.php | 7 +++++++ tests/_data/RemoveFunctionAlias/before.php | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/PhpCollective/Sniffs/AbstractSniffs/AbstractSniff.php b/PhpCollective/Sniffs/AbstractSniffs/AbstractSniff.php index ea08484..e2f79de 100644 --- a/PhpCollective/Sniffs/AbstractSniffs/AbstractSniff.php +++ b/PhpCollective/Sniffs/AbstractSniffs/AbstractSniff.php @@ -67,6 +67,13 @@ protected function getGlobalFunctionName(File $phpcsFile, int $stackPtr): ?strin { $tokens = $phpcsFile->getTokens(); + // An attribute name sits in front of a parenthesis just like a call does, but it names a + // class. Every token between `#[` and its closer carries the opener, including grouped + // attributes after a comma. + if (isset($tokens[$stackPtr]['attribute_opener'])) { + return null; + } + if ($tokens[$stackPtr]['code'] === T_STRING) { return strtolower($tokens[$stackPtr]['content']); } diff --git a/tests/_data/RemoveFunctionAlias/after.php b/tests/_data/RemoveFunctionAlias/after.php index 33ca232..d5116eb 100644 --- a/tests/_data/RemoveFunctionAlias/after.php +++ b/tests/_data/RemoveFunctionAlias/after.php @@ -4,6 +4,13 @@ class FixMe { + + /** + * Attribute names sit in front of a parenthesis but name a class, not a function. + */ + #[Pos(1), Chop(2)] + #[\Join(3)] + public int $attributed = 1; /** * Type check aliases. */ diff --git a/tests/_data/RemoveFunctionAlias/before.php b/tests/_data/RemoveFunctionAlias/before.php index b0b7a61..11c8642 100644 --- a/tests/_data/RemoveFunctionAlias/before.php +++ b/tests/_data/RemoveFunctionAlias/before.php @@ -4,6 +4,13 @@ class FixMe { + + /** + * Attribute names sit in front of a parenthesis but name a class, not a function. + */ + #[Pos(1), Chop(2)] + #[\Join(3)] + public int $attributed = 1; /** * Type check aliases. */