Skip to content

Commit cac9038

Browse files
committed
Tokenizer/PHP: efficiency improvement for DNF type handling
The PHP::processAdditional()` method walks _back_ from the end of the file to the beginning. With that in mind, and knowing that a type can never end on an open parenthesis, and an open parenthesis can never be seen in a type before the close parenthesis has been seen (at least for valid/non-parse error types), it makes no sense to trigger the type handling logic for open parentheses. This should make the tokenizer slightly more efficient as (open) parentheses are used a lot in code ;-) It also prevents the type handling layer from acting on these type of invalid/parse error types, while it previously would. Includes tests safeguarding the behaviour of the type handling layer for this type of invalid/parse error types. Of these tests, the type for the OO constant and for the property were previously not handled consistently/correctly. The parameter type + the return type were fine.
1 parent 027c0cb commit cac9038

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

src/Tokenizers/PHP.php

-1
Original file line numberDiff line numberDiff line change
@@ -3036,7 +3036,6 @@ protected function processAdditional()
30363036
continue;
30373037
} else if ($this->tokens[$i]['code'] === T_BITWISE_OR
30383038
|| $this->tokens[$i]['code'] === T_BITWISE_AND
3039-
|| $this->tokens[$i]['code'] === T_OPEN_PARENTHESIS
30403039
|| $this->tokens[$i]['code'] === T_CLOSE_PARENTHESIS
30413040
) {
30423041
/*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
// Parentheses in broken DNF type declarations will remain tokenized as normal parentheses.
4+
// This test is in a separate file as the 'nested_parenthesis' indexes will be off after this code.
5+
class ParseErrors {
6+
/* testBrokenConstDNFTypeEndOnOpenParenthesis */
7+
const A|(B PARSE_ERROR = null;
8+
9+
/* testBrokenPropertyDNFTypeEndOnOpenParenthesis */
10+
public A|(B $parseError;
11+
12+
function unmatchedParens {
13+
/* testBrokenParamDNFTypeEndOnOpenParenthesis */
14+
A|(B $parseError,
15+
/* testBrokenReturnDNFTypeEndOnOpenParenthesis */
16+
) : A|(B {}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Tests that parentheses tokens are not converted to type parentheses tokens in broken DNF types.
4+
*
5+
* @author Juliette Reinders Folmer <[email protected]>
6+
* @copyright 2024 PHPCSStandards and contributors
7+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Tokenizer\PHP;
11+
12+
use PHP_CodeSniffer\Tests\Core\Tokenizer\AbstractTokenizerTestCase;
13+
14+
final class DNFTypesParseError1Test extends AbstractTokenizerTestCase
15+
{
16+
17+
18+
/**
19+
* Document handling for a DNF type / parse error where the last significant type specific token is an open parenthesis.
20+
*
21+
* @param string $testMarker The comment prefacing the target token.
22+
*
23+
* @dataProvider dataBrokenDNFTypeCantEndOnOpenParenthesis
24+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
25+
*
26+
* @return void
27+
*/
28+
public function testBrokenDNFTypeCantEndOnOpenParenthesis($testMarker)
29+
{
30+
$tokens = $this->phpcsFile->getTokens();
31+
32+
$openPtr = $this->getTargetToken($testMarker, [T_OPEN_PARENTHESIS, T_TYPE_OPEN_PARENTHESIS], '(');
33+
$token = $tokens[$openPtr];
34+
35+
// Verify that the open parenthesis is tokenized as a normal parenthesis.
36+
$this->assertSame(T_OPEN_PARENTHESIS, $token['code'], 'Token tokenized as '.$token['type'].', not T_OPEN_PARENTHESIS (code)');
37+
$this->assertSame('T_OPEN_PARENTHESIS', $token['type'], 'Token tokenized as '.$token['type'].', not T_OPEN_PARENTHESIS (type)');
38+
39+
// Verify that the type union is still tokenized as T_BITWISE_OR as the type declaration
40+
// is not recognized as a valid type declaration.
41+
$unionPtr = $this->getTargetToken($testMarker, [T_BITWISE_OR, T_TYPE_UNION], '|');
42+
$token = $tokens[$unionPtr];
43+
44+
$this->assertSame(T_BITWISE_OR, $token['code'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (code)');
45+
$this->assertSame('T_BITWISE_OR', $token['type'], 'Token tokenized as '.$token['type'].', not T_BITWISE_OR (type)');
46+
47+
}//end testBrokenDNFTypeCantEndOnOpenParenthesis()
48+
49+
50+
/**
51+
* Data provider.
52+
*
53+
* @see testBrokenDNFTypeCantEndOnOpenParenthesis()
54+
*
55+
* @return array<string, array<string, string>>
56+
*/
57+
public static function dataBrokenDNFTypeCantEndOnOpenParenthesis()
58+
{
59+
return [
60+
'OO const type' => ['/* testBrokenConstDNFTypeEndOnOpenParenthesis */'],
61+
'OO property type' => ['/* testBrokenPropertyDNFTypeEndOnOpenParenthesis */'],
62+
'Parameter type' => ['/* testBrokenParamDNFTypeEndOnOpenParenthesis */'],
63+
'Return type' => ['/* testBrokenReturnDNFTypeEndOnOpenParenthesis */'],
64+
];
65+
66+
}//end dataBrokenDNFTypeCantEndOnOpenParenthesis()
67+
68+
69+
}//end class

0 commit comments

Comments
 (0)