Skip to content

Commit 9c961f7

Browse files
committed
Tests/Tokenizer/DNFTypesTest: split the test class
... into two test classes, one targetting the `Tokenizer\PHP` class, one targetting the `Tokenizer\Tokenizer` class. While this does mean there is now some duplication between these test classes, I don't think that's problematic. It also allows for these tests to diverge based on the specific test needs for each of the classes under test.
1 parent 841f909 commit 9c961f7

File tree

4 files changed

+567
-58
lines changed

4 files changed

+567
-58
lines changed

tests/Core/Tokenizer/DNFTypesTest.php renamed to tests/Core/Tokenizer/PHP/DNFTypesTest.php

Lines changed: 7 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
88
*/
99

10-
namespace PHP_CodeSniffer\Tests\Core\Tokenizer;
10+
namespace PHP_CodeSniffer\Tests\Core\Tokenizer\PHP;
1111

12+
use PHP_CodeSniffer\Tests\Core\Tokenizer\AbstractTokenizerTestCase;
1213
use PHP_CodeSniffer\Util\Tokens;
1314

1415
final class DNFTypesTest extends AbstractTokenizerTestCase
@@ -18,17 +19,16 @@ final class DNFTypesTest extends AbstractTokenizerTestCase
1819
/**
1920
* Test that parentheses when **not** used in a type declaration are correctly tokenized.
2021
*
21-
* @param string $testMarker The comment prefacing the target token.
22-
* @param int|false $owner Optional. The parentheses owner or false when no parentheses owner is expected.
23-
* @param bool $skipCheckInside Optional. Skip checking correct token type inside the parentheses.
24-
* Use judiciously for combined normal + DNF tests only.
22+
* @param string $testMarker The comment prefacing the target token.
23+
* @param bool $skipCheckInside Optional. Skip checking correct token type inside the parentheses.
24+
* Use judiciously for combined normal + DNF tests only.
2525
*
2626
* @dataProvider dataNormalParentheses
27-
* @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createParenthesisNestingMap
27+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
2828
*
2929
* @return void
3030
*/
31-
public function testNormalParentheses($testMarker, $owner=false, $skipCheckInside=false)
31+
public function testNormalParentheses($testMarker, $skipCheckInside=false)
3232
{
3333
$tokens = $this->phpcsFile->getTokens();
3434

@@ -39,40 +39,14 @@ public function testNormalParentheses($testMarker, $owner=false, $skipCheckInsid
3939
$this->assertSame(T_OPEN_PARENTHESIS, $opener['code'], 'Token tokenized as '.$opener['type'].', not T_OPEN_PARENTHESIS (code)');
4040
$this->assertSame('T_OPEN_PARENTHESIS', $opener['type'], 'Token tokenized as '.$opener['type'].', not T_OPEN_PARENTHESIS (type)');
4141

42-
if ($owner !== false) {
43-
$this->assertArrayHasKey('parenthesis_owner', $opener, 'Parenthesis owner is not set');
44-
$this->assertSame(($openPtr + $owner), $opener['parenthesis_owner'], 'Opener parenthesis owner is not the expected token');
45-
} else {
46-
$this->assertArrayNotHasKey('parenthesis_owner', $opener, 'Parenthesis owner is set');
47-
}
48-
49-
$this->assertArrayHasKey('parenthesis_opener', $opener, 'Parenthesis opener is not set');
50-
$this->assertArrayHasKey('parenthesis_closer', $opener, 'Parenthesis closer is not set');
51-
$this->assertSame($openPtr, $opener['parenthesis_opener'], 'Parenthesis opener is not the expected token');
52-
5342
$closePtr = $opener['parenthesis_closer'];
5443
$closer = $tokens[$closePtr];
5544

5645
$this->assertSame(')', $closer['content'], 'Content of type close parenthesis is not ")"');
5746
$this->assertSame(T_CLOSE_PARENTHESIS, $closer['code'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (code)');
5847
$this->assertSame('T_CLOSE_PARENTHESIS', $closer['type'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (type)');
5948

60-
if ($owner !== false) {
61-
$this->assertArrayHasKey('parenthesis_owner', $closer, 'Parenthesis owner is not set');
62-
$this->assertSame(($openPtr + $owner), $closer['parenthesis_owner'], 'Closer parenthesis owner is not the expected token');
63-
} else {
64-
$this->assertArrayNotHasKey('parenthesis_owner', $closer, 'Parenthesis owner is set');
65-
}
66-
67-
$this->assertArrayHasKey('parenthesis_opener', $closer, 'Parenthesis opener is not set');
68-
$this->assertArrayHasKey('parenthesis_closer', $closer, 'Parenthesis closer is not set');
69-
$this->assertSame($closePtr, $closer['parenthesis_closer'], 'Parenthesis closer is not the expected token');
70-
7149
for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
72-
$this->assertArrayHasKey('nested_parenthesis', $tokens[$i], "Nested parenthesis key not set on token $i ({$tokens[$i]['type']})");
73-
$this->assertArrayHasKey($openPtr, $tokens[$i]['nested_parenthesis'], 'Nested parenthesis is missing target parentheses set');
74-
$this->assertSame($closePtr, $tokens[$i]['nested_parenthesis'][$openPtr], 'Nested parenthesis closer not set correctly');
75-
7650
// If there are ampersands, make sure these are tokenized as bitwise and.
7751
if ($skipCheckInside === false && $tokens[$i]['content'] === '&') {
7852
$this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (code)');
@@ -133,44 +107,36 @@ public static function dataNormalParentheses()
133107
],
134108
'parens with owner: function; & in default value' => [
135109
'testMarker' => '/* testParensOwnerFunctionAmpersandInDefaultValue */',
136-
'owner' => -3,
137110
],
138111
'parens with owner: closure; param declared by & ref' => [
139112
'testMarker' => '/* testParensOwnerClosureAmpersandParamRef */',
140-
'owner' => -1,
141113
],
142114
'parens with owner: if' => [
143115
'testMarker' => '/* testParensOwnerIf */',
144-
'owner' => -2,
145116
],
146117
'parens without owner in if condition' => [
147118
'testMarker' => '/* testParensNoOwnerInIfCondition */',
148119
],
149120
'parens with owner: for' => [
150121
'testMarker' => '/* testParensOwnerFor */',
151-
'owner' => -2,
152122
],
153123
'parens without owner in for condition' => [
154124
'testMarker' => '/* testParensNoOwnerInForCondition */',
155125
],
156126
'parens with owner: match' => [
157127
'testMarker' => '/* testParensOwnerMatch */',
158-
'owner' => -1,
159128
],
160129
'parens with owner: array' => [
161130
'testMarker' => '/* testParensOwnerArray */',
162-
'owner' => -2,
163131
],
164132
'parens without owner in array; function call with & in callable' => [
165133
'testMarker' => '/* testParensNoOwnerFunctionCallWithAmpersandInCallable */',
166134
],
167135
'parens with owner: fn; & in return value' => [
168136
'testMarker' => '/* testParensOwnerArrowFn */',
169-
'owner' => -1,
170137
],
171138
'parens with owner: list with reference vars' => [
172139
'testMarker' => '/* testParensOwnerListWithRefVars */',
173-
'owner' => -1,
174140
],
175141
'parens without owner, function call with DNF look-a-like param' => [
176142
'testMarker' => '/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */',
@@ -199,11 +165,9 @@ public static function dataNormalParentheses()
199165
],
200166
'parens with owner: closure; & in default value' => [
201167
'testMarker' => '/* testParensOwnerClosureAmpersandInDefaultValue */',
202-
'owner' => -2,
203168
],
204169
'parens with owner: fn; dnf used within' => [
205170
'testMarker' => '/* testParensOwnerArrowDNFUsedWithin */',
206-
'owner' => -2,
207171
'skipCheckInside' => true,
208172
],
209173
'parens without owner: default value for param in arrow function' => [
@@ -228,7 +192,6 @@ public static function dataNormalParentheses()
228192
*
229193
* @dataProvider dataDNFTypeParentheses
230194
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
231-
* @covers PHP_CodeSniffer\Tokenizers\Tokenizer::createParenthesisNestingMap
232195
*
233196
* @return void
234197
*/
@@ -243,29 +206,15 @@ public function testDNFTypeParentheses($testMarker)
243206
$this->assertSame(T_TYPE_OPEN_PARENTHESIS, $opener['code'], 'Token tokenized as '.$opener['type'].', not T_TYPE_OPEN_PARENTHESIS (code)');
244207
$this->assertSame('T_TYPE_OPEN_PARENTHESIS', $opener['type'], 'Token tokenized as '.$opener['type'].', not T_TYPE_OPEN_PARENTHESIS (type)');
245208

246-
$this->assertArrayNotHasKey('parenthesis_owner', $opener, 'Parenthesis owner is set');
247-
$this->assertArrayHasKey('parenthesis_opener', $opener, 'Parenthesis opener is not set');
248-
$this->assertArrayHasKey('parenthesis_closer', $opener, 'Parenthesis closer is not set');
249-
$this->assertSame($openPtr, $opener['parenthesis_opener'], 'Parenthesis opener is not the expected token');
250-
251209
$closePtr = $opener['parenthesis_closer'];
252210
$closer = $tokens[$closePtr];
253211

254212
$this->assertSame(')', $closer['content'], 'Content of type close parenthesis is not ")"');
255213
$this->assertSame(T_TYPE_CLOSE_PARENTHESIS, $closer['code'], 'Token tokenized as '.$closer['type'].', not T_TYPE_CLOSE_PARENTHESIS (code)');
256214
$this->assertSame('T_TYPE_CLOSE_PARENTHESIS', $closer['type'], 'Token tokenized as '.$closer['type'].', not T_TYPE_CLOSE_PARENTHESIS (type)');
257215

258-
$this->assertArrayNotHasKey('parenthesis_owner', $closer, 'Parenthesis owner is set');
259-
$this->assertArrayHasKey('parenthesis_opener', $closer, 'Parenthesis opener is not set');
260-
$this->assertArrayHasKey('parenthesis_closer', $closer, 'Parenthesis closer is not set');
261-
$this->assertSame($closePtr, $closer['parenthesis_closer'], 'Parenthesis closer is not the expected token');
262-
263216
$intersectionCount = 0;
264217
for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
265-
$this->assertArrayHasKey('nested_parenthesis', $tokens[$i], "Nested parenthesis key not set on token $i ({$tokens[$i]['type']})");
266-
$this->assertArrayHasKey($openPtr, $tokens[$i]['nested_parenthesis'], 'Nested parenthesis is missing target parentheses set');
267-
$this->assertSame($closePtr, $tokens[$i]['nested_parenthesis'][$openPtr], 'Nested parenthesis closer not set correctly');
268-
269218
if ($tokens[$i]['content'] === '&') {
270219
$this->assertSame(
271220
T_TYPE_INTERSECTION,
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?php
2+
3+
/*
4+
* Non-exhaustive code samples of "normal" parentheses.
5+
*/
6+
7+
/* testParensNoOwner */
8+
$a = ( CONST_A & CONST_B ) | CONST_C === false;
9+
10+
/* testParensNoOwnerInTernary */
11+
$a = $var ? $something : CONST_C | ( CONST_A & CONST_B );
12+
13+
/* testParensNoOwnerInShortTernary */
14+
$a = $var ?: ( CONST_A & CONST_B );
15+
16+
/* testParensOwnerFunctionAmpersandInDefaultValue */
17+
function defaultValueLooksLikeDNF( mixed $param = (CONST_A&CONST_B) ) {}
18+
19+
/* testParensOwnerClosureAmpersandParamRef */
20+
$closureWithParamRef = function(&$param) {};
21+
22+
/* testParensOwnerIf */
23+
if ( /* testParensNoOwnerInIfCondition */ CONST_C | ( CONST_A & /*comment*/ CONST_B ) > 10 ) {}
24+
25+
/* testParensOwnerFor */
26+
for ($i =0; $i < /* testParensNoOwnerInForCondition */ ( CONST_A & CONST_B ); $i++ );
27+
28+
/* testParensOwnerMatch */
29+
$match = match(CONST_A & CONST_B) {
30+
default => $a,
31+
};
32+
33+
/* testParensOwnerArray */
34+
$array = array (
35+
'text',
36+
\CONST_A & \Fully\Qualified\CONST_B,
37+
/* testParensNoOwnerFunctionCallWithAmpersandInCallable */
38+
do_something($a, /* testParensOwnerArrowFn */ fn($b) => $a & $b, $c),
39+
);
40+
41+
/* testParensOwnerListWithRefVars */
42+
list(&$a, &$b) = $array;
43+
44+
/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */
45+
$obj->static((CONST_A&CONST_B)|CONST_C | $var);
46+
47+
48+
/*
49+
* DNF parentheses.
50+
*/
51+
52+
class DNFTypes {
53+
/* testDNFTypeOOConstUnqualifiedClasses */
54+
public const (A&B)|D UNQUALIFIED = new Foo;
55+
56+
/* testDNFTypeOOConstReverseModifierOrder */
57+
protected final const int|(Foo&Bar)|float MODIFIERS_REVERSED /* testParensNoOwnerOOConstDefaultValue */ = (E_WARNING & E_NOTICE) | E_DEPRECATED;
58+
59+
const
60+
/* testDNFTypeOOConstMulti1 */
61+
(A&B) |
62+
/* testDNFTypeOOConstMulti2 */
63+
(C&D) | // phpcs:ignore Stnd.Cat.Sniff
64+
/* testDNFTypeOOConstMulti3 */
65+
(Y&D)
66+
| null MULTI_DNF = false;
67+
68+
/* testDNFTypeOOConstNamespaceRelative */
69+
final protected const (namespace\Sub\NameA&namespace\Sub\NameB)|namespace\Sub\NameC NAMESPACE_RELATIVE = new namespace\Sub\NameB;
70+
71+
/* testDNFTypeOOConstPartiallyQualified */
72+
const Partially\Qualified\NameC|(Partially\Qualified\NameA&Partially\Qualified\NameB) PARTIALLY_QUALIFIED = new Partially\Qualified\NameA;
73+
74+
/* testDNFTypeOOConstFullyQualified */
75+
const (\Fully\Qualified\NameA&\Fully\Qualified\NameB)|\Fully\Qualified\NameC FULLY_QUALIFIED = new \Fully\Qualified\NameB();
76+
77+
/* testDNFTypePropertyUnqualifiedClasses */
78+
public static (Foo&Bar)|object $obj;
79+
80+
/* testDNFTypePropertyReverseModifierOrder */
81+
static protected string|(A&B)|bool $dnf /* testParensNoOwnerPropertyDefaultValue1 */ = ( E_WARNING & E_NOTICE ) | /* testParensNoOwnerPropertyDefaultValue2 */ (E_ALL & E_DEPRECATED);
82+
83+
private
84+
/* testDNFTypePropertyMultiNamespaceRelative */
85+
(namespace\Sub\NameA&namespace\Sub\NameB) |
86+
/* testDNFTypePropertyMultiPartiallyQualified */
87+
(Partially\Qualified\NameA&Partially\Qualified\NameB) | // phpcs:ignore Stnd.Cat.Sniff
88+
false
89+
/* testDNFTypePropertyMultiFullyQualified */
90+
| (\Fully\Qualified\NameA&\Fully\Qualified\NameB) $multiDnf;
91+
92+
/* testDNFTypePropertyWithReadOnlyKeyword1 */
93+
protected readonly (A&B) | /* testDNFTypePropertyWithReadOnlyKeyword2 */ (C&D) $readonly;
94+
95+
/* testDNFTypePropertyWithStaticAndReadOnlyKeywords */
96+
static readonly (A&B&C)|array $staticReadonly;
97+
98+
/* testDNFTypePropertyWithOnlyStaticKeyword */
99+
static (A&B&C)|true $obj;
100+
101+
public function paramTypes(
102+
/* testDNFTypeParam1WithAttribute */
103+
#[MyAttribute]
104+
(\Foo&Bar)|int|float $paramA /* testParensNoOwnerParamDefaultValue */ = SOMETHING | (CONSTANT_A & CONSTANT_B),
105+
106+
/* testDNFTypeParam2 */
107+
(Foo&\Bar) /* testDNFTypeParam3 */ |(Baz&Fop) &...$paramB = null,
108+
) {
109+
/* testParensNoOwnerInReturnValue1 */
110+
return (
111+
/* testParensNoOwnerInReturnValue2 */
112+
($a1 & $b1) |
113+
/* testParensNoOwnerInReturnValue3 */
114+
($a2 & $b2)
115+
) + $c;
116+
}
117+
118+
public function identifierNames(
119+
/* testDNFTypeParamNamespaceRelative */
120+
(namespace\Sub\NameA&namespace\Sub\NameB)|false $paramA,
121+
/* testDNFTypeParamPartiallyQualified */
122+
Partially\Qualified\NameC|(Partially\Qualified\NameA&Partially\Qualified\NameB) $paramB,
123+
/* testDNFTypeParamFullyQualified */
124+
name|(\Fully\Qualified\NameA&\Fully\Qualified\NameB) $paramC,
125+
) {}
126+
127+
public function __construct(
128+
/* testDNFTypeConstructorPropertyPromotion1 */
129+
public (A&B)| /* testDNFTypeConstructorPropertyPromotion2 */ (A&D) $property
130+
) {}
131+
132+
public function returnType()/* testDNFTypeReturnType1 */ : A|(B&D)|/* testDNFTypeReturnType2 */(B&W)|null {}
133+
134+
abstract public function abstractMethod(): /* testDNFTypeAbstractMethodReturnType1 */ (X&Y) /* testDNFTypeAbstractMethodReturnType2 */ |(W&Z);
135+
136+
public function identifierNamesReturnRelative(
137+
) : /* testDNFTypeReturnTypeNamespaceRelative */ (namespace\Sub\NameA&namespace\Sub\NameB)|namespace\Sub\NameC {}
138+
139+
public function identifierNamesReturnPQ(
140+
) : /* testDNFTypeReturnPartiallyQualified */Partially\Qualified\NameA|(Partially\Qualified\NameB&Partially\Qualified\NameC) {}
141+
142+
// Illegal type: segments which are strict subsets of others are disallowed, but that's not the concern of the tokenizer.
143+
public function identifierNamesReturnFQ(
144+
) /* testDNFTypeReturnFullyQualified */ : (\Fully\Qualified\NameA&\Fully\Qualified\NameB)|\Fully\Qualified\NameB {}
145+
}
146+
147+
function globalFunctionWithSpreadAndReference(
148+
/* testDNFTypeWithReference */
149+
float|(B&A) &$paramA,
150+
/* testDNFTypeWithSpreadOperator */
151+
string|(B|D) ...$paramB
152+
) {}
153+
154+
155+
$closureWithParamType = function ( /* testDNFTypeClosureParamIllegalNullable */ ?(A&B)|bool $string) {};
156+
157+
/* testParensOwnerClosureAmpersandInDefaultValue */
158+
$closureWithReturnType = function ($string = NONSENSE & FAKE) /* testDNFTypeClosureReturn */ : (\Package\MyA&PackageB)|null {};
159+
160+
/* testParensOwnerArrowDNFUsedWithin */
161+
$arrowWithParamType = fn (
162+
/* testDNFTypeArrowParam */
163+
object|(A&B&C)|array $param,
164+
/* testParensNoOwnerAmpersandInDefaultValue */ ?int $int = (CONSTA & CONSTB )| CONST_C
165+
)
166+
/* testParensNoOwnerInArrowReturnExpression */
167+
=> ($param & $foo ) | $int;
168+
169+
$arrowWithReturnType = fn ($param) : /* testDNFTypeArrowReturnType */ int|(A&B) => $param * 10;
170+
171+
$arrowWithParamReturnByRef = fn &(
172+
/* testDNFTypeArrowParamWithReturnByRef */
173+
(A&B)|null $param
174+
) => $param * 10;
175+
176+
function InvalidSyntaxes(
177+
/* testDNFTypeParamIllegalUnnecessaryParens */
178+
(A&B) $parensNotNeeded
179+
180+
/* testDNFTypeParamIllegalIntersectUnionReversed */
181+
A&(B|D) $onlyIntersectAllowedWithinParensAndUnionOutside
182+
183+
/* testDNFTypeParamIllegalNestedParens */
184+
A|(B&(D|W)|null) $nestedParensNotAllowed
185+
) {}

0 commit comments

Comments
 (0)