Merge pull request #17 from AlessioGiacobbe/fix/multilevel-break-propagation
fix(parser): propagate multi-level break/continue before trailing statements of enclosing bodiesmaster
commit
c8d22b66fd
7 changed files with 223 additions and 38 deletions
@ -0,0 +1,155 @@ |
|||||||
|
--TEST-- |
||||||
|
Multi-level break/continue must skip trailing statements of enclosing bodies |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function nativeSwitchBreak(int $n): void |
||||||
|
{ |
||||||
|
// break 2 from a native (int-typed) switch inside a loop must exit the loop |
||||||
|
for ($i = 0; $i < 3; $i++) { |
||||||
|
echo "n-iter $i\n"; |
||||||
|
switch ($n) { |
||||||
|
case 1: |
||||||
|
echo "n-case\n"; |
||||||
|
break 2; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "n-after $i\n"; |
||||||
|
} |
||||||
|
echo "native-switch-break-2: done\n"; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
// break 2: statements after the inner loop must not run |
||||||
|
foreach ([1, 2, 3] as $x) { |
||||||
|
foreach ([1, 2, 3] as $y) { |
||||||
|
echo "b2 inner $x.$y\n"; |
||||||
|
break 2; |
||||||
|
} |
||||||
|
echo "b2 leaked $x\n"; |
||||||
|
} |
||||||
|
echo "break-2: done\n"; |
||||||
|
|
||||||
|
// continue 2: statements after the inner loop must not run |
||||||
|
foreach ([1, 2] as $x) { |
||||||
|
foreach ([1, 2] as $y) { |
||||||
|
echo "c2 inner $x.$y\n"; |
||||||
|
continue 2; |
||||||
|
} |
||||||
|
echo "c2 leaked $x\n"; |
||||||
|
} |
||||||
|
echo "continue-2: done\n"; |
||||||
|
|
||||||
|
// break 2 from a switch inside a loop: statements after the switch |
||||||
|
// must not run and the loop must exit |
||||||
|
for ($i = 0; $i < 3; $i++) { |
||||||
|
echo "sw iter $i\n"; |
||||||
|
switch ($i) { |
||||||
|
case 1: |
||||||
|
echo "sw case $i\n"; |
||||||
|
break 2; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "sw after $i\n"; |
||||||
|
} |
||||||
|
echo "switch-break-2: done\n"; |
||||||
|
|
||||||
|
// continue 2 from a switch inside a loop targets the loop |
||||||
|
for ($i = 0; $i < 3; $i++) { |
||||||
|
switch ($i) { |
||||||
|
case 1: |
||||||
|
echo "swc case $i\n"; |
||||||
|
continue 2; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "swc after $i\n"; |
||||||
|
} |
||||||
|
echo "switch-continue-2: done\n"; |
||||||
|
|
||||||
|
// break 3 from a loop inside a switch inside a loop |
||||||
|
for ($i = 0; $i < 3; $i++) { |
||||||
|
switch ($i) { |
||||||
|
case 0: |
||||||
|
foreach ([1, 2] as $y) { |
||||||
|
echo "b3 deep $i.$y\n"; |
||||||
|
break 3; |
||||||
|
} |
||||||
|
echo "b3 leaked after deep loop\n"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
echo "b3 leaked default\n"; |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "b3 leaked after switch $i\n"; |
||||||
|
} |
||||||
|
echo "break-3-through-switch: done\n"; |
||||||
|
|
||||||
|
// continue 2 from a loop inside a switch acts as break on the switch |
||||||
|
// level: statements after the switch must still run |
||||||
|
for ($i = 0; $i < 2; $i++) { |
||||||
|
switch ($i) { |
||||||
|
case 0: |
||||||
|
foreach ([1, 2] as $y) { |
||||||
|
echo "c2s deep $i.$y\n"; |
||||||
|
continue 2; |
||||||
|
} |
||||||
|
echo "c2s leaked after deep loop\n"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "c2s after switch $i\n"; |
||||||
|
} |
||||||
|
echo "continue-2-targets-switch: done\n"; |
||||||
|
|
||||||
|
// continue 3 propagates through a switch up to the outer loop |
||||||
|
for ($i = 0; $i < 2; $i++) { |
||||||
|
switch ($i) { |
||||||
|
case 0: |
||||||
|
foreach ([1, 2] as $y) { |
||||||
|
echo "c3 deep $i.$y\n"; |
||||||
|
continue 3; |
||||||
|
} |
||||||
|
echo "c3 leaked after deep loop\n"; |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
echo "c3 after switch $i\n"; |
||||||
|
} |
||||||
|
echo "continue-3-through-switch: done\n"; |
||||||
|
|
||||||
|
nativeSwitchBreak(1); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
b2 inner 1.1 |
||||||
|
break-2: done |
||||||
|
c2 inner 1.1 |
||||||
|
c2 inner 2.1 |
||||||
|
continue-2: done |
||||||
|
sw iter 0 |
||||||
|
sw after 0 |
||||||
|
sw iter 1 |
||||||
|
sw case 1 |
||||||
|
switch-break-2: done |
||||||
|
swc after 0 |
||||||
|
swc case 1 |
||||||
|
swc after 2 |
||||||
|
switch-continue-2: done |
||||||
|
b3 deep 0.1 |
||||||
|
break-3-through-switch: done |
||||||
|
c2s deep 0.1 |
||||||
|
c2s after switch 0 |
||||||
|
c2s after switch 1 |
||||||
|
continue-2-targets-switch: done |
||||||
|
c3 deep 0.1 |
||||||
|
c3 after switch 1 |
||||||
|
continue-3-through-switch: done |
||||||
|
n-iter 0 |
||||||
|
n-case |
||||||
|
native-switch-break-2: done |
||||||
Loading…
Reference in new issue