Repository: PHP-CS-Fixer/PHP-CS-Fixer (PHP)

Issue:
[3.15.0] Import for union is removed by `no_unused_imports`
## Bug report

Import alias is removed when used in a union.

### Description

Noticed this change in 3.15.0. It doesn't make sense to me.

```diff
 <?php
 
 use App\Bar\SomeClass as SomeClassBar;
-use App\Foo\SomeClass as SomeClassFoo;
 
 final class Controller
 {
     public static function __invoke(SomeClassBar | SomeClassFoo ...$fields) : array
     {
         return [];
     }
 }
 ```

### Runtime version

3.15.0

### Used command

```
vendor/bin/php-cs-fixer fix
```

### Code snippet that reproduces the problem

```php
<?php

use App\Bar\SomeClass as SomeClassBar;
use App\Foo\SomeClass as SomeClassFoo;

final class Controller
{
    public static function __invoke(SomeClassBar | SomeClassFoo ...$fields) : array
    {
        return [];
    }
}
```



Discussion:

Patch:
diff --git a/src/Tokenizer/AbstractTypeTransformer.php b/src/Tokenizer/AbstractTypeTransformer.php
index f8dd70f1f0a..022473d7f40 100644
--- a/src/Tokenizer/AbstractTypeTransformer.php
+++ b/src/Tokenizer/AbstractTypeTransformer.php
@@ -56,12 +56,18 @@ private function isPartOfType(Tokens $tokens, int $index): bool
             return true;
         }
 
-        // for parameter there will be variable or reference after type
-        $variableIndex = $tokens->getTokenNotOfKindSibling($index, 1, self::TYPE_TOKENS);
-        if (!$tokens[$variableIndex]->isGivenKind(T_VARIABLE)) {
+        // for parameter there will be splat operator or variable after the type ("&" is ambiguous and can be reference or bitwise and)
+        $afterTypeIndex = $tokens->getTokenNotOfKindSibling($index, 1, self::TYPE_TOKENS);
+
+        if ($tokens[$afterTypeIndex]->isGivenKind(T_ELLIPSIS)) {
+            return true;
+        }
+
+        if (!$tokens[$afterTypeIndex]->isGivenKind(T_VARIABLE)) {
             return false;
         }
-        $beforeVariableIndex = $tokens->getPrevMeaningfulToken($variableIndex);
+
+        $beforeVariableIndex = $tokens->getPrevMeaningfulToken($afterTypeIndex);
         if ($tokens[$beforeVariableIndex]->equals('&')) {
             $prevIndex = $tokens->getPrevTokenOfKind($index, ['{', '}', ';', [T_FN], [T_FUNCTION]]);
 


Test patch:
diff --git a/tests/Fixer/Import/NoUnusedImportsFixerTest.php b/tests/Fixer/Import/NoUnusedImportsFixerTest.php
index abde341c721..8b668f0f60d 100644
--- a/tests/Fixer/Import/NoUnusedImportsFixerTest.php
+++ b/tests/Fixer/Import/NoUnusedImportsFixerTest.php
@@ -1515,5 +1515,27 @@ enum Foo: string
                 }
             ',
         ];
+
+        yield [
+            '<?php
+                use Foo\Class1;
+                use Foo\Class2;
+                class C
+                {
+                   public function t(Class1 | Class2 $fields) {}
+                }
+            ',
+        ];
+
+        yield [
+            '<?php
+                use Foo\Class1;
+                use Foo\Class2;
+                class C
+                {
+                   public function t(Class1 | Class2 ...$fields) {}
+                }
+            ',
+        ];
     }
 }
diff --git a/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php b/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php
index 2080214ea48..6a6991a19ac 100644
--- a/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php
+++ b/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php
@@ -367,6 +367,19 @@ function f3(): TypeA|static {}
                 45 => CT::T_TYPE_ALTERNATION,
             ],
         ];
+
+        yield 'splat operator' => [
+            '<?php class Foo {
+                function f1(bool|int ... $x) {}
+                function f2(bool|int $x, TypeA|\Bar\Baz|TypeB ...$y) {}
+            }',
+            [
+                12 => CT::T_TYPE_ALTERNATION,
+                28 => CT::T_TYPE_ALTERNATION,
+                35 => CT::T_TYPE_ALTERNATION,
+                40 => CT::T_TYPE_ALTERNATION,
+            ],
+        ];
     }
 
     /**
diff --git a/tests/Tokenizer/Transformer/TypeIntersectionTransformerTest.php b/tests/Tokenizer/Transformer/TypeIntersectionTransformerTest.php
index 083606a08d0..ce51390d633 100644
--- a/tests/Tokenizer/Transformer/TypeIntersectionTransformerTest.php
+++ b/tests/Tokenizer/Transformer/TypeIntersectionTransformerTest.php
@@ -368,6 +368,19 @@ function f3(): TypeA&static {}
                 45 => CT::T_TYPE_INTERSECTION,
             ],
         ];
+
+        yield 'splat operator' => [
+            '<?php class Foo {
+                function f1(bool&int ... $x) {}
+                function f2(bool&int $x, TypeA&\Bar\Baz&TypeB ...$y) {}
+            }',
+            [
+                12 => CT::T_TYPE_INTERSECTION,
+                28 => CT::T_TYPE_INTERSECTION,
+                35 => CT::T_TYPE_INTERSECTION,
+                40 => CT::T_TYPE_INTERSECTION,
+            ],
+        ];
     }
 
     /**