Skip to content

Commit b1d8ff7

Browse files
authored
Merge pull request #632 from PHPCSStandards/feature/630-tokenizer-php-improve-fix-dnf-types
Tokenizer/PHP: fix handling of "DNF look a likes" with switch/case and alternative control structure syntax
2 parents 9e60f9f + 0c1f4d5 commit b1d8ff7

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

src/Tokenizers/PHP.php

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3226,19 +3226,45 @@ protected function processAdditional()
32263226
}
32273227

32283228
if ($suspectedType === 'return' && $this->tokens[$x]['code'] === T_COLON) {
3229-
// Make sure this is not the colon from a parameter name.
3229+
// Make sure this is the colon for a return type.
32303230
for ($y = ($x - 1); $y > 0; $y--) {
32313231
if (isset(Tokens::$emptyTokens[$this->tokens[$y]['code']]) === false) {
32323232
break;
32333233
}
32343234
}
32353235

3236-
if ($this->tokens[$y]['code'] !== T_PARAM_NAME) {
3237-
$confirmed = true;
3236+
if ($this->tokens[$y]['code'] !== T_CLOSE_PARENTHESIS) {
3237+
// Definitely not a union, intersection or DNF return type, move on.
3238+
continue 2;
3239+
}
3240+
3241+
if (isset($this->tokens[$y]['parenthesis_owner']) === true) {
3242+
if ($this->tokens[$this->tokens[$y]['parenthesis_owner']]['code'] === T_FUNCTION
3243+
|| $this->tokens[$this->tokens[$y]['parenthesis_owner']]['code'] === T_CLOSURE
3244+
|| $this->tokens[$this->tokens[$y]['parenthesis_owner']]['code'] === T_FN
3245+
) {
3246+
$confirmed = true;
3247+
}
3248+
3249+
break;
3250+
}
3251+
3252+
// Arrow functions may not have the parenthesis_owner set correctly yet.
3253+
// Closure use tokens won't be parentheses owners until PHPCS 4.0.
3254+
if (isset($this->tokens[$y]['parenthesis_opener']) === true) {
3255+
for ($z = ($this->tokens[$y]['parenthesis_opener'] - 1); $z > 0; $z--) {
3256+
if (isset(Tokens::$emptyTokens[$this->tokens[$z]['code']]) === false) {
3257+
break;
3258+
}
3259+
}
3260+
3261+
if ($this->tokens[$z]['code'] === T_FN || $this->tokens[$z]['code'] === T_USE) {
3262+
$confirmed = true;
3263+
}
32383264
}
32393265

32403266
break;
3241-
}
3267+
}//end if
32423268

32433269
if ($suspectedType === 'constant' && $this->tokens[$x]['code'] === T_CONST) {
32443270
$confirmed = true;

tests/Core/Tokenizer/PHP/DNFTypesTest.inc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,35 @@ callMe(label: CONST_A | CONST_B);
5353
/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamIntersect */
5454
callMe(label: CONST_A & CONST_B);
5555

56+
/* testSwitchControlStructureCondition */
57+
switch (CONST_A | CONST_B) {
58+
/* testFunctionCallInSwitchCaseCondition */
59+
case get_bool():
60+
/* testFunctionCallInSwitchCaseBody */
61+
\Name\functionInSwitch();
62+
break;
63+
64+
default:
65+
/* testFunctionCallInSwitchDefaultBody */
66+
functionInSwitch();
67+
break;
68+
}
69+
70+
/* testIfAlternativeSyntaxCondition */
71+
if (true):
72+
/* testFunctionCallInIfBody */
73+
\B\call();
74+
/* testElseIfAlternativeSyntaxCondition */
75+
elseif (10):
76+
/* testFunctionCallInElseIfBody */
77+
C\call();
78+
endif;
79+
80+
gotolabel:
81+
/* testFunctionCallInGotoBody */
82+
\doSomething();
83+
84+
5685
/*
5786
* DNF parentheses.
5887
*/
@@ -165,6 +194,8 @@ $closureWithParamType = function ( /* testDNFTypeClosureParamIllegalNullable */
165194
/* testParensOwnerClosureAmpersandInDefaultValue */
166195
$closureWithReturnType = function ($string = NONSENSE & FAKE) /* testDNFTypeClosureReturn */ : (\Package\MyA&PackageB)|null {};
167196

197+
$closureWithUseAndReturnType = function ($foo) use ($a) /* testDNFTypeClosureWithUseReturn */ : null|(Foo&\Bar)|false {};
198+
168199
/* testParensOwnerArrowDNFUsedWithin */
169200
$arrowWithParamType = fn (
170201
/* testDNFTypeArrowParam */

tests/Core/Tokenizer/PHP/DNFTypesTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,33 @@ public static function dataNormalParentheses()
193193
'parens without owner in arrow function return expression' => [
194194
'testMarker' => '/* testParensNoOwnerInArrowReturnExpression */',
195195
],
196+
'parens with owner: switch condition' => [
197+
'testMarker' => '/* testSwitchControlStructureCondition */',
198+
],
199+
'parens without owner in switch-case condition' => [
200+
'testMarker' => '/* testFunctionCallInSwitchCaseCondition */',
201+
],
202+
'parens without owner in switch-case body' => [
203+
'testMarker' => '/* testFunctionCallInSwitchCaseBody */',
204+
],
205+
'parens without owner in switch-default body' => [
206+
'testMarker' => '/* testFunctionCallInSwitchDefaultBody */',
207+
],
208+
'parens with owner: if condition, alternative syntax' => [
209+
'testMarker' => '/* testIfAlternativeSyntaxCondition */',
210+
],
211+
'parens without owner in if body, alternative syntax' => [
212+
'testMarker' => '/* testFunctionCallInIfBody */',
213+
],
214+
'parens with owner: elseif condition, alternative syntax' => [
215+
'testMarker' => '/* testElseIfAlternativeSyntaxCondition */',
216+
],
217+
'parens without owner in elseif body, alternative syntax' => [
218+
'testMarker' => '/* testFunctionCallInElseIfBody */',
219+
],
220+
'parens without owner in goto body' => [
221+
'testMarker' => '/* testFunctionCallInGotoBody */',
222+
],
196223
];
197224

198225
}//end dataNormalParentheses()
@@ -423,6 +450,10 @@ public static function dataDNFTypeParentheses()
423450
'closure return type' => [
424451
'testMarker' => '/* testDNFTypeClosureReturn */',
425452
],
453+
'closure with use return type' => [
454+
'testMarker' => '/* testDNFTypeClosureWithUseReturn */',
455+
],
456+
426457
'arrow function param type' => [
427458
'testMarker' => '/* testDNFTypeArrowParam */',
428459
],

0 commit comments

Comments
 (0)