diff --git a/package.xml b/package.xml
index ee04a0e3e5..f3ceca5c0b 100644
--- a/package.xml
+++ b/package.xml
@@ -141,6 +141,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2100,6 +2102,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2194,6 +2198,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php
index f18bc4017c..c8efba537d 100644
--- a/src/Tokenizers/PHP.php
+++ b/src/Tokenizers/PHP.php
@@ -589,6 +589,64 @@ protected function tokenize($string)
echo PHP_EOL;
}
+ /*
+ Tokenize context sensitive keyword as string when it should be string.
+ */
+
+ if ($tokenIsArray === true
+ && isset(Util\Tokens::$contextSensitiveKeywords[$token[0]]) === true
+ && isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === true
+ ) {
+ $preserveKeyword = false;
+
+ // `new class` should be preserved
+ if ($token[0] === T_CLASS && $finalTokens[$lastNotEmptyToken]['code'] === T_NEW) {
+ $preserveKeyword = true;
+ }
+
+ // `new class extends` `new class implements` should be preserved
+ if (($token[0] === T_EXTENDS || $token[0] === T_IMPLEMENTS)
+ && $finalTokens[$lastNotEmptyToken]['code'] === T_CLASS
+ ) {
+ $preserveKeyword = true;
+ }
+
+ // `namespace\` should be preserved
+ if ($token[0] === T_NAMESPACE) {
+ for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+ if (is_array($tokens[$i]) === false) {
+ break;
+ }
+
+ if (isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === true) {
+ continue;
+ }
+
+ if ($tokens[$i][0] === T_NS_SEPARATOR) {
+ $preserveKeyword = true;
+ }
+
+ break;
+ }
+ }
+
+ if ($preserveKeyword === false) {
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = Util\Tokens::tokenName($token[0]);
+ echo "\t\t* token $stackPtr changed from $type to T_STRING".PHP_EOL;
+ }
+
+ $finalTokens[$newStackPtr] = [
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ 'content' => $token[1],
+ ];
+
+ $newStackPtr++;
+ continue;
+ }
+ }//end if
+
/*
Parse doc blocks into something that can be easily iterated over.
*/
@@ -1113,6 +1171,7 @@ protected function tokenize($string)
&& $tokenIsArray === true
&& $token[0] === T_STRING
&& strtolower($token[1]) === 'yield'
+ && isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
) {
if (isset($tokens[($stackPtr + 1)]) === true
&& isset($tokens[($stackPtr + 2)]) === true
@@ -1446,57 +1505,42 @@ protected function tokenize($string)
if ($tokenIsArray === true
&& $token[0] === T_DEFAULT
+ && isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
) {
- if (isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false) {
- for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
- if ($tokens[$x] === ',') {
- // Skip over potential trailing comma (supported in PHP).
- continue;
- }
-
- if (is_array($tokens[$x]) === false
- || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
- ) {
- // Non-empty, non-comma content.
- break;
- }
+ for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
+ if ($tokens[$x] === ',') {
+ // Skip over potential trailing comma (supported in PHP).
+ continue;
}
- if (isset($tokens[$x]) === true
- && is_array($tokens[$x]) === true
- && $tokens[$x][0] === T_DOUBLE_ARROW
+ if (is_array($tokens[$x]) === false
+ || isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
) {
- // Modify the original token stack for the double arrow so that
- // future checks can disregard the double arrow token more easily.
- // For match expression "case" statements, this is handled
- // in PHP::processAdditional().
- $tokens[$x][0] = T_MATCH_ARROW;
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- echo "\t\t* token $x changed from T_DOUBLE_ARROW to T_MATCH_ARROW".PHP_EOL;
- }
-
- $newToken = [];
- $newToken['code'] = T_MATCH_DEFAULT;
- $newToken['type'] = 'T_MATCH_DEFAULT';
- $newToken['content'] = $token[1];
+ // Non-empty, non-comma content.
+ break;
+ }
+ }
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- echo "\t\t* token $stackPtr changed from T_DEFAULT to T_MATCH_DEFAULT".PHP_EOL;
- }
+ if (isset($tokens[$x]) === true
+ && is_array($tokens[$x]) === true
+ && $tokens[$x][0] === T_DOUBLE_ARROW
+ ) {
+ // Modify the original token stack for the double arrow so that
+ // future checks can disregard the double arrow token more easily.
+ // For match expression "case" statements, this is handled
+ // in PHP::processAdditional().
+ $tokens[$x][0] = T_MATCH_ARROW;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token $x changed from T_DOUBLE_ARROW to T_MATCH_ARROW".PHP_EOL;
+ }
- $finalTokens[$newStackPtr] = $newToken;
- $newStackPtr++;
- continue;
- }//end if
- } else {
- // Definitely not the "default" keyword.
$newToken = [];
- $newToken['code'] = T_STRING;
- $newToken['type'] = 'T_STRING';
+ $newToken['code'] = T_MATCH_DEFAULT;
+ $newToken['type'] = 'T_MATCH_DEFAULT';
$newToken['content'] = $token[1];
if (PHP_CODESNIFFER_VERBOSITY > 1) {
- echo "\t\t* token $stackPtr changed from T_DEFAULT to T_STRING".PHP_EOL;
+ echo "\t\t* token $stackPtr changed from T_DEFAULT to T_MATCH_DEFAULT".PHP_EOL;
}
$finalTokens[$newStackPtr] = $newToken;
@@ -1693,14 +1737,9 @@ protected function tokenize($string)
}
/*
- The string-like token after a function keyword should always be
- tokenized as T_STRING even if it appears to be a different token,
- such as when writing code like: function default(): foo
- so go forward and change the token type before it is processed.
-
- Note: this should not be done for `function Level\Name` within a
- group use statement for the PHP 8 identifier name tokens as it
- would interfere with the re-tokenization of those.
+ This is a special condition for T_ARRAY tokens used for
+ function return types. We want to keep the parenthesis map clean,
+ so let's tag these tokens as T_STRING.
*/
if ($tokenIsArray === true
@@ -1708,37 +1747,6 @@ protected function tokenize($string)
|| $token[0] === T_FN)
&& $finalTokens[$lastNotEmptyToken]['code'] !== T_USE
) {
- if ($token[0] === T_FUNCTION) {
- for ($x = ($stackPtr + 1); $x < $numTokens; $x++) {
- if (is_array($tokens[$x]) === false
- || (isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === false
- && $tokens[$x][1] !== '&')
- ) {
- // Non-empty content.
- break;
- }
- }
-
- if ($x < $numTokens
- && is_array($tokens[$x]) === true
- && $tokens[$x][0] !== T_STRING
- && $tokens[$x][0] !== T_NAME_QUALIFIED
- ) {
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- $oldType = Util\Tokens::tokenName($tokens[$x][0]);
- echo "\t\t* token $x changed from $oldType to T_STRING".PHP_EOL;
- }
-
- $tokens[$x][0] = T_STRING;
- }
- }//end if
-
- /*
- This is a special condition for T_ARRAY tokens used for
- function return types. We want to keep the parenthesis map clean,
- so let's tag these tokens as T_STRING.
- */
-
// Go looking for the colon to start the return type hint.
// Start by finding the closing parenthesis of the function.
$parenthesisStack = [];
@@ -1778,22 +1786,6 @@ function return types. We want to keep the parenthesis map clean,
&& is_array($tokens[$x]) === false
&& $tokens[$x] === ':'
) {
- $allowed = [
- T_STRING => T_STRING,
- T_NAME_FULLY_QUALIFIED => T_NAME_FULLY_QUALIFIED,
- T_NAME_RELATIVE => T_NAME_RELATIVE,
- T_NAME_QUALIFIED => T_NAME_QUALIFIED,
- T_ARRAY => T_ARRAY,
- T_CALLABLE => T_CALLABLE,
- T_SELF => T_SELF,
- T_PARENT => T_PARENT,
- T_NAMESPACE => T_NAMESPACE,
- T_STATIC => T_STATIC,
- T_NS_SEPARATOR => T_NS_SEPARATOR,
- ];
-
- $allowed += Util\Tokens::$emptyTokens;
-
// Find the start of the return type.
for ($x += 1; $x < $numTokens; $x++) {
if (is_array($tokens[$x]) === true
@@ -1926,31 +1918,31 @@ function return types. We want to keep the parenthesis map clean,
$newStackPtr++;
}
} else {
- if ($tokenIsArray === true && $token[0] === T_STRING) {
- // Some T_STRING tokens should remain that way
- // due to their context.
- if (isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
- // Special case for syntax like: return new self
- // where self should not be a string.
- if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
- && strtolower($token[1]) === 'self'
- ) {
- $finalTokens[$newStackPtr] = [
- 'content' => $token[1],
- 'code' => T_SELF,
- 'type' => 'T_SELF',
- ];
- } else {
- $finalTokens[$newStackPtr] = [
- 'content' => $token[1],
- 'code' => T_STRING,
- 'type' => 'T_STRING',
- ];
- }
+ // Some T_STRING tokens should remain that way due to their context.
+ if ($tokenIsArray === true
+ && $token[0] === T_STRING
+ && isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === true
+ ) {
+ // Special case for syntax like: return new self
+ // where self should not be a string.
+ if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
+ && strtolower($token[1]) === 'self'
+ ) {
+ $finalTokens[$newStackPtr] = [
+ 'content' => $token[1],
+ 'code' => T_SELF,
+ 'type' => 'T_SELF',
+ ];
+ } else {
+ $finalTokens[$newStackPtr] = [
+ 'content' => $token[1],
+ 'code' => T_STRING,
+ 'type' => 'T_STRING',
+ ];
+ }
- $newStackPtr++;
- continue;
- }//end if
+ $newStackPtr++;
+ continue;
}//end if
$newToken = null;
@@ -2114,16 +2106,6 @@ function return types. We want to keep the parenthesis map clean,
$newToken['type'] = 'T_FINALLY';
}
- // This is a special case for the PHP 5.5 classname::class syntax
- // where "class" should be T_STRING instead of T_CLASS.
- if (($newToken['code'] === T_CLASS
- || $newToken['code'] === T_FUNCTION)
- && $finalTokens[$lastNotEmptyToken]['code'] === T_DOUBLE_COLON
- ) {
- $newToken['code'] = T_STRING;
- $newToken['type'] = 'T_STRING';
- }
-
// This is a special case for PHP 5.6 use function and use const
// where "function" and "const" should be T_STRING instead of T_FUNCTION
// and T_CONST.
@@ -2819,34 +2801,11 @@ protected function processAdditional()
$this->tokens[$i]['code'] = T_STRING;
$this->tokens[$i]['type'] = 'T_STRING';
}
- } else if ($this->tokens[$i]['code'] === T_CONST) {
- // Context sensitive keywords support.
- for ($x = ($i + 1); $i < $numTokens; $x++) {
- if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
- // Non-whitespace content.
- break;
- }
- }
-
- if ($this->tokens[$x]['code'] !== T_STRING) {
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- $line = $this->tokens[$x]['line'];
- $type = $this->tokens[$x]['type'];
- echo "\t* token $x on line $line changed from $type to T_STRING".PHP_EOL;
- }
-
- $this->tokens[$x]['code'] = T_STRING;
- $this->tokens[$x]['type'] = 'T_STRING';
- }
- } else if ($this->tokens[$i]['code'] === T_READONLY
- || ($this->tokens[$i]['code'] === T_STRING
- && strtolower($this->tokens[$i]['content']) === 'readonly')
+ } else if ($this->tokens[$i]['code'] === T_STRING
+ && strtolower($this->tokens[$i]['content']) === 'readonly'
) {
/*
- Adds "readonly" keyword support:
- PHP < 8.1: Converts T_STRING to T_READONLY
- PHP >= 8.1: Converts some T_READONLY to T_STRING because token_get_all()
- without the TOKEN_PARSE flag cannot distinguish between them in some situations.
+ Adds "readonly" keyword support for PHP < 8.1.
*/
$allowedAfter = [
@@ -2890,7 +2849,7 @@ protected function processAdditional()
}
}
- if ($this->tokens[$i]['code'] === T_STRING && $shouldBeReadonly === true) {
+ if ($shouldBeReadonly === true) {
if (PHP_CODESNIFFER_VERBOSITY > 1) {
$line = $this->tokens[$i]['line'];
echo "\t* token $i on line $line changed from T_STRING to T_READONLY".PHP_EOL;
@@ -2898,14 +2857,6 @@ protected function processAdditional()
$this->tokens[$i]['code'] = T_READONLY;
$this->tokens[$i]['type'] = 'T_READONLY';
- } else if ($this->tokens[$i]['code'] === T_READONLY && $shouldBeReadonly === false) {
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- $line = $this->tokens[$i]['line'];
- echo "\t* token $i on line $line changed from T_READONLY to T_STRING".PHP_EOL;
- }
-
- $this->tokens[$i]['code'] = T_STRING;
- $this->tokens[$i]['type'] = 'T_STRING';
}
continue;
diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php
index 0bc1747275..b05eb6161a 100644
--- a/src/Util/Tokens.php
+++ b/src/Util/Tokens.php
@@ -653,6 +653,81 @@ final class Tokens
T_TRAIT_C => T_TRAIT_C,
];
+ /**
+ * Tokens representing context sensitive keywords in PHP.
+ *
+ * @var array
+ *
+ * https://wiki.php.net/rfc/context_sensitive_lexer
+ */
+ public static $contextSensitiveKeywords = [
+ T_ABSTRACT => T_ABSTRACT,
+ T_ARRAY => T_ARRAY,
+ T_AS => T_AS,
+ T_BREAK => T_BREAK,
+ T_CALLABLE => T_CALLABLE,
+ T_CASE => T_CASE,
+ T_CATCH => T_CATCH,
+ T_CLASS => T_CLASS,
+ T_CLONE => T_CLONE,
+ T_CONST => T_CONST,
+ T_CONTINUE => T_CONTINUE,
+ T_DECLARE => T_DECLARE,
+ T_DEFAULT => T_DEFAULT,
+ T_DO => T_DO,
+ T_ECHO => T_ECHO,
+ T_ELSE => T_ELSE,
+ T_ELSEIF => T_ELSEIF,
+ T_ENDDECLARE => T_ENDDECLARE,
+ T_ENDFOR => T_ENDFOR,
+ T_ENDFOREACH => T_ENDFOREACH,
+ T_ENDIF => T_ENDIF,
+ T_ENDSWITCH => T_ENDSWITCH,
+ T_ENDWHILE => T_ENDWHILE,
+ T_EXIT => T_EXIT,
+ T_EXTENDS => T_EXTENDS,
+ T_FINAL => T_FINAL,
+ T_FINALLY => T_FINALLY,
+ T_FN => T_FN,
+ T_FOR => T_FOR,
+ T_FOREACH => T_FOREACH,
+ T_FUNCTION => T_FUNCTION,
+ T_GLOBAL => T_GLOBAL,
+ T_GOTO => T_GOTO,
+ T_IF => T_IF,
+ T_IMPLEMENTS => T_IMPLEMENTS,
+ T_INCLUDE => T_INCLUDE,
+ T_INCLUDE_ONCE => T_INCLUDE_ONCE,
+ T_INSTANCEOF => T_INSTANCEOF,
+ T_INSTEADOF => T_INSTEADOF,
+ T_INTERFACE => T_INTERFACE,
+ T_LIST => T_LIST,
+ T_LOGICAL_AND => T_LOGICAL_AND,
+ T_LOGICAL_OR => T_LOGICAL_OR,
+ T_LOGICAL_XOR => T_LOGICAL_XOR,
+ T_MATCH => T_MATCH,
+ T_NAMESPACE => T_NAMESPACE,
+ T_NEW => T_NEW,
+ T_PRINT => T_PRINT,
+ T_PRIVATE => T_PRIVATE,
+ T_PROTECTED => T_PROTECTED,
+ T_PUBLIC => T_PUBLIC,
+ T_READONLY => T_READONLY,
+ T_REQUIRE => T_REQUIRE,
+ T_REQUIRE_ONCE => T_REQUIRE_ONCE,
+ T_RETURN => T_RETURN,
+ T_STATIC => T_STATIC,
+ T_SWITCH => T_SWITCH,
+ T_THROW => T_THROW,
+ T_TRAIT => T_TRAIT,
+ T_TRY => T_TRY,
+ T_USE => T_USE,
+ T_VAR => T_VAR,
+ T_WHILE => T_WHILE,
+ T_YIELD => T_YIELD,
+ T_YIELD_FROM => T_YIELD_FROM,
+ ];
+
/**
* Given a token, returns the name of the token.
diff --git a/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc b/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc
new file mode 100644
index 0000000000..9506a35c6e
--- /dev/null
+++ b/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.inc
@@ -0,0 +1,208 @@
+ 'a',
+ 2 => 'b',
+ /* testMatchDefaultIsKeyword */ default => 'default',
+};
+
+$closure = /* testFnIsKeyword */ fn () => 'string';
+
+function () {
+ /* testYieldIsKeyword */ yield $f;
+ /* testYieldFromIsKeyword */ yield from someFunction();
+};
+
+/* testDeclareIsKeyword */ declare(ticks=1):
+/* testEndDeclareIsKeyword */ enddeclare;
+
+if (true /* testAndIsKeyword */ and false /* testOrIsKeyword */ or null /* testXorIsKeyword */ xor 0) {
+
+}
+
+$anonymousClass = new /* testAnonymousClassIsKeyword */ class {};
+$anonymousClass2 = new class /* testExtendsInAnonymousClassIsKeyword */ extends SomeParent {};
+$anonymousClass3 = new class /* testImplementsInAnonymousClassIsKeyword */ implements SomeInterface {};
+
+class Foo extends /* testNamespaceInNameIsKeyword */ namespace\Exception
+{}
diff --git a/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php b/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php
new file mode 100644
index 0000000000..4c200fbc30
--- /dev/null
+++ b/tests/Core/Tokenizer/ContextSensitiveKeywordsTest.php
@@ -0,0 +1,469 @@
+
+ * @copyright 2020 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 ContextSensitiveKeywordsTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Test that context sensitive keyword is tokenized as string when it should be string.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ *
+ * @dataProvider dataStrings
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testStrings($testMarker)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $token = $this->getTargetToken($testMarker, (Tokens::$contextSensitiveKeywords + [T_STRING]));
+
+ $this->assertSame(T_STRING, $tokens[$token]['code']);
+ $this->assertSame('T_STRING', $tokens[$token]['type']);
+
+ }//end testStrings()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testStrings()
+ *
+ * @return array
+ */
+ public function dataStrings()
+ {
+ return [
+ ['/* testAbstract */'],
+ ['/* testArray */'],
+ ['/* testAs */'],
+ ['/* testBreak */'],
+ ['/* testCallable */'],
+ ['/* testCase */'],
+ ['/* testCatch */'],
+ ['/* testClass */'],
+ ['/* testClone */'],
+ ['/* testConst */'],
+ ['/* testContinue */'],
+ ['/* testDeclare */'],
+ ['/* testDefault */'],
+ ['/* testDo */'],
+ ['/* testEcho */'],
+ ['/* testElse */'],
+ ['/* testElseIf */'],
+ ['/* testEndDeclare */'],
+ ['/* testEndFor */'],
+ ['/* testEndForeach */'],
+ ['/* testEndIf */'],
+ ['/* testEndSwitch */'],
+ ['/* testEndWhile */'],
+ ['/* testExit */'],
+ ['/* testExtends */'],
+ ['/* testFinal */'],
+ ['/* testFinally */'],
+ ['/* testFn */'],
+ ['/* testFor */'],
+ ['/* testForeach */'],
+ ['/* testFunction */'],
+ ['/* testGlobal */'],
+ ['/* testGoto */'],
+ ['/* testIf */'],
+ ['/* testImplements */'],
+ ['/* testInclude */'],
+ ['/* testIncludeOnce */'],
+ ['/* testInstanceOf */'],
+ ['/* testInsteadOf */'],
+ ['/* testInterface */'],
+ ['/* testList */'],
+ ['/* testMatch */'],
+ ['/* testNamespace */'],
+ ['/* testNew */'],
+ ['/* testParent */'],
+ ['/* testPrint */'],
+ ['/* testPrivate */'],
+ ['/* testProtected */'],
+ ['/* testPublic */'],
+ ['/* testReadonly */'],
+ ['/* testRequire */'],
+ ['/* testRequireOnce */'],
+ ['/* testReturn */'],
+ ['/* testSelf */'],
+ ['/* testStatic */'],
+ ['/* testSwitch */'],
+ ['/* testThrows */'],
+ ['/* testTrait */'],
+ ['/* testTry */'],
+ ['/* testUse */'],
+ ['/* testVar */'],
+ ['/* testWhile */'],
+ ['/* testYield */'],
+ ['/* testYieldFrom */'],
+ ['/* testAnd */'],
+ ['/* testOr */'],
+ ['/* testXor */'],
+
+ ['/* testKeywordAfterNamespaceShouldBeString */'],
+ ['/* testNamespaceNameIsString1 */'],
+ ['/* testNamespaceNameIsString2 */'],
+ ['/* testNamespaceNameIsString3 */'],
+ ];
+
+ }//end dataStrings()
+
+
+ /**
+ * Test that context sensitive keyword is tokenized as keyword when it should be keyword.
+ *
+ * @param string $testMarker The comment which prefaces the target token in the test file.
+ * @param string $expectedTokenType The expected token type.
+ *
+ * @dataProvider dataKeywords
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testKeywords($testMarker, $expectedTokenType)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $token = $this->getTargetToken($testMarker, (Tokens::$contextSensitiveKeywords + [T_ANON_CLASS, T_MATCH_DEFAULT, T_PARENT, T_SELF, T_STRING]));
+
+ $this->assertSame(constant($expectedTokenType), $tokens[$token]['code']);
+ $this->assertSame($expectedTokenType, $tokens[$token]['type']);
+
+ }//end testKeywords()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testKeywords()
+ *
+ * @return array
+ */
+ public function dataKeywords()
+ {
+ return [
+ [
+ '/* testNamespaceIsKeyword */',
+ 'T_NAMESPACE',
+ ],
+ [
+ '/* testAbstractIsKeyword */',
+ 'T_ABSTRACT',
+ ],
+ [
+ '/* testClassIsKeyword */',
+ 'T_CLASS',
+ ],
+ [
+ '/* testExtendsIsKeyword */',
+ 'T_EXTENDS',
+ ],
+ [
+ '/* testImplementsIsKeyword */',
+ 'T_IMPLEMENTS',
+ ],
+ [
+ '/* testUseIsKeyword */',
+ 'T_USE',
+ ],
+ [
+ '/* testInsteadOfIsKeyword */',
+ 'T_INSTEADOF',
+ ],
+ [
+ '/* testAsIsKeyword */',
+ 'T_AS',
+ ],
+ [
+ '/* testConstIsKeyword */',
+ 'T_CONST',
+ ],
+ [
+ '/* testPrivateIsKeyword */',
+ 'T_PRIVATE',
+ ],
+ [
+ '/* testProtectedIsKeyword */',
+ 'T_PROTECTED',
+ ],
+ [
+ '/* testPublicIsKeyword */',
+ 'T_PUBLIC',
+ ],
+ [
+ '/* testVarIsKeyword */',
+ 'T_VAR',
+ ],
+ [
+ '/* testStaticIsKeyword */',
+ 'T_STATIC',
+ ],
+ [
+ '/* testReadonlyIsKeyword */',
+ 'T_READONLY',
+ ],
+ [
+ '/* testFinalIsKeyword */',
+ 'T_FINAL',
+ ],
+ [
+ '/* testFunctionIsKeyword */',
+ 'T_FUNCTION',
+ ],
+ [
+ '/* testCallableIsKeyword */',
+ 'T_CALLABLE',
+ ],
+ [
+ '/* testSelfIsKeyword */',
+ 'T_SELF',
+ ],
+ [
+ '/* testParentIsKeyword */',
+ 'T_PARENT',
+ ],
+ [
+ '/* testReturnIsKeyword */',
+ 'T_RETURN',
+ ],
+
+ [
+ '/* testInterfaceIsKeyword */',
+ 'T_INTERFACE',
+ ],
+ [
+ '/* testTraitIsKeyword */',
+ 'T_TRAIT',
+ ],
+
+ [
+ '/* testNewIsKeyword */',
+ 'T_NEW',
+ ],
+ [
+ '/* testInstanceOfIsKeyword */',
+ 'T_INSTANCEOF',
+ ],
+ [
+ '/* testCloneIsKeyword */',
+ 'T_CLONE',
+ ],
+
+ [
+ '/* testIfIsKeyword */',
+ 'T_IF',
+ ],
+ [
+ '/* testElseIfIsKeyword */',
+ 'T_ELSEIF',
+ ],
+ [
+ '/* testElseIsKeyword */',
+ 'T_ELSE',
+ ],
+ [
+ '/* testEndIfIsKeyword */',
+ 'T_ENDIF',
+ ],
+
+ [
+ '/* testForIsKeyword */',
+ 'T_FOR',
+ ],
+ [
+ '/* testEndForIsKeyword */',
+ 'T_ENDFOR',
+ ],
+
+ [
+ '/* testForeachIsKeyword */',
+ 'T_FOREACH',
+ ],
+ [
+ '/* testEndForeachIsKeyword */',
+ 'T_ENDFOREACH',
+ ],
+
+ [
+ '/* testSwitchIsKeyword */',
+ 'T_SWITCH',
+ ],
+ [
+ '/* testCaseIsKeyword */',
+ 'T_CASE',
+ ],
+ [
+ '/* testDefaultIsKeyword */',
+ 'T_DEFAULT',
+ ],
+ [
+ '/* testEndSwitchIsKeyword */',
+ 'T_ENDSWITCH',
+ ],
+ [
+ '/* testBreakIsKeyword */',
+ 'T_BREAK',
+ ],
+ [
+ '/* testContinueIsKeyword */',
+ 'T_CONTINUE',
+ ],
+
+ [
+ '/* testDoIsKeyword */',
+ 'T_DO',
+ ],
+ [
+ '/* testWhileIsKeyword */',
+ 'T_WHILE',
+ ],
+ [
+ '/* testEndWhileIsKeyword */',
+ 'T_ENDWHILE',
+ ],
+
+ [
+ '/* testTryIsKeyword */',
+ 'T_TRY',
+ ],
+ [
+ '/* testThrowIsKeyword */',
+ 'T_THROW',
+ ],
+ [
+ '/* testCatchIsKeyword */',
+ 'T_CATCH',
+ ],
+ [
+ '/* testFinallyIsKeyword */',
+ 'T_FINALLY',
+ ],
+
+ [
+ '/* testGlobalIsKeyword */',
+ 'T_GLOBAL',
+ ],
+ [
+ '/* testEchoIsKeyword */',
+ 'T_ECHO',
+ ],
+ [
+ '/* testPrintIsKeyword */',
+ 'T_PRINT',
+ ],
+ [
+ '/* testDieIsKeyword */',
+ 'T_EXIT',
+ ],
+ [
+ '/* testExitIsKeyword */',
+ 'T_EXIT',
+ ],
+
+ [
+ '/* testIncludeIsKeyword */',
+ 'T_INCLUDE',
+ ],
+ [
+ '/* testIncludeOnceIsKeyword */',
+ 'T_INCLUDE_ONCE',
+ ],
+ [
+ '/* testRequireIsKeyword */',
+ 'T_REQUIRE',
+ ],
+ [
+ '/* testRequireOnceIsKeyword */',
+ 'T_REQUIRE_ONCE',
+ ],
+
+ [
+ '/* testListIsKeyword */',
+ 'T_LIST',
+ ],
+ [
+ '/* testGotoIsKeyword */',
+ 'T_GOTO',
+ ],
+ [
+ '/* testMatchIsKeyword */',
+ 'T_MATCH',
+ ],
+ [
+ '/* testMatchDefaultIsKeyword */',
+ 'T_MATCH_DEFAULT',
+ ],
+ [
+ '/* testFnIsKeyword */',
+ 'T_FN',
+ ],
+
+ [
+ '/* testYieldIsKeyword */',
+ 'T_YIELD',
+ ],
+ [
+ '/* testYieldFromIsKeyword */',
+ 'T_YIELD_FROM',
+ ],
+
+ [
+ '/* testDeclareIsKeyword */',
+ 'T_DECLARE',
+ ],
+ [
+ '/* testEndDeclareIsKeyword */',
+ 'T_ENDDECLARE',
+ ],
+
+ [
+ '/* testAndIsKeyword */',
+ 'T_LOGICAL_AND',
+ ],
+ [
+ '/* testOrIsKeyword */',
+ 'T_LOGICAL_OR',
+ ],
+ [
+ '/* testXorIsKeyword */',
+ 'T_LOGICAL_XOR',
+ ],
+
+ [
+ '/* testAnonymousClassIsKeyword */',
+ 'T_ANON_CLASS',
+ ],
+ [
+ '/* testExtendsInAnonymousClassIsKeyword */',
+ 'T_EXTENDS',
+ ],
+ [
+ '/* testImplementsInAnonymousClassIsKeyword */',
+ 'T_IMPLEMENTS',
+ ],
+ [
+ '/* testNamespaceInNameIsKeyword */',
+ 'T_NAMESPACE',
+ ],
+ ];
+
+ }//end dataKeywords()
+
+
+}//end class