Browse Source

PhpdocTypesFixer - support generic types

Kuba Werłos 4 years ago
parent
commit
c04067da75
2 changed files with 56 additions and 9 deletions
  1. 27 9
      src/Fixer/Phpdoc/PhpdocTypesFixer.php
  2. 29 0
      tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php

+ 27 - 9
src/Fixer/Phpdoc/PhpdocTypesFixer.php

@@ -23,6 +23,7 @@ use PhpCsFixer\FixerConfiguration\FixerOptionBuilder;
 use PhpCsFixer\FixerDefinition\CodeSample;
 use PhpCsFixer\FixerDefinition\FixerDefinition;
 use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
+use PhpCsFixer\Preg;
 
 /**
  * @author Graham Campbell <hello@gjcampbell.co.uk>
@@ -35,7 +36,7 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
      *
      * @var array<string,string[]>
      */
-    private static $possibleTypes = [
+    private const POSSIBLE_TYPES = [
         'simple' => [
             'array',
             'bool',
@@ -69,9 +70,9 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
     ];
 
     /**
-     * @var array string[]
+     * @var string
      */
-    private $typesToFix = [];
+    private $patternToFix = '';
 
     /**
      * {@inheritdoc}
@@ -80,9 +81,22 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
     {
         parent::configure($configuration);
 
-        $this->typesToFix = array_merge(...array_map(static function (string $group): array {
-            return self::$possibleTypes[$group];
+        $typesToFix = array_merge(...array_map(static function (string $group): array {
+            return self::POSSIBLE_TYPES[$group];
         }, $this->configuration['groups']));
+
+        $this->patternToFix = sprintf(
+            '/(?<![a-zA-Z0-9_\x80-\xff]\\\\)(\b|.(?=\$))(%s)\b(?!\\\\)/i',
+            implode(
+                '|',
+                array_map(
+                    function (string $type): string {
+                        return preg_quote($type, '/');
+                    },
+                    $typesToFix
+                )
+            )
+        );
     }
 
     /**
@@ -140,9 +154,13 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
      */
     protected function normalize(string $type): string
     {
-        $lower = strtolower($type);
-
-        return \in_array($lower, $this->typesToFix, true) ? $lower : $type;
+        return Preg::replaceCallback(
+            $this->patternToFix,
+            function (array $matches): string {
+                return strtolower($matches[0]);
+            },
+            $type
+        );
     }
 
     /**
@@ -150,7 +168,7 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
      */
     protected function createConfigurationDefinition(): FixerConfigurationResolverInterface
     {
-        $possibleGroups = array_keys(self::$possibleTypes);
+        $possibleGroups = array_keys(self::POSSIBLE_TYPES);
 
         return new FixerConfigurationResolver([
             (new FixerOptionBuilder('groups', 'Type groups to fix.'))

+ 29 - 0
tests/Fixer/Phpdoc/PhpdocTypesFixerTest.php

@@ -255,6 +255,35 @@ EOF;
         $this->doTest($expected, $input);
     }
 
+    public function testGenerics(): void
+    {
+        $this->fixer->configure(['groups' => ['simple', 'meta']]);
+        $this->doTest(
+            '<?php
+            /**
+             * @param array<int, object> $a
+             * @param array<iterable> $b
+             * @param array<parent|$this|self> $c
+             * @param array<\int, \object> $d
+             * @param iterable<Foo\Int\Bar|Foo\Int|Int\Bar> $thisShouldNotBeChanged
+             * @param iterable<BOOLBOOLBOOL|INTINTINT|ARRAY_BOOL_INT_STRING_> $thisShouldNotBeChangedNeither
+             *
+             * @return array<int, array<string, array<int, DoNotChangeThisAsThisIsAClass>>>
+             */',
+            '<?php
+            /**
+             * @param ARRAY<INT, OBJECT> $a
+             * @param ARRAY<ITERABLE> $b
+             * @param array<Parent|$This|Self> $c
+             * @param ARRAY<\INT, \OBJECT> $d
+             * @param iterable<Foo\Int\Bar|Foo\Int|Int\Bar> $thisShouldNotBeChanged
+             * @param iterable<BOOLBOOLBOOL|INTINTINT|ARRAY_BOOL_INT_STRING_> $thisShouldNotBeChangedNeither
+             *
+             * @return ARRAY<INT, ARRAY<STRING, ARRAY<INT, DoNotChangeThisAsThisIsAClass>>>
+             */'
+        );
+    }
+
     public function testWrongConfig(): void
     {
         $this->expectException(InvalidFixerConfigurationException::class);