Skip to content

Commit 843b4d6

Browse files
committed
Simplify isConstructorPromotion and add ignore typehints
1 parent f1f9c90 commit 843b4d6

File tree

1 file changed

+65
-34
lines changed

1 file changed

+65
-34
lines changed

VariableAnalysis/Lib/Helpers.php

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,59 +1535,90 @@ public static function getForLoopForIncrementVariable($stackPtr, $forLoops)
15351535
*/
15361536
public static function isConstructorPromotion(File $phpcsFile, $stackPtr)
15371537
{
1538+
// If we are not in a function's parameters, this is not promotion.
15381539
$functionIndex = self::getFunctionIndexForFunctionParameter($phpcsFile, $stackPtr);
15391540
if (! $functionIndex) {
15401541
return false;
15411542
}
15421543

15431544
$tokens = $phpcsFile->getTokens();
15441545

1545-
// If the previous token is a visibility keyword, this is constructor
1546-
// promotion. eg: `public $foobar`.
1547-
$prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), $functionIndex, true);
1548-
if (! is_int($prevIndex)) {
1546+
// Move backwards from the token, ignoring whitespace, typehints, and the
1547+
// 'readonly' keyword, and return true if the previous token is a
1548+
// visibility keyword (eg: `public`).
1549+
for ($i = $stackPtr - 1; $i > $functionIndex; $i--) {
1550+
if (in_array($tokens[$i]['code'], Tokens::$scopeModifiers, true)) {
1551+
return true;
1552+
}
1553+
if (in_array($tokens[$i]['code'], Tokens::$emptyTokens, true)) {
1554+
continue;
1555+
}
1556+
if ($tokens[$i]['content'] === 'readonly') {
1557+
continue;
1558+
}
1559+
if (self::isTokenPartOfTypehint($phpcsFile, $i)) {
1560+
continue;
1561+
}
15491562
return false;
15501563
}
1551-
$prevToken = $tokens[$prevIndex];
1552-
if (in_array($prevToken['code'], Tokens::$scopeModifiers, true)) {
1564+
return false;
1565+
}
1566+
1567+
/**
1568+
* Return false if the token is definitely not part of a typehint
1569+
*
1570+
* @param File $phpcsFile
1571+
* @param int $stackPtr
1572+
*
1573+
* @return bool
1574+
*/
1575+
private static function isTokenPossiblyPartOfTypehint(File $phpcsFile, $stackPtr) {
1576+
$tokens = $phpcsFile->getTokens();
1577+
$token = $tokens[$stackPtr];
1578+
if ($token['code'] === 'PHPCS_T_NULLABLE') {
15531579
return true;
15541580
}
1555-
1556-
// If the previous token is not a visibility keyword, but the one before it
1557-
// is, the previous token was probably a typehint and this is constructor
1558-
// promotion. eg: `public boolean $foobar`.
1559-
$prev2Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prevIndex - 1), $functionIndex, true);
1560-
if (! is_int($prev2Index)) {
1561-
return false;
1581+
if ($token['code'] === T_NS_SEPARATOR) {
1582+
return true;
15621583
}
1563-
$prev2Token = $tokens[$prev2Index];
1564-
// If the token that might be a visibility keyword is a nullable typehint,
1565-
// ignore it and move back one token further eg: `public ?boolean $foobar`.
1566-
if ($prev2Token['code'] === 'PHPCS_T_NULLABLE') {
1567-
$prev2Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev2Index - 1), $functionIndex, true);
1568-
if (! is_int($prev2Index)) {
1569-
return false;
1570-
}
1584+
if ($token['code'] === T_STRING) {
1585+
return true;
15711586
}
1572-
$prev2Token = $tokens[$prev2Index];
1573-
if (in_array($prev2Token['code'], Tokens::$scopeModifiers, true)) {
1587+
if (in_array($token['code'], Tokens::$emptyTokens)) {
15741588
return true;
15751589
}
1590+
return false;
1591+
}
1592+
1593+
/**
1594+
* Return true if the token is inside a typehint
1595+
*
1596+
* @param File $phpcsFile
1597+
* @param int $stackPtr
1598+
*
1599+
* @return bool
1600+
*/
1601+
public static function isTokenPartOfTypehint(File $phpcsFile, $stackPtr) {
1602+
$tokens = $phpcsFile->getTokens();
1603+
$token = $tokens[$stackPtr];
15761604

1577-
// If the previous token is not a visibility keyword, but the one two
1578-
// before it is, and one of the tokens is `readonly`, the previous token
1579-
// was probably a typehint and this is constructor promotion. eg: `public
1580-
// readonly boolean $foobar`.
1581-
$prev3Index = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev2Index - 1), $functionIndex, true);
1582-
if (! is_int($prev3Index)) {
1605+
if (! self::isTokenPossiblyPartOfTypehint($phpcsFile, $stackPtr)) {
15831606
return false;
15841607
}
1585-
$prev3Token = $tokens[$prev3Index];
1586-
$wasPreviousReadonly = $prevToken['content'] === 'readonly' || $prev2Token['content'] === 'readonly';
1587-
if (in_array($prev3Token['code'], Tokens::$scopeModifiers, true) && $wasPreviousReadonly) {
1588-
return true;
1589-
}
15901608

1609+
// Examine every following token, ignoring everything that might be part of
1610+
// a typehint. If we find a variable at the end, this is part of a
1611+
// typehint.
1612+
$i = $stackPtr;
1613+
while (true) {
1614+
$i += 1;
1615+
if (! isset($tokens[$i])) {
1616+
return false;
1617+
}
1618+
if (! self::isTokenPossiblyPartOfTypehint($phpcsFile, $i)) {
1619+
return ($tokens[$i]['code'] === T_VARIABLE);
1620+
}
1621+
}
15911622
return false;
15921623
}
15931624

0 commit comments

Comments
 (0)