Browse Source

bug: PhpdocAlignFixer - fix static `@method` (#6366)

HypeMC 2 years ago
parent
commit
3b471b4337
2 changed files with 170 additions and 3 deletions
  1. 38 3
      src/Fixer/Phpdoc/PhpdocAlignFixer.php
  2. 132 0
      tests/Fixer/Phpdoc/PhpdocAlignFixerTest.php

+ 38 - 3
src/Fixer/Phpdoc/PhpdocAlignFixer.php

@@ -113,7 +113,7 @@ final class PhpdocAlignFixer extends AbstractFixer implements ConfigurableFixerI
 
         // e.g. @method <hint> <signature>
         if ([] !== $tagsWithMethodSignatureToAlign) {
-            $types[] = '(?P<tag3>'.implode('|', $tagsWithMethodSignatureToAlign).')(\s+(?P<hint3>[^\s(]+)|)\s+(?P<signature>.+\))';
+            $types[] = '(?P<tag3>'.implode('|', $tagsWithMethodSignatureToAlign).')(\s+(?P<static>static))?(\s+(?P<hint3>[^\s(]+)|)\s+(?P<signature>.+\))';
         }
 
         // optional <desc>
@@ -255,6 +255,7 @@ EOF;
             }
 
             // compute the max length of the tag, hint and variables
+            $hasStatic = false;
             $tagMax = 0;
             $hintMax = 0;
             $varMax = 0;
@@ -264,6 +265,7 @@ EOF;
                     continue;
                 }
 
+                $hasStatic = $hasStatic || $item['static'];
                 $tagMax = max($tagMax, \strlen($item['tag']));
                 $hintMax = max($hintMax, \strlen($item['hint']));
                 $varMax = max($varMax, \strlen($item['var']));
@@ -286,6 +288,10 @@ EOF;
                         $extraIndent = 3;
                     }
 
+                    if ($hasStatic) {
+                        $extraIndent += 7; // \strlen('static ');
+                    }
+
                     $line =
                         $item['indent']
                         .' *  '
@@ -307,8 +313,24 @@ EOF;
                     $item['indent']
                     .' * @'
                     .$item['tag']
-                    .$this->getIndent(
-                        $tagMax - \strlen($item['tag']) + 1,
+                ;
+
+                if ($hasStatic) {
+                    $line .=
+                        $this->getIndent(
+                            $tagMax - \strlen($item['tag']) + 1,
+                            $item['static'] ? 1 : 0
+                        )
+                        .($item['static'] ?: $this->getIndent(6 /* \strlen('static') */, 0))
+                    ;
+                    $hintVerticalAlignIndent = 1;
+                } else {
+                    $hintVerticalAlignIndent = $tagMax - \strlen($item['tag']) + 1;
+                }
+
+                $line .=
+                    $this->getIndent(
+                        $hintVerticalAlignIndent,
                         $item['hint'] ? 1 : 0
                     )
                     .$item['hint']
@@ -351,12 +373,23 @@ EOF;
                 $matches['tag'] = $matches['tag3'];
                 $matches['hint'] = $matches['hint3'];
                 $matches['var'] = $matches['signature'];
+
+                // Since static can be both a return type declaration & a keyword that defines static methods
+                // we assume it's a type declaration when only one value is present
+                if ('' === $matches['hint'] && '' !== $matches['static']) {
+                    $matches['hint'] = $matches['static'];
+                    $matches['static'] = '';
+                }
             }
 
             if (isset($matches['hint'])) {
                 $matches['hint'] = trim($matches['hint']);
             }
 
+            if (!isset($matches['static'])) {
+                $matches['static'] = '';
+            }
+
             return $matches;
         }
 
@@ -364,6 +397,7 @@ EOF;
             $matches['tag'] = null;
             $matches['var'] = '';
             $matches['hint'] = '';
+            $matches['static'] = '';
 
             return $matches;
         }
@@ -403,6 +437,7 @@ EOF;
 
         // Indent according to existing values:
         return
+            $this->getSentenceIndent($item['static']) +
             $this->getSentenceIndent($item['tag']) +
             $this->getSentenceIndent($item['hint']) +
             $this->getSentenceIndent($item['var']);

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

@@ -1103,6 +1103,138 @@ EOF;
         $this->doTest($expected, $input);
     }
 
+    public function testAlignsStaticAndNonStaticMethods(): void
+    {
+        $this->fixer->configure(['tags' => ['method', 'property']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @property        string      $foo             Desc1
+     * @property        int         $bar             Desc2
+     * @method                      foo(string $foo) DescriptionFoo
+     * @method          static      bar(string $foo) DescriptionBar
+     * @method          string|null baz(bool $baz)   DescriptionBaz
+     * @method   static int|false   qux(float $qux)  DescriptionQux
+     * @method   static static      quux(int $quux)  DescriptionQuux
+     * @method   static $this       quuz(bool $quuz) DescriptionQuuz
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @property    string   $foo     Desc1
+     * @property  int   $bar   Desc2
+     * @method     foo(string $foo)    DescriptionFoo
+     * @method  static     bar(string $foo) DescriptionBar
+     * @method    string|null    baz(bool $baz)  DescriptionBaz
+     * @method static     int|false qux(float $qux) DescriptionQux
+     * @method static   static    quux(int $quux) DescriptionQuux
+     * @method static  $this     quuz(bool $quuz) DescriptionQuuz
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsStaticAndNonStaticMethodsLeftAlign(): void
+    {
+        $this->fixer->configure(['tags' => ['method', 'property'], 'align' => PhpdocAlignFixer::ALIGN_LEFT]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @property string $foo Desc1
+     * @property int $bar Desc2
+     * @method foo(string $foo) DescriptionFoo
+     * @method static bar(string $foo) DescriptionBar
+     * @method string|null baz(bool $baz) DescriptionBaz
+     * @method static int|false qux(float $qux) DescriptionQux
+     * @method static static quux(int $quux) DescriptionQuux
+     * @method static $this quuz(bool $quuz) DescriptionQuuz
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @property    string   $foo     Desc1
+     * @property  int   $bar   Desc2
+     * @method     foo(string $foo)    DescriptionFoo
+     * @method  static     bar(string $foo) DescriptionBar
+     * @method    string|null    baz(bool $baz)  DescriptionBaz
+     * @method static     int|false qux(float $qux) DescriptionQux
+     * @method static   static    quux(int $quux) DescriptionQuux
+     * @method static  $this     quuz(bool $quuz) DescriptionQuuz
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsReturnStatic(): void
+    {
+        $this->fixer->configure(['tags' => ['param', 'return', 'throws']]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @param  string    $foobar Desc1
+     * @param  int       &$baz   Desc2
+     * @param  ?Qux      $qux    Desc3
+     * @param  int|float $quux   Desc4
+     * @return static    DescriptionReturn
+     * @throws Exception DescriptionException
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @param    string   $foobar     Desc1
+     * @param  int   &$baz   Desc2
+     * @param ?Qux       $qux   Desc3
+     * @param    int|float $quux   Desc4
+     * @return  static     DescriptionReturn
+     * @throws   Exception       DescriptionException
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
+    public function testAlignsReturnStaticLeftAlign(): void
+    {
+        $this->fixer->configure(['tags' => ['param', 'return', 'throws'], 'align' => PhpdocAlignFixer::ALIGN_LEFT]);
+
+        $expected = <<<'EOF'
+<?php
+    /**
+     * @param string $foobar Desc1
+     * @param int &$baz Desc2
+     * @param ?Qux $qux Desc3
+     * @param int|float $quux Desc4
+     * @return static DescriptionReturn
+     * @throws Exception DescriptionException
+     */
+EOF;
+
+        $input = <<<'EOF'
+<?php
+    /**
+     * @param    string   $foobar     Desc1
+     * @param  int   &$baz   Desc2
+     * @param ?Qux       $qux   Desc3
+     * @param    int|float $quux   Desc4
+     * @return  static     DescriptionReturn
+     * @throws   Exception       DescriptionException
+     */
+EOF;
+
+        $this->doTest($expected, $input);
+    }
+
     public function testDoesNotAlignWithEmptyConfig(): void
     {
         $this->fixer->configure(['tags' => []]);