-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tokenizer/PHP: retokenize the match double arrow to T_MATCH_ARROW
The double arrow in PHP is used in a number of contexts: * Long/short arrays with keys; * Long/short lists with keys; * In the `as` part of `foreach()` statements with keys; * For `yield` statements with keys; * For arrow functions as the scope opener; * And now for `match` expressions to separate the cases from the return value (body). As most of these constructs can be nested in each other - an arrow function in an array value, a match expression in a list key -, every sniff handling any of these constructs has to take a lot of care when searching for the double arrow for the construct they are handling, to prevent matching a double arrow belonging to another type of construct nested in the target construct. This type of detection and special handling has to be done in each individual sniff which in one way or another has to deal with the `T_DOUBLE_ARROW` token and can cause quite some processing overhead. With that in mind, the double arrow as a scope opener for arrow functions has previously already been retokenized to `T_FN_ARROW`. Following the same reasoning, I'm proposing to retokenize the double arrow which separates `match` case expressions from the body expression to `T_MATCH_ARROW`. This should make life easier for any sniff dealing with any of the above constructs and will prevent potential false positives being introduced for sniffs currently handling any of these constructs, but not yet updated to allow for match expressions. Includes a set of dedicated unit tests verifying the tokenization of the double arrow operator in all currently supported contexts, including in combined (nested) contexts.
- Loading branch information
Showing
5 changed files
with
502 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
<?php | ||
|
||
function simpleLongArray($x) { | ||
return array( | ||
/* testLongArrayArrowSimple */ | ||
0 => 'Zero', | ||
); | ||
} | ||
|
||
function simpleShortArray($x) { | ||
return [ | ||
/* testShortArrayArrowSimple */ | ||
0 => 'Zero', | ||
]; | ||
} | ||
|
||
function simpleLongList($x) { | ||
list( | ||
/* testLongListArrowSimple */ | ||
0 => $a, | ||
) = $x; | ||
} | ||
|
||
function simpleShortList($x) { | ||
[ | ||
/* testShortListArrowSimple */ | ||
0 => $a, | ||
] = $x; | ||
} | ||
|
||
function simpleYield($x) { | ||
$i = 0; | ||
foreach (explode("\n", $x) as $line) { | ||
/* testYieldArrowSimple */ | ||
yield ++$i => $line; | ||
} | ||
} | ||
|
||
function simpleForeach($x) { | ||
/* testForeachArrowSimple */ | ||
foreach ($x as $k => $value) {} | ||
} | ||
|
||
function simpleMatch($x) { | ||
return match ($x) { | ||
/* testMatchArrowSimpleSingleCase */ | ||
0 => 'Zero', | ||
/* testMatchArrowSimpleMultiCase */ | ||
2, 4, 6 => 'Zero', | ||
/* testMatchArrowSimpleSingleCaseWithTrailingComma */ | ||
1, => 'Zero', | ||
/* testMatchArrowSimpleMultiCaseWithTrailingComma */ | ||
3, 5, => 'Zero', | ||
}; | ||
} | ||
|
||
function simpleArrowFunction($y) { | ||
/* testFnArrowSimple */ | ||
return fn ($y) => callMe($y); | ||
} | ||
|
||
function matchNestedInMatch() { | ||
$x = match ($y) { | ||
/* testMatchArrowNestedMatchOuter */ | ||
default, => match ($z) { | ||
/* testMatchArrowNestedMatchInner */ | ||
1 => 1 | ||
}, | ||
}; | ||
} | ||
|
||
function matchNestedInLongArrayValue() { | ||
$array = array( | ||
/* testLongArrayArrowWithNestedMatchValue1 */ | ||
'a' => match ($test) { | ||
/* testMatchArrowInLongArrayValue1 */ | ||
1 => 'a', | ||
/* testMatchArrowInLongArrayValue2 */ | ||
2 => 'b' | ||
}, | ||
/* testLongArrayArrowWithNestedMatchValue2 */ | ||
$i => match ($test) { | ||
/* testMatchArrowInLongArrayValue3 */ | ||
1 => 'a', | ||
}, | ||
); | ||
} | ||
|
||
function matchNestedInShortArrayValue() { | ||
$array = [ | ||
/* testShortArrayArrowWithNestedMatchValue1 */ | ||
'a' => match ($test) { | ||
/* testMatchArrowInShortArrayValue1 */ | ||
1 => 'a', | ||
/* testMatchArrowInShortArrayValue2 */ | ||
2 => 'b' | ||
}, | ||
/* testShortArrayArrowWithNestedMatchValue2 */ | ||
$i => match ($test) { | ||
/* testMatchArrowInShortArrayValue3 */ | ||
1 => 'a', | ||
}, | ||
]; | ||
} | ||
|
||
function matchNestedInLongArrayKey() { | ||
$array = array( | ||
match ($test) { /* testMatchArrowInLongArrayKey1 */ 1 => 'a', /* testMatchArrowInLongArrayKey2 */ 2 => 'b' } | ||
/* testLongArrayArrowWithMatchKey */ | ||
=> 'dynamic keys, woho!', | ||
); | ||
} | ||
|
||
function matchNestedInShortArrayKey() { | ||
$array = [ | ||
match ($test) { /* testMatchArrowInShortArrayKey1 */ 1 => 'a', /* testMatchArrowInShortArrayKey2 */ 2 => 'b' } | ||
/* testShortArrayArrowWithMatchKey */ | ||
=> 'dynamic keys, woho!', | ||
]; | ||
} | ||
|
||
function arraysNestedInMatch() { | ||
$matcher = match ($x) { | ||
/* testMatchArrowWithLongArrayBodyWithKeys */ | ||
0 => array( | ||
/* testLongArrayArrowInMatchBody1 */ | ||
0 => 1, | ||
/* testLongArrayArrowInMatchBody2 */ | ||
'a' => 2, | ||
/* testLongArrayArrowInMatchBody3 */ | ||
'b' => 3 | ||
), | ||
/* testMatchArrowWithShortArrayBodyWithoutKeys */ | ||
1 => [1, 2, 3], | ||
/* testMatchArrowWithLongArrayBodyWithoutKeys */ | ||
2 => array( 1, [1, 2, 3], 2, 3), | ||
/* testMatchArrowWithShortArrayBodyWithKeys */ | ||
3 => [ | ||
/* testShortArrayArrowInMatchBody1 */ | ||
0 => 1, | ||
/* testShortArrayArrowInMatchBody2 */ | ||
'a' => array(1, 2, 3), | ||
/* testShortArrayArrowInMatchBody3 */ | ||
'b' => 2, | ||
3 | ||
], | ||
/* testShortArrayArrowinMatchCase1 */ | ||
[4 => 'a', /* testShortArrayArrowinMatchCase2 */ 5 => 6] | ||
/* testMatchArrowWithShortArrayWithKeysAsCase */ | ||
=> 'match with array as case value', | ||
/* testShortArrayArrowinMatchCase3 */ | ||
[4 => 'a'], /* testLongArrayArrowinMatchCase4 */ array(5 => 6), | ||
/* testMatchArrowWithMultipleArraysWithKeysAsCase */ | ||
=> 'match with multiple arrays as case value', | ||
}; | ||
} | ||
|
||
function matchNestedInArrowFunction($x) { | ||
/* testFnArrowWithMatchInValue */ | ||
$fn = fn($x) => match(true) { | ||
/* testMatchArrowInFnBody1 */ | ||
1, 2, 3, 4, 5 => 'foo', | ||
/* testMatchArrowInFnBody2 */ | ||
default => 'bar', | ||
}; | ||
} | ||
|
||
function arrowFunctionsNestedInMatch($x) { | ||
return match ($x) { | ||
/* testMatchArrowWithFnBody1 */ | ||
1 => /* testFnArrowInMatchBody1 */ fn($y) => callMe($y), | ||
/* testMatchArrowWithFnBody2 */ | ||
default => /* testFnArrowInMatchBody2 */ fn($y) => callThem($y) | ||
}; | ||
} | ||
|
||
function matchShortArrayMismash() { | ||
$array = [ | ||
match ($test) { | ||
/* testMatchArrowInComplexShortArrayKey1 */ | ||
1 => [ /* testShortArrayArrowInComplexMatchValueinShortArrayKey */ 1 => 'a'], | ||
/* testMatchArrowInComplexShortArrayKey2 */ | ||
2 => 'b' | ||
/* testShortArrayArrowInComplexMatchArrayMismash */ | ||
} => match ($test) { | ||
/* testMatchArrowInComplexShortArrayValue1 */ | ||
1 => [ /* testShortArrayArrowInComplexMatchValueinShortArrayValue */ 1 => 'a'], | ||
/* testMatchArrowInComplexShortArrayValue1 */ | ||
2 => /* testFnArrowInComplexMatchValueInShortArrayValue */ fn($y) => callMe($y) | ||
}, | ||
]; | ||
} | ||
|
||
|
||
function longListInMatch($x, $y) { | ||
return match($x) { | ||
/* testMatchArrowWithLongListBody */ | ||
1 => list('a' => $a, /* testLongListArrowInMatchBody */ 'b' => $b, 'c' => list('d' => $c)) = $y, | ||
/* testLongListArrowInMatchCase */ | ||
list('a' => $a, 'b' => $b) = $y /* testMatchArrowWithLongListInCase */ => 'something' | ||
}; | ||
} | ||
|
||
function shortListInMatch($x, $y) { | ||
return match($x) { | ||
/* testMatchArrowWithShortListBody */ | ||
1 => ['a' => $a, 'b' => $b, 'c' => /* testShortListArrowInMatchBody */ ['d' => $c]] = $y, | ||
/* testShortListArrowInMatchCase */ | ||
['a' => $a, 'b' => $b] = $y /* testMatchArrowWithShortListInCase */ => 'something' | ||
}; | ||
} | ||
|
||
function matchInLongList() { | ||
/* testMatchArrowInLongListKey */ | ||
list(match($x) {1 => 1, 2 => 2} /* testLongListArrowWithMatchInKey */ => $a) = $array; | ||
} | ||
|
||
function matchInShortList() { | ||
/* testMatchArrowInShortListKey */ | ||
[match($x) {1 => 1, 2 => 2} /* testShortListArrowWithMatchInKey */ => $a] = $array; | ||
} |
Oops, something went wrong.