Skip to content

Commit 118891d

Browse files
committed
Tokenizer/PHP: stabilize T_FINALLY backfill
Make the backfill for T_FINALLY a little more stable by preventing re-tokenizing non-keyword `finally` strings to `T_FINALLY`. Includes unit tests.
1 parent fde816c commit 118891d

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed

package.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
137137
<file baseinstalldir="" name="DefaultKeywordTest.php" role="test" />
138138
<file baseinstalldir="" name="DoubleArrowTest.inc" role="test" />
139139
<file baseinstalldir="" name="DoubleArrowTest.php" role="test" />
140+
<file baseinstalldir="" name="FinallyTest.inc" role="test" />
141+
<file baseinstalldir="" name="FinallyTest.php" role="test" />
140142
<file baseinstalldir="" name="GotoLabelTest.inc" role="test" />
141143
<file baseinstalldir="" name="GotoLabelTest.php" role="test" />
142144
<file baseinstalldir="" name="NamedFunctionCallArgumentsTest.inc" role="test" />
@@ -2054,6 +2056,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
20542056
<install as="CodeSniffer/Core/Tokenizer/DefaultKeywordTest.inc" name="tests/Core/Tokenizer/DefaultKeywordTest.inc" />
20552057
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.php" name="tests/Core/Tokenizer/DoubleArrowTest.php" />
20562058
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.inc" name="tests/Core/Tokenizer/DoubleArrowTest.inc" />
2059+
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.php" name="tests/Core/Tokenizer/FinallyTest.php" />
2060+
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.inc" name="tests/Core/Tokenizer/FinallyTest.inc" />
20572061
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />
20582062
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.inc" name="tests/Core/Tokenizer/GotoLabelTest.inc" />
20592063
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" />
@@ -2140,6 +2144,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
21402144
<install as="CodeSniffer/Core/Tokenizer/DefaultKeywordTest.inc" name="tests/Core/Tokenizer/DefaultKeywordTest.inc" />
21412145
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.php" name="tests/Core/Tokenizer/DoubleArrowTest.php" />
21422146
<install as="CodeSniffer/Core/Tokenizer/DoubleArrowTest.inc" name="tests/Core/Tokenizer/DoubleArrowTest.inc" />
2147+
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.php" name="tests/Core/Tokenizer/FinallyTest.php" />
2148+
<install as="CodeSniffer/Core/Tokenizer/FinallyTest.inc" name="tests/Core/Tokenizer/FinallyTest.inc" />
21432149
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />
21442150
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.inc" name="tests/Core/Tokenizer/GotoLabelTest.inc" />
21452151
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" />

src/Tokenizers/PHP.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,6 +2084,7 @@ function return types. We want to keep the parenthesis map clean,
20842084
// where "finally" should be T_FINALLY instead of T_STRING.
20852085
if ($newToken['code'] === T_STRING
20862086
&& strtolower($newToken['content']) === 'finally'
2087+
&& $finalTokens[$lastNotEmptyToken]['code'] === T_CLOSE_CURLY_BRACKET
20872088
) {
20882089
$newToken['code'] = T_FINALLY;
20892090
$newToken['type'] = 'T_FINALLY';

tests/Core/Tokenizer/FinallyTest.inc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
try {
4+
// Do something.
5+
} catch(Exception $e) {
6+
// Do something.
7+
}
8+
/* testTryCatchFinally */
9+
finally {
10+
// Do something.
11+
}
12+
13+
/* testTryFinallyCatch */
14+
try {
15+
// Do something.
16+
} finally {
17+
// Do something.
18+
} catch(Exception $e) {
19+
// Do something.
20+
}
21+
22+
/* testTryFinally */
23+
try {
24+
// Do something.
25+
} FINALLY {
26+
// Do something.
27+
}
28+
29+
class FinallyAsMethod {
30+
/* testFinallyUsedAsClassConstantName */
31+
const FINALLY = 'foo';
32+
33+
/* testFinallyUsedAsMethodName */
34+
public function finally() {
35+
// Do something.
36+
37+
/* testFinallyUsedAsPropertyName */
38+
$this->finally = 'foo';
39+
}
40+
}

tests/Core/Tokenizer/FinallyTest.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
/**
3+
* Tests the tokenization of the finally keyword.
4+
*
5+
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
6+
* @copyright 2021 Squiz Pty Ltd (ABN 77 084 670 600)
7+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
11+
12+
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
13+
14+
class FinallyTest extends AbstractMethodUnitTest
15+
{
16+
17+
18+
/**
19+
* Test that the finally keyword is tokenized as such.
20+
*
21+
* @param string $testMarker The comment which prefaces the target token in the test file.
22+
*
23+
* @dataProvider dataFinallyKeyword
24+
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
25+
*
26+
* @return void
27+
*/
28+
public function testFinallyKeyword($testMarker)
29+
{
30+
$tokens = self::$phpcsFile->getTokens();
31+
32+
$target = $this->getTargetToken($testMarker, [T_FINALLY, T_STRING]);
33+
$this->assertSame(T_FINALLY, $tokens[$target]['code']);
34+
$this->assertSame('T_FINALLY', $tokens[$target]['type']);
35+
36+
}//end testFinallyKeyword()
37+
38+
39+
/**
40+
* Data provider.
41+
*
42+
* @see testFinallyKeyword()
43+
*
44+
* @return array
45+
*/
46+
public function dataFinallyKeyword()
47+
{
48+
return [
49+
['/* testTryCatchFinally */'],
50+
['/* testTryFinallyCatch */'],
51+
['/* testTryFinally */'],
52+
];
53+
54+
}//end dataFinallyKeyword()
55+
56+
57+
/**
58+
* Test that 'finally' when not used as the reserved keyword is tokenized as `T_STRING`.
59+
*
60+
* @param string $testMarker The comment which prefaces the target token in the test file.
61+
*
62+
* @dataProvider dataFinallyNonKeyword
63+
* @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
64+
*
65+
* @return void
66+
*/
67+
public function testFinallyNonKeyword($testMarker)
68+
{
69+
$tokens = self::$phpcsFile->getTokens();
70+
71+
$target = $this->getTargetToken($testMarker, [T_FINALLY, T_STRING]);
72+
$this->assertSame(T_STRING, $tokens[$target]['code']);
73+
$this->assertSame('T_STRING', $tokens[$target]['type']);
74+
75+
}//end testFinallyNonKeyword()
76+
77+
78+
/**
79+
* Data provider.
80+
*
81+
* @see testFinallyNonKeyword()
82+
*
83+
* @return array
84+
*/
85+
public function dataFinallyNonKeyword()
86+
{
87+
return [
88+
['/* testFinallyUsedAsClassConstantName */'],
89+
['/* testFinallyUsedAsMethodName */'],
90+
['/* testFinallyUsedAsPropertyName */'],
91+
];
92+
93+
}//end dataFinallyNonKeyword()
94+
95+
96+
}//end class

0 commit comments

Comments
 (0)