Browse Source

bug #5943 NoUnusedImportsFixer - use in attribute (SpacePossum)

This PR was merged into the master branch.

Discussion
----------

NoUnusedImportsFixer - use in attribute

closes #5940

build on https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5942

not 100% about this one, cc `@kubawerlos` about attribute usages

Commits
-------

a65559994 NoUnusedImportsFixer - use in attribute
Dariusz Ruminski 3 years ago
parent
commit
b3fcf260af

+ 23 - 3
src/Fixer/Import/NoUnusedImportsFixer.php

@@ -24,6 +24,7 @@ use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
 use PhpCsFixer\Tokenizer\Analyzer\GotoLabelAnalyzer;
 use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
 use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
+use PhpCsFixer\Tokenizer\CT;
 use PhpCsFixer\Tokenizer\Token;
 use PhpCsFixer\Tokenizer\Tokens;
 use PhpCsFixer\Tokenizer\TokensAnalyzer;
@@ -105,26 +106,45 @@ final class NoUnusedImportsFixer extends AbstractFixer
 
         $tokensNotBeforeFunctionCall = [T_NEW];
 
-        if (\defined('T_ATTRIBUTE')) { // @TODO: drop condition when PHP 8.0+ is required
+        $attributeIsDefined = \defined('T_ATTRIBUTE');
+
+        if ($attributeIsDefined) { // @TODO: drop condition when PHP 8.0+ is required
             $tokensNotBeforeFunctionCall[] = T_ATTRIBUTE;
         }
 
         $namespaceEndIndex = $namespace->getScopeEndIndex();
+        $inAttribute = false;
 
         for ($index = $namespace->getScopeStartIndex(); $index <= $namespaceEndIndex; ++$index) {
+            $token = $tokens[$index];
+
+            if ($attributeIsDefined && $token->isGivenKind(T_ATTRIBUTE)) {
+                $inAttribute = true;
+
+                continue;
+            }
+
+            if ($attributeIsDefined && $token->isGivenKind(CT::T_ATTRIBUTE_CLOSE)) {
+                $inAttribute = false;
+
+                continue;
+            }
+
             if (isset($ignoredIndexes[$index])) {
                 $index = $ignoredIndexes[$index];
 
                 continue;
             }
 
-            $token = $tokens[$index];
-
             if ($token->isGivenKind(T_STRING)) {
                 if (0 !== strcasecmp($import->getShortName(), $token->getContent())) {
                     continue;
                 }
 
+                if ($inAttribute) {
+                    return true;
+                }
+
                 $prevMeaningfulToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
 
                 if ($prevMeaningfulToken->isGivenKind(T_NAMESPACE)) {

+ 16 - 1
tests/Fixer/Import/NoUnusedImportsFixerTest.php

@@ -1395,7 +1395,7 @@ use Bar;
 try {} catch (Foo | Bar) {}',
         ];
 
-        yield [
+        yield 'union return' => [
             '<?php
 
 use Foo;
@@ -1407,5 +1407,20 @@ abstract class Baz
 }
 ',
         ];
+
+        yield 'attribute' => [
+            "<?php
+use Acme\\JsonSchemaValidationBundle\\Annotation\\JsonSchema;
+use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\IsGranted;
+use Symfony\\Component\\Routing\\Annotation\\Route;
+
+#[
+  Route('/basket/{uuid}/item', name: 'addBasketItem', requirements: ['uuid' => '%regex.uuid%'], methods: ['POST']),
+  IsGranted('ROLE_USER'),
+  JsonSchema('Public/Basket/addItem.json'),
+]
+class Foo {}
+",
+        ];
     }
 }