From badc598256f97ee94d95d23e2fcec45c747f8c2c Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 13 Aug 2017 06:29:42 +0200 Subject: [PATCH] Fix `File::isReference()` method The `File:isReference()` method misidentified a number of situations where the `T_BITWISE_AND` token was encountered, most notably: * An array assignment of a calculated value with a bitwise and operator in it ,was being misidentified as a reference. * A calculated default value for a function parameter with a bitwise and operator in it, was being misidentified as a reference. * New by reference was not recognized as a reference. * References to class properties with `self::`, `parent::`, `static::`, `namespace\Class::`, `classname::` were not recognized as references. This commit fixes these cases. Fixes 1604 --- src/Files/File.php | 82 +++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/src/Files/File.php b/src/Files/File.php index f3220ced05..2ba7dc75c4 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -1612,7 +1612,7 @@ public function isReference($stackPtr) } if ($this->tokens[$tokenBefore]['code'] === T_DOUBLE_ARROW) { - // Inside a foreach loop, this is a reference. + // Inside a foreach loop or array assignment, this is a reference. return true; } @@ -1621,17 +1621,23 @@ public function isReference($stackPtr) return true; } - if ($this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY) { - // Inside an array declaration, this is a reference. - return true; - } - if (isset(Util\Tokens::$assignmentTokens[$this->tokens[$tokenBefore]['code']]) === true) { // This is directly after an assignment. It's a reference. Even if // it is part of an operation, the other tests will handle it. return true; } + $tokenAfter = $this->findNext( + Util\Tokens::$emptyTokens, + ($stackPtr + 1), + null, + true + ); + + if ($this->tokens[$tokenAfter]['code'] === T_NEW) { + return true; + } + if (isset($this->tokens[$stackPtr]['nested_parenthesis']) === true) { $brackets = $this->tokens[$stackPtr]['nested_parenthesis']; $lastBracket = array_pop($brackets); @@ -1639,11 +1645,27 @@ public function isReference($stackPtr) $owner = $this->tokens[$this->tokens[$lastBracket]['parenthesis_owner']]; if ($owner['code'] === T_FUNCTION || $owner['code'] === T_CLOSURE - || $owner['code'] === T_ARRAY ) { - // Inside a function or array declaration, this is a reference. - return true; - } + $params = $this->getMethodParameters($this->tokens[$lastBracket]['parenthesis_owner']); + foreach ($params as $param) { + $varToken = $tokenAfter; + if ($param['variable_length'] === true) { + $varToken = $this->findNext( + (Util\Tokens::$emptyTokens + array(T_ELLIPSIS)), + ($stackPtr + 1), + null, + true + ); + } + + if ($param['token'] === $varToken + && $param['pass_by_reference'] === true + ) { + // Function parameter declared to be passed by reference. + return true; + } + } + }//end if } else { $prev = false; for ($t = ($this->tokens[$lastBracket]['parenthesis_opener'] - 1); $t >= 0; $t--) { @@ -1654,24 +1676,40 @@ public function isReference($stackPtr) } if ($prev !== false && $this->tokens[$prev]['code'] === T_USE) { + // Closure use by reference. return true; } }//end if }//end if - $tokenAfter = $this->findNext( - Util\Tokens::$emptyTokens, - ($stackPtr + 1), - null, - true - ); - - if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE - && ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS - || $this->tokens[$tokenBefore]['code'] === T_COMMA) + // Pass by reference in function calls and assign by reference in arrays. + if ($this->tokens[$tokenBefore]['code'] === T_OPEN_PARENTHESIS + || $this->tokens[$tokenBefore]['code'] === T_COMMA + || $this->tokens[$tokenBefore]['code'] === T_OPEN_SHORT_ARRAY ) { - return true; - } + if ($this->tokens[$tokenAfter]['code'] === T_VARIABLE) { + return true; + } else { + $skip = Util\Tokens::$emptyTokens; + $skip[] = T_NS_SEPARATOR; + $skip[] = T_SELF; + $skip[] = T_PARENT; + $skip[] = T_STATIC; + $skip[] = T_STRING; + $skip[] = T_NAMESPACE; + $skip[] = T_DOUBLE_COLON; + + $nextSignificantAfter = $this->findNext( + $skip, + ($stackPtr + 1), + null, + true + ); + if ($this->tokens[$nextSignificantAfter]['code'] === T_VARIABLE) { + return true; + } + }//end if + }//end if return false;