Browse Source

bug: Fix PHPDoc alignment fixer containing callbacks using `\Closure` (#6746)

Chris Smith 2 years ago
parent
commit
ecb41d54f8

+ 1 - 1
src/DocBlock/TypeExpression.php

@@ -46,7 +46,7 @@ final class TypeExpression
                 )
                 |
                 (?<callable> # callable syntax, e.g. `callable(string): bool`
-                    (?<callable_start>(?:callable|Closure)\h*\(\h*)
+                    (?<callable_start>(?:callable|\\\\?Closure)\h*\(\h*)
                         (?<callable_arguments>
                             (?&types)
                             (?:

+ 1 - 1
tests/Console/Output/ProcessOutputTest.php

@@ -201,7 +201,7 @@ final class ProcessOutputTest extends TestCase
 
     /**
      * @param list<array{0: FixerFileProcessedEvent::STATUS_*, 1?: int}> $statuses
-     * @param \Closure(FixerFileProcessedEvent::STATUS_*): void $action
+     * @param \Closure(FixerFileProcessedEvent::STATUS_*): void          $action
      */
     private function foreachStatus(array $statuses, \Closure $action): void
     {

+ 16 - 0
tests/DocBlock/TypeExpressionTest.php

@@ -129,6 +129,22 @@ final class TypeExpressionTest extends TestCase
 
         yield ['Closure(string)', ['Closure(string)']];
 
+        yield ['\\Closure', ['\\Closure']];
+
+        yield ['\\Closure()', ['\\Closure()']];
+
+        yield ['\\Closure(string)', ['\\Closure(string)']];
+
+        yield ['\\Closure(string, bool)', ['\\Closure(string, bool)']];
+
+        yield ['\\Closure(string|int, bool)', ['\\Closure(string|int, bool)']];
+
+        yield ['\\Closure(string):bool', ['\\Closure(string):bool']];
+
+        yield ['\\Closure(string): bool', ['\\Closure(string): bool']];
+
+        yield ['\\Closure(string|int, bool): bool', ['\\Closure(string|int, bool): bool']];
+
         yield ['array  <  int   , callable  (  string  )  :   bool  >', ['array  <  int   , callable  (  string  )  :   bool  >']];
     }
 

+ 85 - 0
tests/Fixer/Phpdoc/PhpdocAlignFixerTest.php

@@ -1454,4 +1454,89 @@ class Foo {}
              */
         ');
     }
+
+    public function testClosureTypesContainingBackslash(): void
+    {
+        $this->doTest('<?php
+            /**
+             * @var string                            $input
+             * @var \Closure                          $fn
+             * @var \Closure(bool):int                $fn2
+             * @var Closure                           $fn3
+             * @var Closure(string):string            $fn4
+             * @var array<string,array<string,mixed>> $data
+             */
+            /**
+             * @param string                            $input
+             * @param \Closure                          $fn
+             * @param \Closure(bool):int                $fn2
+             * @param Closure                           $fn3
+             * @param Closure(string):string            $fn4
+             * @param array<string,array<string,mixed>> $data
+             */
+            /**
+             * @var string                   $value
+             * @var \Closure(string): string $callback
+             * @var Closure(int): bool       $callback2
+             */
+            /**
+             * @param string                   $value
+             * @param \Closure(string): string $callback
+             * @param Closure(int): bool       $callback2
+             */
+            /**
+             * @var Closure(array<int,bool>): bool $callback1
+             * @var \Closure(string): string       $callback2
+             */
+            /**
+             * @param Closure(array<int,bool>): bool $callback1
+             * @param \Closure(string): string       $callback2
+             */
+        ');
+    }
+
+    /**
+     * @dataProvider provideCallableTypesWithUglyCodeCases
+     */
+    public function testCallableTypesWithUglyCode(string $input): void
+    {
+        $this->doTest(<<<'EOT'
+        <?php
+        /**
+         * @var callable                      $fn
+         * @var callable(bool): int           $fn2
+         * @var Closure                       $fn3
+         * @var Closure(string|object):string $fn4
+         * @var \Closure                      $fn5
+         * @var \Closure(int, bool): bool     $fn6
+         */
+        EOT, $input);
+    }
+
+    public function provideCallableTypesWithUglyCodeCases(): iterable
+    {
+        yield [<<<'EOT'
+        <?php
+        /**
+         * @var callable $fn
+         * @var callable(bool): int $fn2
+         * @var Closure $fn3
+         * @var Closure(string|object):string $fn4
+         * @var \Closure $fn5
+         * @var \Closure(int, bool): bool $fn6
+         */
+        EOT];
+
+        yield [<<<'EOT'
+        <?php
+        /**
+         * @var          callable           $fn
+         * @var   callable(bool): int     $fn2
+         * @var   Closure          $fn3
+         * @var Closure(string|object):string                  $fn4
+         * @var      \Closure             $fn5
+         * @var            \Closure(int, bool): bool       $fn6
+         */
+        EOT];
+    }
 }