From bba74b37e261f5b77bc7b02cc2d9721f7ddcd44c Mon Sep 17 00:00:00 2001
From: Klaus Purer <klaus.purer@protonmail.ch>
Date: Fri, 3 Jan 2025 17:25:27 +0100
Subject: [PATCH] fix(FullyQualifiedNamespace): Fix detection of class names at
 the beginning of attributes (#3483583)

---
 .../Classes/FullyQualifiedNamespaceSniff.php  |  7 ++++--
 .../FullyQualifiedNamespaceUnitTest.4.inc     | 22 +++++++++++++++++++
 ...ullyQualifiedNamespaceUnitTest.4.inc.fixed | 21 ++++++++++++++++++
 .../FullyQualifiedNamespaceUnitTest.php       |  9 +++++++-
 .../Commenting/ClassCommentUnitTest.inc.fixed |  4 +++-
 .../FunctionCommentUnitTest.inc.fixed         |  4 +++-
 6 files changed, 62 insertions(+), 5 deletions(-)
 create mode 100644 tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc
 create mode 100644 tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc.fixed

diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php
index 01aa74f7..ee6e564f 100644
--- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php
+++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php
@@ -60,7 +60,7 @@ class FullyQualifiedNamespaceSniff implements Sniff
         }
 
         // We are only interested in a backslash embedded between strings, which
-        // means this is a class reference with more than once namespace part.
+        // means this is a class reference with more than one namespace part.
         if ($tokens[($stackPtr - 1)]['code'] !== T_STRING || $tokens[($stackPtr + 1)]['code'] !== T_STRING) {
             return;
         }
@@ -76,7 +76,10 @@ class FullyQualifiedNamespaceSniff implements Sniff
         // If this is a namespaced function call then ignore this because use
         // statements for functions are not possible in PHP 5.5 and lower.
         $after = $phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], $stackPtr, null, true);
-        if ($tokens[$after]['code'] === T_OPEN_PARENTHESIS && $tokens[$before]['code'] !== T_NEW) {
+        if ($tokens[$after]['code'] === T_OPEN_PARENTHESIS
+            && $tokens[$before]['code'] !== T_NEW
+            && $tokens[$before]['code'] !== T_ATTRIBUTE
+        ) {
             return ($after + 1);
         }
 
diff --git a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc
new file mode 100644
index 00000000..1badbe14
--- /dev/null
+++ b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\action_link_test_plugins\Plugin\StateAction;
+
+use Drupal\action_link\Entity\ActionLinkInterface;
+use Drupal\action_link\Plugin\StateAction\StateActionBase;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Test action which is always usable.
+ */
+#[\Drupal\action_link\Attribute\StateAction(
+  id: 'test_always',
+  label: new \Drupal\Core\StringTranslation\TranslatableMarkup('Test Always'),
+  description: new \Drupal\Core\StringTranslation\TranslatableMarkup('Test Always'),
+  directions: [
+    'change' => 'change',
+  ]
+)]
+class TestAlways extends StateActionBase {
+}
diff --git a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc.fixed b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc.fixed
new file mode 100644
index 00000000..07dd59e1
--- /dev/null
+++ b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.4.inc.fixed
@@ -0,0 +1,21 @@
+<?php
+
+namespace Drupal\action_link_test_plugins\Plugin\StateAction;
+
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\action_link\Attribute\StateAction;
+use Drupal\action_link\Plugin\StateAction\StateActionBase;
+
+/**
+ * Test action which is always usable.
+ */
+#[StateAction(
+  id: 'test_always',
+  label: new TranslatableMarkup('Test Always'),
+  description: new TranslatableMarkup('Test Always'),
+  directions: [
+    'change' => 'change',
+  ]
+)]
+class TestAlways extends StateActionBase {
+}
diff --git a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.php b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.php
index 41b521c6..1ec06c33 100644
--- a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.php
+++ b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.php
@@ -37,7 +37,13 @@ class FullyQualifiedNamespaceUnitTest extends CoderSniffUnitTest
             return [8 => 1];
         case 'FullyQualifiedNamespaceUnitTest.3.inc':
             return [10 => 2];
-        }
+        case 'FullyQualifiedNamespaceUnitTest.4.inc':
+            return [
+                13 => 1,
+                15 => 1,
+                16 => 1,
+            ];
+        }//end switch
 
         return [];
 
@@ -75,6 +81,7 @@ class FullyQualifiedNamespaceUnitTest extends CoderSniffUnitTest
             __DIR__.'/FullyQualifiedNamespaceUnitTest.1.inc',
             __DIR__.'/FullyQualifiedNamespaceUnitTest.2.inc',
             __DIR__.'/FullyQualifiedNamespaceUnitTest.3.inc',
+            __DIR__.'/FullyQualifiedNamespaceUnitTest.4.inc',
             __DIR__.'/FullyQualifiedNamespaceUnitTest.api.php',
         ];
 
diff --git a/tests/Drupal/Commenting/ClassCommentUnitTest.inc.fixed b/tests/Drupal/Commenting/ClassCommentUnitTest.inc.fixed
index 3c04bade..805cb8a3 100644
--- a/tests/Drupal/Commenting/ClassCommentUnitTest.inc.fixed
+++ b/tests/Drupal/Commenting/ClassCommentUnitTest.inc.fixed
@@ -5,6 +5,8 @@
  * Testing class/trait comments.
  */
 
+use Some\Attribute;
+
 /**
  *
  */
@@ -60,7 +62,7 @@ class WrongSpacing {
 /**
  * This is correct.
  */
-#[Some\Attribute(foo: 'bar')]
+#[Attribute(foo: 'bar')]
 #[Other\Attribute(baz: 'qux')]
 class DoubleAttribute {
 
diff --git a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed
index 817400ca..4647d931 100644
--- a/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed
+++ b/tests/Drupal/Commenting/FunctionCommentUnitTest.inc.fixed
@@ -5,6 +5,8 @@
  * Some function comment tests.
  */
 
+use Some\Attribute;
+
 /**
  * Test.
  *
@@ -579,7 +581,7 @@ class Test41 {
   /**
    * Method docblock.
    */
-  #[Some\Attribute(foo: 'bar')]
+  #[Attribute(foo: 'bar')]
   #[Other\Attribute(baz: 'qux')]
   public function method() {
   }
-- 
GitLab