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

Issue:
PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer removes only the first variable name
## Bug report
PHP version: 7.4
PHP CS Fixer 2.17.3 Desert Beast by Fabien Potencier and Dariusz Ruminski

Run via `https://github.com/symplify/easy-coding-standard`

Console output:
``` bash
13) FeedbackModalControl.php

    ---------- begin diff ----------
--- Original
+++ New
@@ -16,7 +16,7 @@
 {

     /**
-     * @var array<callable(string $feedback, FileUpload $screenshot): void>
+     * @var array<callable(string, FileUpload $screenshot): void>
      */
     public array $onSuccess = [];
    ----------- end diff -----------


Applied checkers:

 * PhpCsFixer\Fixer\Phpdoc\PhpdocVarWithoutNameFixer
```

Relevant source code:
```php
final class FeedbackModalControl extends Control
{

    /**
     * @var array<callable(string $feedback, FileUpload $screenshot): void>
     */
    public array $onSuccess = [];

}
```

Expected behavior:
Remove both variables.


Discussion:
Actually I don't think it should remove anything at all: the type is an array of callables and the variables are the arguments of the signature those callables must match.
@juliendufresne 
The resulting (fixed) code I expected is this:
```php
final class FeedbackModalControl extends Control
{

    /**
     * @var array<callable(string, FileUpload): void>
     */
    public array $onSuccess = [];

}
```

This is absolutely fine by me. It hints at the number and type of arguments for the callable and is supported by both `phpstan` and `psalm`. It is true that having the variable name might help in aiding the developer to know what is the meaning of the parameter, however, removing only some but not all cannot be IMHO correct behavior.
Ah you're right, the variable names are not needed here. Though I think removing them should be a new rule then. The first one is likely removed because `phpdoc_var_without_name` does not understand this syntax correctly.
@julienfalque Yes, I think this is the case, hence the bug report. I hope we can agree that this syntax is not too outlandish. I do not know any better of encoding the same semantics - an array of event subscribers.
can you raise a fix proposal, @patrickkusebauch ?
@keradus I would argue for removing all variable names. I am not up to creating a PR if that is what you are asking.
FYI: reproduced on v3.16.1, so it's still relevant.
Reproduced on `3.22.1-DEV (d408658c)`.

---

Since this issue has not had any activity within the last 90 days, I have marked it as stale.

I will close it if no further activity occurs within the next 30 days.
Since this issue has not had any activity within the last 90 days, I have marked it as stale.

The purpose of this action is to enforce backlog review once in a while. This is mostly for maintainers and helps with keeping repository in good condition, because stale issues and PRs can accumulate over time and make it harder for others to find relevant information. It is also possible that some changes has been made to the repo already, and issue or PR became outdated, but wasn't closed for some reason. This action helps with periodic review and closing of such stale items in automated way.

You may let maintainers handle this or verify current relevancy by yourself, to help with re-triage. Any activity will remove stale label so it won't be automatically closed at this point.


I will close it if no further activity occurs within the next 30 days.



Patch:
diff --git a/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php b/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php
index f2240105fd7..f42b2d9fe10 100644
--- a/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php
+++ b/src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php
@@ -114,12 +114,12 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
 
     private function fixLine(Line $line): void
     {
-        $content = $line->getContent();
+        Preg::matchAll('/ \$'.TypeExpression::REGEX_IDENTIFIER.'(?<!\$this)/', $line->getContent(), $matches);
 
-        Preg::matchAll('/ \$'.TypeExpression::REGEX_IDENTIFIER.'(?<!\$this)/', $content, $matches);
-
-        if (isset($matches[0][0])) {
-            $line->setContent(str_replace($matches[0][0], '', $content));
+        if (isset($matches[0])) {
+            foreach ($matches[0] as $match) {
+                $line->setContent(str_replace($match, '', $line->getContent()));
+            }
         }
     }
 


Test patch:
diff --git a/tests/Fixer/Phpdoc/PhpdocVarWithoutNameFixerTest.php b/tests/Fixer/Phpdoc/PhpdocVarWithoutNameFixerTest.php
index 19782bc33cd..f4c447bdbb4 100644
--- a/tests/Fixer/Phpdoc/PhpdocVarWithoutNameFixerTest.php
+++ b/tests/Fixer/Phpdoc/PhpdocVarWithoutNameFixerTest.php
@@ -595,6 +595,29 @@ class Foo
                 EOF
             ,
         ];
+
+        yield '@var with callable syntax' => [
+            <<<'EOF'
+                <?php
+
+                class Foo
+                {
+                    /** @var array<callable(string, Buzz): void> */
+                    protected $bar;
+                }
+                EOF
+            ,
+            <<<'EOF'
+                <?php
+
+                class Foo
+                {
+                    /** @var array<callable(string $baz, Buzz $buzz): void> */
+                    protected $bar;
+                }
+                EOF
+            ,
+        ];
     }
 
     /**