From e1aeec1eb2d7e988e3ece90becb4bed3406b6d0d Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 1 Aug 2020 00:14:56 +0200 Subject: [PATCH 1/6] Collections: deprecate $objectOperators in favour of objectOperators() Deprecate the `$objectOperators` property in favour of a new `Collections::objectOperators()` method. PHP 8.0 introduces a new `T_NULLSAFE_OBJECT_OPERATOR` token. As that token will not always be available, a property can't handle this. In anticipation of the introduction of the new token, I'm deprecating the `Collections::$objectOperators` property straight away to prevent BC breaks at a later point in time. Includes adding unit tests for the new method. --- PHPCSUtils/Tokens/Collections.php | 20 ++++++++- .../Collections/ObjectOperatorsTest.php | 42 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 Tests/Tokens/Collections/ObjectOperatorsTest.php diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index dc2fcb19..670a687e 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -231,10 +231,13 @@ class Collections ]; /** - * Object operators. + * DEPRECATED: Object operators. * * @since 1.0.0-alpha3 * + * @deprecated 1.0.0-alpha4 Use the {@see \PHPCSUtils\Tokens\Collections::objectOperators()} + * method instead. + * * @var array => */ public static $objectOperators = [ @@ -576,6 +579,21 @@ public static function functionDeclarationTokensBC() return $tokens; } + /** + * Object operators. + * + * @since 1.0.0-alpha4 + * + * @return array => + */ + public static function objectOperators() + { + return [ + \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, + \T_DOUBLE_COLON => \T_DOUBLE_COLON, + ]; + } + /** * Token types which can be encountered in a parameter type declaration. * diff --git a/Tests/Tokens/Collections/ObjectOperatorsTest.php b/Tests/Tokens/Collections/ObjectOperatorsTest.php new file mode 100644 index 00000000..85d59c93 --- /dev/null +++ b/Tests/Tokens/Collections/ObjectOperatorsTest.php @@ -0,0 +1,42 @@ + \T_OBJECT_OPERATOR, + \T_DOUBLE_COLON => \T_DOUBLE_COLON, + ]; + + $this->assertSame($expected, Collections::objectOperators()); + } +} From 0a7f073404d4e6bc5ede8b7325a06be177c4a53b Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 1 Aug 2020 00:14:56 +0200 Subject: [PATCH 2/6] Collections::objectOperators(): support the PHP 8 nullsafe object operator Includes adjusted unit test. --- PHPCSUtils/Tokens/Collections.php | 11 ++++++++++- Tests/Tokens/Collections/ObjectOperatorsTest.php | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index 670a687e..08b074df 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -582,16 +582,25 @@ public static function functionDeclarationTokensBC() /** * Object operators. * + * Note: this is a method, not a property as the `T_NULLSAFE_OBJECT_OPERATOR` token may not exist. + * * @since 1.0.0-alpha4 * * @return array => */ public static function objectOperators() { - return [ + $tokens = [ \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, \T_DOUBLE_COLON => \T_DOUBLE_COLON, ]; + + if (\defined('T_NULLSAFE_OBJECT_OPERATOR') === true) { + // PHP 8.0. + $tokens[\T_NULLSAFE_OBJECT_OPERATOR] = \T_NULLSAFE_OBJECT_OPERATOR; + } + + return $tokens; } /** diff --git a/Tests/Tokens/Collections/ObjectOperatorsTest.php b/Tests/Tokens/Collections/ObjectOperatorsTest.php index 85d59c93..a6f8c591 100644 --- a/Tests/Tokens/Collections/ObjectOperatorsTest.php +++ b/Tests/Tokens/Collections/ObjectOperatorsTest.php @@ -37,6 +37,11 @@ public function testObjectOperators() \T_DOUBLE_COLON => \T_DOUBLE_COLON, ]; + if (\version_compare(\PHP_VERSION_ID, '80000', '>=') === true + ) { + $expected[\T_NULLSAFE_OBJECT_OPERATOR] = \T_NULLSAFE_OBJECT_OPERATOR; + } + $this->assertSame($expected, Collections::objectOperators()); } } From 6bd63b3f1dc9cc7ca7c05c497d40fa1df51fa809 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 7 Aug 2020 00:55:31 +0200 Subject: [PATCH 3/6] Tokens\Collections: add new `objectOperatorsBC() method Sister-method to the `objectOperators()` method, which returns the tokens which can be encountered as object operators in PHP < 8.0 in combination with a PHPCS version in which the token was not backfilled yet. The backfill for PHPCS has been pulled and is expected to be included in PHPCS 3.5.6 or 3.6.0, but as it stands, is not available in yet in a released version. Includes unit test. Related: * squizlabs/PHP_CodeSniffer 3046 --- PHPCSUtils/Tokens/Collections.php | 54 +++++++++++++++++++ .../Collections/ObjectOperatorsBCTest.php | 49 +++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 Tests/Tokens/Collections/ObjectOperatorsBCTest.php diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index 08b074df..68f14d9e 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -584,6 +584,16 @@ public static function functionDeclarationTokensBC() * * Note: this is a method, not a property as the `T_NULLSAFE_OBJECT_OPERATOR` token may not exist. * + * Sister-method to the {@see Collections::objectOperatorsBC()} method. + * This method supports PHP 8.0 and up. + * The {@see Collections::objectOperatorsBC()} method supports PHP < 8.0. + * + * This method can also safely be used if the token collection is only used when looking back + * via `$phpcsFile->findPrevious()` as in that case, a non-backfilled nullsafe object operator + * will still match the "normal" object operator. + * + * @see \PHPCSUtils\Tokens\Collections::objectOperatorsBC() Related method (PHP < 8.0). + * * @since 1.0.0-alpha4 * * @return array => @@ -603,6 +613,50 @@ public static function objectOperators() return $tokens; } + /** + * Object operators. + * + * Note: this is a method, not a property as the `T_NULLSAFE_OBJECT_OPERATOR` token may not exist. + * + * Sister-method to the {@see Collections::objectOperators()} method. + * The {@see Collections::objectOperators()} method supports PHP 8.0 and up. + * This method supports PHP < 8.0. + * + * Notable difference: + * - This method accounts for tokens which may be encountered when the `T_NULLSAFE_OBJECT_OPERATOR` token + * doesn't exist. + * + * It is recommended to use the {@see Collections::objectOperators()} method instead of + * this method if a standard does not need to support PHP < 8.0. + * + * The {@see Collections::objectOperators()} method can also safely be used if the token collection + * is only used when looking back via `$phpcsFile->findPrevious()` as in that case, a non-backfilled + * nullsafe object operator will still match the "normal" object operator. + * + * @see \PHPCSUtils\Tokens\Collections::objectOperators() Related method (PHP 8.0+). + * + * @since 1.0.0-alpha4 + * + * @return array => + */ + public static function objectOperatorsBC() + { + $tokens = [ + \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, + \T_DOUBLE_COLON => \T_DOUBLE_COLON, + ]; + + if (\defined('T_NULLSAFE_OBJECT_OPERATOR') === true) { + // PHP 8.0. + $tokens[\T_NULLSAFE_OBJECT_OPERATOR] = \T_NULLSAFE_OBJECT_OPERATOR; + } else { + // PHP < 8.0. + $tokens[\T_INLINE_THEN] = \T_INLINE_THEN; + } + + return $tokens; + } + /** * Token types which can be encountered in a parameter type declaration. * diff --git a/Tests/Tokens/Collections/ObjectOperatorsBCTest.php b/Tests/Tokens/Collections/ObjectOperatorsBCTest.php new file mode 100644 index 00000000..f8204099 --- /dev/null +++ b/Tests/Tokens/Collections/ObjectOperatorsBCTest.php @@ -0,0 +1,49 @@ + \T_OBJECT_OPERATOR, + \T_DOUBLE_COLON => \T_DOUBLE_COLON, + ]; + + if (\version_compare(\PHP_VERSION_ID, '80000', '>=') === true + ) { + $expected[\T_NULLSAFE_OBJECT_OPERATOR] = \T_NULLSAFE_OBJECT_OPERATOR; + } else { + $expected[\T_INLINE_THEN] = \T_INLINE_THEN; + } + + $this->assertSame($expected, Collections::objectOperatorsBC()); + } +} From c816e8e84655aa0597ad77fc36503753aabaa5e1 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 7 Aug 2020 11:27:53 +0200 Subject: [PATCH 4/6] Tokens\Collections: add new `nullsafeObjectOperatorBC() method ... to retrieve the tokens which can represent the nullsafe object operator. This method will return the appropriate token(s) based on the PHP/PHPCS version used. Includes unit test. Related: * squizlabs/PHP_CodeSniffer 3046 --- PHPCSUtils/Tokens/Collections.php | 34 ++++++++++--- .../NullsafeObjectOperatorTokensBCTest.php | 50 +++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 Tests/Tokens/Collections/NullsafeObjectOperatorTokensBCTest.php diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index 68f14d9e..e8f0f8f7 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -633,7 +633,9 @@ public static function objectOperators() * is only used when looking back via `$phpcsFile->findPrevious()` as in that case, a non-backfilled * nullsafe object operator will still match the "normal" object operator. * - * @see \PHPCSUtils\Tokens\Collections::objectOperators() Related method (PHP 8.0+). + * @see \PHPCSUtils\Tokens\Collections::objectOperators() Related method (PHP 8.0+). + * @see \PHPCSUtils\Tokens\Collections::nullsafeObjectOperatorBC() Tokens which can represent a + * nullsafe object operator. * * @since 1.0.0-alpha4 * @@ -646,15 +648,35 @@ public static function objectOperatorsBC() \T_DOUBLE_COLON => \T_DOUBLE_COLON, ]; + $tokens += self::nullsafeObjectOperatorBC(); + + return $tokens; + } + + /** + * Tokens which can represent the nullsafe object operator. + * + * This method will return the appropriate tokens based on the PHP/PHPCS version used. + * + * Note: this is a method, not a property as the `T_NULLSAFE_OBJECT_OPERATOR` token may not exist. + * + * @since 1.0.0-alpha4 + * + * @return array => + */ + public static function nullsafeObjectOperatorBC() + { if (\defined('T_NULLSAFE_OBJECT_OPERATOR') === true) { // PHP 8.0. - $tokens[\T_NULLSAFE_OBJECT_OPERATOR] = \T_NULLSAFE_OBJECT_OPERATOR; - } else { - // PHP < 8.0. - $tokens[\T_INLINE_THEN] = \T_INLINE_THEN; + return [ + \T_NULLSAFE_OBJECT_OPERATOR => \T_NULLSAFE_OBJECT_OPERATOR, + ]; } - return $tokens; + return [ + \T_INLINE_THEN => \T_INLINE_THEN, + \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, + ]; } /** diff --git a/Tests/Tokens/Collections/NullsafeObjectOperatorTokensBCTest.php b/Tests/Tokens/Collections/NullsafeObjectOperatorTokensBCTest.php new file mode 100644 index 00000000..80bfc755 --- /dev/null +++ b/Tests/Tokens/Collections/NullsafeObjectOperatorTokensBCTest.php @@ -0,0 +1,50 @@ +=') === true + ) { + $expected = [ + \T_NULLSAFE_OBJECT_OPERATOR => \T_NULLSAFE_OBJECT_OPERATOR, + ]; + } else { + $expected = [ + \T_INLINE_THEN => \T_INLINE_THEN, + \T_OBJECT_OPERATOR => \T_OBJECT_OPERATOR, + ]; + } + + $this->assertSame($expected, Collections::nullsafeObjectOperatorBC()); + } +} From 50fc1d1c98773a2099e69e1f41dcfba991d08a4e Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 7 Aug 2020 11:38:46 +0200 Subject: [PATCH 5/6] New Operators::isNullsafeObjectOperator() method ... which emulates the backfill as pulled to PHPCS itself. PHP 8 introduces a new object chaining operator `?->` which short-circuits moving to the next expression if the left-hand side evaluates to `null`. This operator can not be used in write-context, but that is not the concern of this method. For PHPCS versions where the backfill is not available yet, this method can be used to determine whether a `?` or `->` token is actually part of the `?->` nullsafe object operator token. Includes perfunctory unit tests. Includes mentioning the method in appropriate places in the documentation elsewhere. Refs: * squizlabs/PHP_CodeSniffer 3046 * https://wiki.php.net/rfc/nullsafe_operator * https://github.com/php/php-src/commit/9bf119832dbf625174794834c71b1e793450d87f --- PHPCSUtils/Tokens/Collections.php | 13 ++ PHPCSUtils/Utils/Operators.php | 50 ++++++ .../IsNullsafeObjectOperatorTest.inc | 33 ++++ .../IsNullsafeObjectOperatorTest.php | 156 ++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 Tests/Utils/Operators/IsNullsafeObjectOperatorTest.inc create mode 100644 Tests/Utils/Operators/IsNullsafeObjectOperatorTest.php diff --git a/PHPCSUtils/Tokens/Collections.php b/PHPCSUtils/Tokens/Collections.php index e8f0f8f7..c843dfe4 100644 --- a/PHPCSUtils/Tokens/Collections.php +++ b/PHPCSUtils/Tokens/Collections.php @@ -633,9 +633,15 @@ public static function objectOperators() * is only used when looking back via `$phpcsFile->findPrevious()` as in that case, a non-backfilled * nullsafe object operator will still match the "normal" object operator. * + * Note: if this method is used, the {@see \PHPCSUtils\Utils\Operators::isNullsafeObjectOperator()} + * method needs to be used on potential nullsafe object operator tokens to verify whether it really + * is a nullsafe object operator or not. + * * @see \PHPCSUtils\Tokens\Collections::objectOperators() Related method (PHP 8.0+). * @see \PHPCSUtils\Tokens\Collections::nullsafeObjectOperatorBC() Tokens which can represent a * nullsafe object operator. + * @see \PHPCSUtils\Utils\Operators::isNullsafeObjectOperator() Nullsafe object operator detection for + * PHP < 8.0. * * @since 1.0.0-alpha4 * @@ -660,6 +666,13 @@ public static function objectOperatorsBC() * * Note: this is a method, not a property as the `T_NULLSAFE_OBJECT_OPERATOR` token may not exist. * + * Note: if this method is used, the {@see \PHPCSUtils\Utils\Operators::isNullsafeObjectOperator()} + * method needs to be used on potential nullsafe object operator tokens to verify whether it really + * is a nullsafe object operator or not. + * + * @see \PHPCSUtils\Utils\Operators::isNullsafeObjectOperator() Nullsafe object operator detection for + * PHP < 8.0. + * * @since 1.0.0-alpha4 * * @return array => diff --git a/PHPCSUtils/Utils/Operators.php b/PHPCSUtils/Utils/Operators.php index f55129ee..0672737e 100644 --- a/PHPCSUtils/Utils/Operators.php +++ b/PHPCSUtils/Utils/Operators.php @@ -311,6 +311,56 @@ public static function isTypeUnion(File $phpcsFile, $stackPtr) return false; } + /** + * Determine whether a token is (part of) a nullsafe object operator. + * + * Helper method for PHP < 8.0 in combination with PHPCS versions in which the + * `T_NULLSAFE_OBJECT_OPERATOR` token is not yet backfilled. + * + * @since 1.0.0-alpha4 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the T_INLINE_THEN or T_OBJECT_OPERATOR + * token in the stack. + * + * @return bool `TRUE` if nullsafe object operator; or `FALSE` otherwise. + */ + public static function isNullsafeObjectOperator(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + if (isset($tokens[$stackPtr]) === false) { + return false; + } + + // Safeguard in case this method is used on PHP 8 and the nullsafe object operator would be passed. + if ($tokens[$stackPtr]['type'] === 'T_NULLSAFE_OBJECT_OPERATOR') { + return true; + } + + if (isset(Collections::nullsafeObjectOperatorBC()[$tokens[$stackPtr]['code']]) === false) { + return false; + } + + /* + * Note: not bypassing empty tokens as whitespace and comments are not allowed + * within an operator. + */ + if ($tokens[$stackPtr]['code'] === \T_INLINE_THEN) { + if (isset($tokens[$stackPtr + 1]) && $tokens[$stackPtr + 1]['code'] === \T_OBJECT_OPERATOR) { + return true; + } + } + + if ($tokens[$stackPtr]['code'] === \T_OBJECT_OPERATOR) { + if (isset($tokens[$stackPtr - 1]) && $tokens[$stackPtr - 1]['code'] === \T_INLINE_THEN) { + return true; + } + } + + // Not a nullsafe object operator token. + return false; + } + /** * Determine whether a T_MINUS/T_PLUS token is a unary operator. * diff --git a/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.inc b/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.inc new file mode 100644 index 00000000..aa7c34ff --- /dev/null +++ b/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.inc @@ -0,0 +1,33 @@ +foo; + +/* testNullsafeObjectOperator */ +echo $obj?->foo; + +/* testNullsafeObjectOperatorWriteContext */ +// Intentional parse error, but not the concern of this method. +$foo?->bar->baz = 'baz'; + +/* testTernaryThen */ +echo $obj ? $obj->prop : /* testObjectOperatorInTernary */ $other->prop; + +/* testParseErrorWhitespaceNotAllowed */ +echo $obj ? + -> foo; + +/* testParseErrorCommentNotAllowed */ +echo $obj ?/*comment*/-> foo; + +/* testLiveCoding */ +// Intentional parse error. This has to be the last test in the file. +echo $obj? + diff --git a/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.php b/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.php new file mode 100644 index 00000000..fff86478 --- /dev/null +++ b/Tests/Utils/Operators/IsNullsafeObjectOperatorTest.php @@ -0,0 +1,156 @@ +assertFalse(Operators::isNullsafeObjectOperator(self::$phpcsFile, 10000)); + } + + /** + * Test that false is returned when an unsupported token is passed. + * + * @return void + */ + public function testUnsupportedToken() + { + $target = $this->getTargetToken('/* testUnsupportedToken */', \T_DOUBLE_COLON); + $this->assertFalse(Operators::isNullsafeObjectOperator(self::$phpcsFile, $target)); + } + + /** + * Test whether a nullsafe object operator is correctly identified as such. + * + * @dataProvider dataIsNullsafeObjectOperator + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * + * @return void + */ + public function testIsNullsafeObjectOperator($testMarker) + { + $targetTokenTypes = $this->getTargetTokensTypes(); + $stackPtr = $this->getTargetToken($testMarker, $targetTokenTypes); + + $this->assertTrue( + Operators::isNullsafeObjectOperator(self::$phpcsFile, $stackPtr), + 'Failed asserting that (first) token is the nullsafe object operator' + ); + + // Also test the second token of a non-backfilled nullsafe object operator. + $tokens = self::$phpcsFile->getTokens(); + if ($tokens[$stackPtr]['code'] === \T_INLINE_THEN) { + $stackPtr = $this->getTargetToken($testMarker, [\T_OBJECT_OPERATOR]); + + $this->assertTrue( + Operators::isNullsafeObjectOperator(self::$phpcsFile, $stackPtr), + 'Failed asserting that (second) token is the nullsafe object operator' + ); + } + } + + /** + * Data provider. + * + * @see testIsNullsafeObjectOperator() + * + * @return array + */ + public function dataIsNullsafeObjectOperator() + { + return [ + 'nullsafe' => ['/* testNullsafeObjectOperator */'], + 'nullsafe-write-context' => ['/* testNullsafeObjectOperatorWriteContext */'], + ]; + } + + /** + * Test whether tokens which can be confused with a non-nullsafe object operator are + * not misidentified as a nullsafe object operator. + * + * @dataProvider dataNotNullsafeObjectOperator + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool $textNext Whether to also test the next non-empty token. Defaults to false. + * + * @return void + */ + public function testNotNullsafeObjectOperator($testMarker, $textNext = false) + { + $stackPtr = $this->getTargetToken($testMarker, $this->getTargetTokensTypes()); + + $this->assertFalse(Operators::isNullsafeObjectOperator(self::$phpcsFile, $stackPtr)); + + if ($textNext === true) { + $next = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + $this->assertFalse(Operators::isNullsafeObjectOperator(self::$phpcsFile, $next)); + } + } + + /** + * Data provider. + * + * @see testNotNullsafeObjectOperator() + * + * @return array + */ + public function dataNotNullsafeObjectOperator() + { + return [ + 'normal-object-operator' => ['/* testObjectOperator */'], + 'ternary-then' => ['/* testTernaryThen */'], + 'object-operator-in-ternary' => ['/* testObjectOperatorInTernary */'], + 'parse-error-whitespace-not-allowed' => ['/* testParseErrorWhitespaceNotAllowed */', true], + 'parse-error-comment-not-allowed' => ['/* testParseErrorCommentNotAllowed */', true], + 'live-coding' => ['/* testLiveCoding */'], + ]; + } + + /** + * Get the target token types to pass to the getTargetToken() method. + * + * @return array => + */ + private function getTargetTokensTypes() + { + $targets = [ + \T_OBJECT_OPERATOR, + \T_INLINE_THEN, + ]; + + if (defined('T_NULLSAFE_OBJECT_OPERATOR') === true) { + $targets[] = \T_NULLSAFE_OBJECT_OPERATOR; + } + + return $targets; + } +} From 0ec9ce60397b0056ace5cfbfc40b03c0b70c8b9c Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sat, 1 Aug 2020 00:25:23 +0200 Subject: [PATCH 6/6] Namespaces::getType: switch to objectOperators() method ... and add a unit test using the nullsafe object operator. No additional code is needed for the correct functioning of this method, as when the nullsafe object operator is not yet backfilled, the method will correctly signal on the object operator part of the token. --- PHPCSUtils/Utils/Namespaces.php | 2 +- Tests/Utils/Namespaces/NamespaceTypeTest.inc | 3 +++ Tests/Utils/Namespaces/NamespaceTypeTest.php | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/PHPCSUtils/Utils/Namespaces.php b/PHPCSUtils/Utils/Namespaces.php index 47458ce2..fa01b6bc 100644 --- a/PHPCSUtils/Utils/Namespaces.php +++ b/PHPCSUtils/Utils/Namespaces.php @@ -62,7 +62,7 @@ public static function getType(File $phpcsFile, $stackPtr) + Tokens::$castTokens + Tokens::$blockOpeners + Collections::$incrementDecrementOperators - + Collections::$objectOperators; + + Collections::objectOperators(); $findAfter[\T_OPEN_CURLY_BRACKET] = \T_OPEN_CURLY_BRACKET; $findAfter[\T_OPEN_SQUARE_BRACKET] = \T_OPEN_SQUARE_BRACKET; diff --git a/Tests/Utils/Namespaces/NamespaceTypeTest.inc b/Tests/Utils/Namespaces/NamespaceTypeTest.inc index 99e13c1a..7b9c31ab 100644 --- a/Tests/Utils/Namespaces/NamespaceTypeTest.inc +++ b/Tests/Utils/Namespaces/NamespaceTypeTest.inc @@ -45,6 +45,9 @@ namespace\ClassName::$property++; /* testNamespaceOperatorGlobalNamespaceStartOfStatementCombiWithNonConfusingToken3 */ namespace\CONSTANT['key']; +/* testNamespaceOperatorGlobalNamespaceStartOfStatementCombiWithNonConfusingToken4 */ +namespace\functionReturningObj()?->chained(); + /* testParseErrorScopedNamespaceDeclaration */ function testScope() { diff --git a/Tests/Utils/Namespaces/NamespaceTypeTest.php b/Tests/Utils/Namespaces/NamespaceTypeTest.php index 5eb39394..5be9717a 100644 --- a/Tests/Utils/Namespaces/NamespaceTypeTest.php +++ b/Tests/Utils/Namespaces/NamespaceTypeTest.php @@ -184,6 +184,13 @@ public function dataNamespaceType() 'operator' => true, ], ], + 'namespace-operator-global-namespace-start-of-statement-with-non-confusing-token-4' => [ + '/* testNamespaceOperatorGlobalNamespaceStartOfStatementCombiWithNonConfusingToken4 */', + [ + 'declaration' => false, + 'operator' => true, + ], + ], 'parse-error-scoped-namespace-declaration' => [ '/* testParseErrorScopedNamespaceDeclaration */', [