Browse Source

bug: BinaryOperatorSpacesFixer - fix parameters with union types passed by reference (#6826)

Co-authored-by: Grzegorz Korba <wirone@gmail.com>
Kuba Werłos 2 years ago
parent
commit
2c8332b623

+ 13 - 7
src/Tokenizer/AbstractTypeTransformer.php

@@ -50,18 +50,24 @@ abstract class AbstractTypeTransformer extends AbstractTransformer
 
     private function isPartOfType(Tokens $tokens, int $index): bool
     {
-        // for parameter there will be variable after type
-        $variableIndex = $tokens->getTokenNotOfKindSibling($index, 1, self::TYPE_TOKENS);
-        if ($tokens[$variableIndex]->isGivenKind(T_VARIABLE)) {
-            return $tokens[$tokens->getPrevMeaningfulToken($variableIndex)]->equalsAny(self::TYPE_END_TOKENS);
-        }
-
         // return types and non-capturing catches
         $typeColonIndex = $tokens->getTokenNotOfKindSibling($index, -1, self::TYPE_TOKENS);
         if ($tokens[$typeColonIndex]->isGivenKind([T_CATCH, CT::T_TYPE_COLON])) {
             return true;
         }
 
-        return false;
+        // for parameter there will be variable or reference after type
+        $variableIndex = $tokens->getTokenNotOfKindSibling($index, 1, self::TYPE_TOKENS);
+        if (!$tokens[$variableIndex]->isGivenKind(T_VARIABLE)) {
+            return false;
+        }
+        $beforeVariableIndex = $tokens->getPrevMeaningfulToken($variableIndex);
+        if ($tokens[$beforeVariableIndex]->equals('&')) {
+            $prevIndex = $tokens->getPrevTokenOfKind($index, ['{', '}', ';', [T_FN], [T_FUNCTION]]);
+
+            return null !== $prevIndex && $tokens[$prevIndex]->isGivenKind([T_FN, T_FUNCTION]);
+        }
+
+        return $tokens[$beforeVariableIndex]->equalsAny(self::TYPE_END_TOKENS);
     }
 }

+ 3 - 0
tests/Fixer/Operator/BinaryOperatorSpacesFixerTest.php

@@ -3107,6 +3107,9 @@ function test()
                     callable|array $a,
                     array|callable $b,
                 ) {}
+                public function qux(
+                    bool|int | string &$reference
+                ) {}
             }'
         );
     }

+ 5 - 0
tests/Tokenizer/TokensAnalyzerTest.php

@@ -1878,6 +1878,11 @@ $b;',
             [6 => false],
             '<?php function foo(callable|int $x) {}',
         ];
+
+        yield [
+            [6 => false],
+            '<?php function foo(bool|int &$x) {}',
+        ];
     }
 
     /**

+ 19 - 0
tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php

@@ -388,6 +388,25 @@ class Foo
                 ) {}
             }',
         ];
+
+        yield 'parameters by reference' => [
+            [
+                19 => CT::T_TYPE_ALTERNATION,
+                21 => CT::T_TYPE_ALTERNATION,
+                91 => CT::T_TYPE_ALTERNATION,
+                93 => CT::T_TYPE_ALTERNATION,
+            ],
+            '<?php
+                f(FOO|BAR|BAZ&$x);
+                function f1(FOO|BAR|BAZ&$x) {} // Alternation found
+                function f2(FOO&BAR&BAZ&$x) {}
+                f(FOO&BAR|BAZ&$x);
+                f(FOO|BAR&BAZ&$x);
+                fn(FOO&BAR&BAZ&$x) => 0;
+                fn(FOO|BAR|BAZ&$x) => 0; // Alternation found
+                f(FOO&BAR&BAZ&$x);
+            ',
+        ];
     }
 
     /**

+ 19 - 0
tests/Tokenizer/Transformer/TypeIntersectionTransformerTest.php

@@ -323,6 +323,25 @@ function f( #[Target(\'a\')] #[Target(\'b\')] #[Target(\'c\')] #[Target(\'d\')]
                 45 => CT::T_TYPE_INTERSECTION,
             ],
         ];
+
+        yield 'parameters by reference' => [
+            '<?php
+                f(FOO|BAR|BAZ&$x);
+                function f1(FOO|BAR|BAZ&$x) {}
+                function f2(FOO&BAR&BAZ&$x) {} // Intersection found
+                f(FOO&BAR|BAZ&$x);
+                f(FOO|BAR&BAZ&$x);
+                fn(FOO&BAR&BAZ&$x) => 0; // Intersection found
+                fn(FOO|BAR|BAZ&$x) => 0;
+                f(FOO&BAR&BAZ&$x);
+            ',
+            [
+                35 => CT::T_TYPE_INTERSECTION,
+                37 => CT::T_TYPE_INTERSECTION,
+                75 => CT::T_TYPE_INTERSECTION,
+                77 => CT::T_TYPE_INTERSECTION,
+            ],
+        ];
     }
 
     /**