xarxaprod-wp-theme/vendor/squizlabs/php_codesniffer/tests/Core/Tokenizer/BackfillMatchTokenTest.php

530 lines
21 KiB
PHP

<?php
/**
* Tests the backfilling of the T_MATCH token to PHP < 8.0, as well as the
* setting of parenthesis/scopes for match control structures across PHP versions.
*
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2020-2021 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
use PHP_CodeSniffer\Util\Tokens;
class BackfillMatchTokenTest extends AbstractMethodUnitTest
{
/**
* Test tokenization of match expressions.
*
* @param string $testMarker The comment prefacing the target token.
* @param int $openerOffset The expected offset of the scope opener in relation to the testMarker.
* @param int $closerOffset The expected offset of the scope closer in relation to the testMarker.
* @param string $testContent The token content to look for.
*
* @dataProvider dataMatchExpression
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
*
* @return void
*/
public function testMatchExpression($testMarker, $openerOffset, $closerOffset, $testContent='match')
{
$tokens = self::$phpcsFile->getTokens();
$token = $this->getTargetToken($testMarker, [T_STRING, T_MATCH], $testContent);
$tokenArray = $tokens[$token];
$this->assertSame(T_MATCH, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH (code)');
$this->assertSame('T_MATCH', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_MATCH (type)');
$this->scopeTestHelper($token, $openerOffset, $closerOffset);
$this->parenthesisTestHelper($token);
}//end testMatchExpression()
/**
* Data provider.
*
* @see testMatchExpression()
*
* @return array
*/
public function dataMatchExpression()
{
return [
'simple_match' => [
'/* testMatchSimple */',
6,
33,
],
'no_trailing_comma' => [
'/* testMatchNoTrailingComma */',
6,
24,
],
'with_default_case' => [
'/* testMatchWithDefault */',
6,
33,
],
'expression_in_condition' => [
'/* testMatchExpressionInCondition */',
6,
77,
],
'multicase' => [
'/* testMatchMultiCase */',
6,
40,
],
'multicase_trailing_comma_in_case' => [
'/* testMatchMultiCaseTrailingCommaInCase */',
6,
47,
],
'in_closure_not_lowercase' => [
'/* testMatchInClosureNotLowercase */',
6,
36,
'Match',
],
'in_arrow_function' => [
'/* testMatchInArrowFunction */',
5,
36,
],
'arrow_function_in_match_no_trailing_comma' => [
'/* testArrowFunctionInMatchNoTrailingComma */',
6,
44,
],
'in_function_call_param_not_lowercase' => [
'/* testMatchInFunctionCallParamNotLowercase */',
8,
32,
'MATCH',
],
'in_method_call_param' => [
'/* testMatchInMethodCallParam */',
5,
13,
],
'discard_result' => [
'/* testMatchDiscardResult */',
6,
18,
],
'duplicate_conditions_and_comments' => [
'/* testMatchWithDuplicateConditionsWithComments */',
12,
59,
],
'nested_match_outer' => [
'/* testNestedMatchOuter */',
6,
33,
],
'nested_match_inner' => [
'/* testNestedMatchInner */',
6,
14,
],
'ternary_condition' => [
'/* testMatchInTernaryCondition */',
6,
21,
],
'ternary_then' => [
'/* testMatchInTernaryThen */',
6,
21,
],
'ternary_else' => [
'/* testMatchInTernaryElse */',
6,
21,
],
'array_value' => [
'/* testMatchInArrayValue */',
6,
21,
],
'array_key' => [
'/* testMatchInArrayKey */',
6,
21,
],
'returning_array' => [
'/* testMatchreturningArray */',
6,
125,
],
'nested_in_switch_case_1' => [
'/* testMatchWithDefaultNestedInSwitchCase1 */',
6,
25,
],
'nested_in_switch_case_2' => [
'/* testMatchWithDefaultNestedInSwitchCase2 */',
6,
25,
],
'nested_in_switch_default' => [
'/* testMatchWithDefaultNestedInSwitchDefault */',
6,
25,
],
'match_with_nested_switch' => [
'/* testMatchContainingSwitch */',
6,
180,
],
'no_cases' => [
'/* testMatchNoCases */',
6,
7,
],
'multi_default' => [
'/* testMatchMultiDefault */',
6,
40,
],
];
}//end dataMatchExpression()
/**
* Verify that "match" keywords which are not match control structures get tokenized as T_STRING
* and don't have the extra token array indexes.
*
* @param string $testMarker The comment prefacing the target token.
* @param string $testContent The token content to look for.
*
* @dataProvider dataNotAMatchStructure
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
*
* @return void
*/
public function testNotAMatchStructure($testMarker, $testContent='match')
{
$tokens = self::$phpcsFile->getTokens();
$token = $this->getTargetToken($testMarker, [T_STRING, T_MATCH], $testContent);
$tokenArray = $tokens[$token];
$this->assertSame(T_STRING, $tokenArray['code'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (code)');
$this->assertSame('T_STRING', $tokenArray['type'], 'Token tokenized as '.$tokenArray['type'].', not T_STRING (type)');
$this->assertArrayNotHasKey('scope_condition', $tokenArray, 'Scope condition is set');
$this->assertArrayNotHasKey('scope_opener', $tokenArray, 'Scope opener is set');
$this->assertArrayNotHasKey('scope_closer', $tokenArray, 'Scope closer is set');
$this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set');
$this->assertArrayNotHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is set');
$this->assertArrayNotHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is set');
$next = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($token + 1), null, true);
if ($next !== false && $tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
$this->assertArrayNotHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is set for opener after');
}
}//end testNotAMatchStructure()
/**
* Data provider.
*
* @see testNotAMatchStructure()
*
* @return array
*/
public function dataNotAMatchStructure()
{
return [
'static_method_call' => ['/* testNoMatchStaticMethodCall */'],
'class_constant_access' => [
'/* testNoMatchClassConstantAccess */',
'MATCH',
],
'class_constant_array_access' => [
'/* testNoMatchClassConstantArrayAccessMixedCase */',
'Match',
],
'method_call' => ['/* testNoMatchMethodCall */'],
'method_call_uppercase' => [
'/* testNoMatchMethodCallUpper */',
'MATCH',
],
'property_access' => ['/* testNoMatchPropertyAccess */'],
'namespaced_function_call' => ['/* testNoMatchNamespacedFunctionCall */'],
'namespace_operator_function_call' => ['/* testNoMatchNamespaceOperatorFunctionCall */'],
'interface_method_declaration' => ['/* testNoMatchInterfaceMethodDeclaration */'],
'class_constant_declaration' => ['/* testNoMatchClassConstantDeclarationLower */'],
'class_method_declaration' => ['/* testNoMatchClassMethodDeclaration */'],
'property_assigment' => ['/* testNoMatchPropertyAssignment */'],
'class_instantiation' => [
'/* testNoMatchClassInstantiation */',
'Match',
],
'anon_class_method_declaration' => [
'/* testNoMatchAnonClassMethodDeclaration */',
'maTCH',
],
'class_declaration' => [
'/* testNoMatchClassDeclaration */',
'Match',
],
'interface_declaration' => [
'/* testNoMatchInterfaceDeclaration */',
'Match',
],
'trait_declaration' => [
'/* testNoMatchTraitDeclaration */',
'Match',
],
'constant_declaration' => [
'/* testNoMatchConstantDeclaration */',
'MATCH',
],
'function_declaration' => ['/* testNoMatchFunctionDeclaration */'],
'namespace_declaration' => [
'/* testNoMatchNamespaceDeclaration */',
'Match',
],
'class_extends_declaration' => [
'/* testNoMatchExtendedClassDeclaration */',
'Match',
],
'class_implements_declaration' => [
'/* testNoMatchImplementedClassDeclaration */',
'Match',
],
'use_statement' => [
'/* testNoMatchInUseStatement */',
'Match',
],
'unsupported_inline_control_structure' => ['/* testNoMatchMissingCurlies */'],
'unsupported_alternative_syntax' => ['/* testNoMatchAlternativeSyntax */'],
'live_coding' => ['/* testLiveCoding */'],
];
}//end dataNotAMatchStructure()
/**
* Verify that the tokenization of switch structures is not affected by the backfill.
*
* @param string $testMarker The comment prefacing the target token.
* @param int $openerOffset The expected offset of the scope opener in relation to the testMarker.
* @param int $closerOffset The expected offset of the scope closer in relation to the testMarker.
*
* @dataProvider dataSwitchExpression
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
*
* @return void
*/
public function testSwitchExpression($testMarker, $openerOffset, $closerOffset)
{
$token = $this->getTargetToken($testMarker, T_SWITCH);
$this->scopeTestHelper($token, $openerOffset, $closerOffset);
$this->parenthesisTestHelper($token);
}//end testSwitchExpression()
/**
* Data provider.
*
* @see testSwitchExpression()
*
* @return array
*/
public function dataSwitchExpression()
{
return [
'switch_containing_match' => [
'/* testSwitchContainingMatch */',
6,
174,
],
'match_containing_switch_1' => [
'/* testSwitchNestedInMatch1 */',
5,
63,
],
'match_containing_switch_2' => [
'/* testSwitchNestedInMatch2 */',
5,
63,
],
];
}//end dataSwitchExpression()
/**
* Verify that the tokenization of a switch case/default structure containing a match structure
* or contained *in* a match structure is not affected by the backfill.
*
* @param string $testMarker The comment prefacing the target token.
* @param int $openerOffset The expected offset of the scope opener in relation to the testMarker.
* @param int $closerOffset The expected offset of the scope closer in relation to the testMarker.
*
* @dataProvider dataSwitchCaseVersusMatch
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
*
* @return void
*/
public function testSwitchCaseVersusMatch($testMarker, $openerOffset, $closerOffset)
{
$token = $this->getTargetToken($testMarker, [T_CASE, T_DEFAULT]);
$this->scopeTestHelper($token, $openerOffset, $closerOffset);
}//end testSwitchCaseVersusMatch()
/**
* Data provider.
*
* @see testSwitchCaseVersusMatch()
*
* @return array
*/
public function dataSwitchCaseVersusMatch()
{
return [
'switch_with_nested_match_case_1' => [
'/* testMatchWithDefaultNestedInSwitchCase1 */',
3,
55,
],
'switch_with_nested_match_case_2' => [
'/* testMatchWithDefaultNestedInSwitchCase2 */',
4,
21,
],
'switch_with_nested_match_default_case' => [
'/* testMatchWithDefaultNestedInSwitchDefault */',
1,
38,
],
'match_with_nested_switch_case' => [
'/* testSwitchDefaultNestedInMatchCase */',
1,
18,
],
'match_with_nested_switch_default_case' => [
'/* testSwitchDefaultNestedInMatchDefault */',
1,
20,
],
];
}//end dataSwitchCaseVersusMatch()
/**
* Helper function to verify that all scope related array indexes for a control structure
* are set correctly.
*
* @param string $token The control structure token to check.
* @param int $openerOffset The expected offset of the scope opener in relation to
* the control structure token.
* @param int $closerOffset The expected offset of the scope closer in relation to
* the control structure token.
* @param bool $skipScopeCloserCheck Whether to skip the scope closer check.
* This should be set to "true" when testing nested arrow functions,
* where the "inner" arrow function shares a scope closer with the
* "outer" arrow function, as the 'scope_condition' for the scope closer
* of the "inner" arrow function will point to the "outer" arrow function.
*
* @return void
*/
private function scopeTestHelper($token, $openerOffset, $closerOffset, $skipScopeCloserCheck=false)
{
$tokens = self::$phpcsFile->getTokens();
$tokenArray = $tokens[$token];
$tokenType = $tokenArray['type'];
$expectedScopeOpener = ($token + $openerOffset);
$expectedScopeCloser = ($token + $closerOffset);
$this->assertArrayHasKey('scope_condition', $tokenArray, 'Scope condition is not set');
$this->assertArrayHasKey('scope_opener', $tokenArray, 'Scope opener is not set');
$this->assertArrayHasKey('scope_closer', $tokenArray, 'Scope closer is not set');
$this->assertSame($token, $tokenArray['scope_condition'], 'Scope condition is not the '.$tokenType.' token');
$this->assertSame($expectedScopeOpener, $tokenArray['scope_opener'], 'Scope opener of the '.$tokenType.' token incorrect');
$this->assertSame($expectedScopeCloser, $tokenArray['scope_closer'], 'Scope closer of the '.$tokenType.' token incorrect');
$opener = $tokenArray['scope_opener'];
$this->assertArrayHasKey('scope_condition', $tokens[$opener], 'Opener scope condition is not set');
$this->assertArrayHasKey('scope_opener', $tokens[$opener], 'Opener scope opener is not set');
$this->assertArrayHasKey('scope_closer', $tokens[$opener], 'Opener scope closer is not set');
$this->assertSame($token, $tokens[$opener]['scope_condition'], 'Opener scope condition is not the '.$tokenType.' token');
$this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], $tokenType.' opener scope opener token incorrect');
$this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], $tokenType.' opener scope closer token incorrect');
$closer = $tokenArray['scope_closer'];
$this->assertArrayHasKey('scope_condition', $tokens[$closer], 'Closer scope condition is not set');
$this->assertArrayHasKey('scope_opener', $tokens[$closer], 'Closer scope opener is not set');
$this->assertArrayHasKey('scope_closer', $tokens[$closer], 'Closer scope closer is not set');
if ($skipScopeCloserCheck === false) {
$this->assertSame($token, $tokens[$closer]['scope_condition'], 'Closer scope condition is not the '.$tokenType.' token');
}
$this->assertSame($expectedScopeOpener, $tokens[$closer]['scope_opener'], $tokenType.' closer scope opener token incorrect');
$this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], $tokenType.' closer scope closer token incorrect');
if (($opener + 1) !== $closer) {
for ($i = ($opener + 1); $i < $closer; $i++) {
$this->assertArrayHasKey(
$token,
$tokens[$i]['conditions'],
$tokenType.' condition not added for token belonging to the '.$tokenType.' structure'
);
}
}
}//end scopeTestHelper()
/**
* Helper function to verify that all parenthesis related array indexes for a control structure
* token are set correctly.
*
* @param int $token The position of the control structure token.
*
* @return void
*/
private function parenthesisTestHelper($token)
{
$tokens = self::$phpcsFile->getTokens();
$tokenArray = $tokens[$token];
$tokenType = $tokenArray['type'];
$this->assertArrayHasKey('parenthesis_owner', $tokenArray, 'Parenthesis owner is not set');
$this->assertArrayHasKey('parenthesis_opener', $tokenArray, 'Parenthesis opener is not set');
$this->assertArrayHasKey('parenthesis_closer', $tokenArray, 'Parenthesis closer is not set');
$this->assertSame($token, $tokenArray['parenthesis_owner'], 'Parenthesis owner is not the '.$tokenType.' token');
$opener = $tokenArray['parenthesis_opener'];
$this->assertArrayHasKey('parenthesis_owner', $tokens[$opener], 'Opening parenthesis owner is not set');
$this->assertSame($token, $tokens[$opener]['parenthesis_owner'], 'Opening parenthesis owner is not the '.$tokenType.' token');
$closer = $tokenArray['parenthesis_closer'];
$this->assertArrayHasKey('parenthesis_owner', $tokens[$closer], 'Closing parenthesis owner is not set');
$this->assertSame($token, $tokens[$closer]['parenthesis_owner'], 'Closing parenthesis owner is not the '.$tokenType.' token');
}//end parenthesisTestHelper()
}//end class