* @copyright 2024 PHPCSStandards and contributors * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence */ namespace PHP_CodeSniffer\Tests\Core\Tokenizer\PHP; use PHP_CodeSniffer\Tests\Core\Tokenizer\AbstractTokenizerTestCase; use PHP_CodeSniffer\Util\Tokens; final class DNFTypesParseError2Test extends AbstractTokenizerTestCase { /** * Document handling for a DNF type / parse error where the type declaration contains an unmatched parenthesis. * * @param string $testMarker The comment prefacing the target token. * * @dataProvider dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ public function testBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens($testMarker) { $tokens = $this->phpcsFile->getTokens(); // Verify that the type union is still tokenized as T_BITWISE_OR as the type declaration // is not recognized as a valid type declaration. $unionPtr = $this->getTargetToken($testMarker, [T_BITWISE_OR, T_TYPE_UNION], '|'); $token = $tokens[$unionPtr]; $this->assertSame(T_BITWISE_OR, $token['code'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (code)'); $this->assertSame('T_BITWISE_OR', $token['type'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (type)'); // Verify that the unmatched open parenthesis is tokenized as a normal parenthesis. $openPtr = $this->getTargetToken($testMarker, [T_OPEN_PARENTHESIS, T_TYPE_OPEN_PARENTHESIS], '('); $token = $tokens[$openPtr]; $this->assertSame(T_OPEN_PARENTHESIS, $token['code'], 'Token tokenized as '.$token['type'].', not T_OPEN_PARENTHESIS (code)'); $this->assertSame('T_OPEN_PARENTHESIS', $token['type'], 'Token tokenized as '.$token['type'].', not T_OPEN_PARENTHESIS (type)'); // Verify that the type intersection is still tokenized as T_BITWISE_AND as the type declaration // is not recognized as a valid type declaration. $intersectPtr = $this->getTargetToken($testMarker, [T_BITWISE_AND, T_TYPE_INTERSECTION], '&'); $token = $tokens[$intersectPtr]; $this->assertSame(T_BITWISE_AND, $token['code'], 'Token tokenized as '.$token['type'].', not T_BITWISE_AND (code)'); $this->assertSame('T_BITWISE_AND', $token['type'], 'Token tokenized as '.$token['type'].', not T_BITWISE_AND (type)'); }//end testBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens() /** * Data provider. * * @see testBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens() * * @return array> */ public static function dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens() { return [ 'OO const type' => ['/* testBrokenConstDNFTypeParensMissingClose */'], 'OO property type' => ['/* testBrokenPropertyDNFTypeParensMissingClose */'], 'Parameter type' => ['/* testBrokenParamDNFTypeParensMissingClose */'], 'Return type' => ['/* testBrokenReturnDNFTypeParensMissingClose */'], ]; }//end dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingCloseParens() /** * Document handling for a DNF type / parse error where the type declaration contains an unmatched parenthesis. * * @param string $testMarker The comment prefacing the target token. * * @dataProvider dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ public function testBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens($testMarker) { $tokens = $this->phpcsFile->getTokens(); // Verify that the type union is still tokenized as T_BITWISE_OR as the type declaration // is not recognized as a valid type declaration. $unionPtr = $this->getTargetToken($testMarker, [T_BITWISE_OR, T_TYPE_UNION], '|'); $token = $tokens[$unionPtr]; $this->assertSame(T_BITWISE_OR, $token['code'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (code)'); $this->assertSame('T_BITWISE_OR', $token['type'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (type)'); // Verify that the unmatched open parenthesis is tokenized as a normal parenthesis. $closePtr = $this->getTargetToken($testMarker, [T_CLOSE_PARENTHESIS, T_TYPE_CLOSE_PARENTHESIS], ')'); $token = $tokens[$closePtr]; $this->assertSame(T_CLOSE_PARENTHESIS, $token['code'], 'Token tokenized as '.$token['type'].', not T_CLOSE_PARENTHESIS (code)'); $this->assertSame('T_CLOSE_PARENTHESIS', $token['type'], 'Token tokenized as '.$token['type'].', not T_CLOSE_PARENTHESIS (type)'); // Verify that the type intersection is still tokenized as T_BITWISE_AND as the type declaration // is not recognized as a valid type declaration. $intersectPtr = $this->getTargetToken($testMarker, [T_BITWISE_AND, T_TYPE_INTERSECTION], '&'); $token = $tokens[$intersectPtr]; $this->assertSame(T_BITWISE_AND, $token['code'], 'Token tokenized as '.$token['type'].', not T_BITWISE_AND (code)'); $this->assertSame('T_BITWISE_AND', $token['type'], 'Token tokenized as '.$token['type'].', not T_BITWISE_AND (type)'); }//end testBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens() /** * Data provider. * * @see testBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens() * * @return array> */ public static function dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens() { return [ 'OO const type' => ['/* testBrokenConstDNFTypeParensMissingOpen */'], 'OO property type' => ['/* testBrokenPropertyDNFTypeParensMissingOpen */'], 'Parameter type' => ['/* testBrokenParamDNFTypeParensMissingOpen */'], 'Return type' => ['/* testBrokenReturnDNFTypeParensMissingOpen */'], ]; }//end dataBrokenDNFTypeParensShouldAlwaysBeAPairMissingOpenParens() /** * Document handling for a DNF type / parse error where the type declaration contains an unmatched parenthesis, * but also contains a set of matched parentheses. * * @param string $testMarker The comment prefacing the target token. * * @dataProvider dataBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional * * @return void */ public function testBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched($testMarker) { $tokens = $this->phpcsFile->getTokens(); $startPtr = $this->getTargetToken($testMarker, [T_OPEN_PARENTHESIS, T_TYPE_OPEN_PARENTHESIS], '('); for ($i = $startPtr; $i < $this->phpcsFile->numTokens; $i++) { if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) { continue; } if ($tokens[$i]['code'] === T_EQUAL || $tokens[$i]['code'] === T_VARIABLE || $tokens[$i]['code'] === T_OPEN_CURLY_BRACKET ) { // Reached the end of the type. break; } $errorPrefix = 'Token tokenized as '.$tokens[$i]['type']; // Verify that type tokens have not been retokenized to `T_TYPE_*` tokens for broken type declarations. switch ($tokens[$i]['content']) { case '|': $this->assertSame(T_BITWISE_OR, $tokens[$i]['code'], $errorPrefix.', not T_BITWISE_OR (code)'); $this->assertSame('T_BITWISE_OR', $tokens[$i]['type'], $errorPrefix.', not T_BITWISE_OR (type)'); break; case '&': $this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], $errorPrefix.', not T_BITWISE_AND (code)'); $this->assertSame('T_BITWISE_AND', $tokens[$i]['type'], $errorPrefix.', not T_BITWISE_AND (type)'); break; case '(': // Verify that the open parenthesis is tokenized as a normal parenthesis. $this->assertSame(T_OPEN_PARENTHESIS, $tokens[$i]['code'], $errorPrefix.', not T_OPEN_PARENTHESIS (code)'); $this->assertSame('T_OPEN_PARENTHESIS', $tokens[$i]['type'], $errorPrefix.', not T_OPEN_PARENTHESIS (type)'); break; case ')': $this->assertSame(T_CLOSE_PARENTHESIS, $tokens[$i]['code'], $errorPrefix.', not T_CLOSE_PARENTHESIS (code)'); $this->assertSame('T_CLOSE_PARENTHESIS', $tokens[$i]['type'], $errorPrefix.', not T_CLOSE_PARENTHESIS (type)'); break; default: break; }//end switch }//end for }//end testBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched() /** * Data provider. * * @see testBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched() * * @return array> */ public static function dataBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched() { return [ 'OO const type - missing one close parenthesis' => ['/* testBrokenConstDNFTypeParensMissingOneClose */'], 'OO property type - missing one open parenthesis' => ['/* testBrokenPropertyDNFTypeParensMissingOneOpen */'], 'Parameter type - missing one close parenthesis' => ['/* testBrokenParamDNFTypeParensMissingOneClose */'], 'Return type - missing one open parenthesis' => ['/* testBrokenReturnDNFTypeParensMissingOneOpen */'], ]; }//end dataBrokenDNFTypeParensShouldAlwaysBeAPairMatchedAndUnmatched() }//end class