From b15bae447921a7b8f6191d0098c318ae9799a487 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 20 May 2021 11:20:56 +0200 Subject: [PATCH] Arrays::isShortArray()/Lists::isShortList(): bugfix for tokenizer issue icw short open tag The tokenizer issue as tested for in the BC1 and BC2 test case files and described in squizlabs/php_codesniffer 1971, where a short array bracket is tokenized as a square bracket, also happens when the PHP short open echo tag is used to open PHP. This commit accounts for this tokenizer bug in the `Arrays::isShortArray()` and the `Lists::isShortList()` methods and adds additional tests to safeguard the correct handling of this in the `Arrays::isShortArray()` and the `Lists::isShortList()` methods. --- PHPCSUtils/Utils/Arrays.php | 4 +- PHPCSUtils/Utils/Lists.php | 3 +- .../IsShortArrayOrListTokenizerBC3Test.inc | 5 + .../IsShortArrayOrListTokenizerBC3Test.php | 119 ++++++++++++++++++ 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 Tests/Utils/Lists/IsShortArrayOrListTokenizerBC3Test.inc create mode 100644 Tests/Utils/Lists/IsShortArrayOrListTokenizerBC3Test.php diff --git a/PHPCSUtils/Utils/Arrays.php b/PHPCSUtils/Utils/Arrays.php index d1fe32b0..af8234b7 100644 --- a/PHPCSUtils/Utils/Arrays.php +++ b/PHPCSUtils/Utils/Arrays.php @@ -105,7 +105,9 @@ public static function isShortArray(File $phpcsFile, $stackPtr) * * @link https://github.com/squizlabs/PHP_CodeSniffer/issues/1971 */ - if ($prevNonEmpty !== 0 || $tokens[$prevNonEmpty]['code'] !== \T_OPEN_TAG) { + if ($prevNonEmpty !== 0 + || isset(Collections::phpOpenTags()[$tokens[$prevNonEmpty]['code']]) === false + ) { return false; } } diff --git a/PHPCSUtils/Utils/Lists.php b/PHPCSUtils/Utils/Lists.php index 12b76309..4c9d229c 100644 --- a/PHPCSUtils/Utils/Lists.php +++ b/PHPCSUtils/Utils/Lists.php @@ -106,7 +106,8 @@ public static function isShortList(File $phpcsFile, $stackPtr) } $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($opener - 1), null, true); - if ((($prevNonEmpty === 0 && $tokens[$prevNonEmpty]['code'] === \T_OPEN_TAG) // Bug #1971. + if ((($prevNonEmpty === 0 + && isset(Collections::phpOpenTags()[$tokens[$prevNonEmpty]['code']]) === true) // Bug #1971. || ($tokens[$prevNonEmpty]['code'] === \T_CLOSE_CURLY_BRACKET && isset($tokens[$prevNonEmpty]['scope_condition']))) // Bug #1284. ) { diff --git a/Tests/Utils/Lists/IsShortArrayOrListTokenizerBC3Test.inc b/Tests/Utils/Lists/IsShortArrayOrListTokenizerBC3Test.inc new file mode 100644 index 00000000..0145bfe5 --- /dev/null +++ b/Tests/Utils/Lists/IsShortArrayOrListTokenizerBC3Test.inc @@ -0,0 +1,5 @@ + true, + 'array' => true, + ]; + + $this->assertNotSame($forbidden, $data); + } + + /** + * Test correctly determining whether a short array open token is a short array, + * even when the token is incorrectly tokenized. + * + * @dataProvider dataIsShortArrayOrList + * @covers \PHPCSUtils\Utils\Arrays::isShortArray + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool[] $expected The expected boolean return value for list and array. + * + * @return void + */ + public function testIsShortArray($testMarker, $expected) + { + $stackPtr = $this->getTargetToken($testMarker, [\T_OPEN_SHORT_ARRAY, \T_OPEN_SQUARE_BRACKET]); + $result = Arrays::isShortArray(self::$phpcsFile, $stackPtr); + + $this->assertSame($expected['array'], $result); + } + + /** + * Test correctly determining whether a short array open token is a short array or a short list, + * even when the token is incorrectly tokenized. + * + * @dataProvider dataIsShortArrayOrList + * @covers \PHPCSUtils\Utils\Lists::isShortList + * + * @param string $testMarker The comment which prefaces the target token in the test file. + * @param bool[] $expected The expected boolean return value for list and array. + * + * @return void + */ + public function testIsShortList($testMarker, $expected) + { + $stackPtr = $this->getTargetToken($testMarker, [\T_OPEN_SHORT_ARRAY, \T_OPEN_SQUARE_BRACKET]); + $result = Lists::isShortList(self::$phpcsFile, $stackPtr); + + $this->assertSame($expected['list'], $result); + } + + /** + * Data provider. + * + * @see testIsShortArray() For the array format. + * @see testIsShortList() For the array format. + * + * @return array + */ + public function dataIsShortArrayOrList() + { + return [ + 'issue-1971-short-array-first-in-file' => [ + '/* testTokenizerIssue1971PHPCSlt330gt271E */', + [ + 'array' => true, + 'list' => false, + ], + ], + 'issue-1971-short-array-first-in-file-nested' => [ + '/* testTokenizerIssue1971PHPCSlt330gt271F */', + [ + 'array' => true, + 'list' => false, + ], + ], + ]; + } +}