diff --git a/Zend/tests/switch/continue_targeting_switch_warning.phpt b/Zend/tests/switch/continue_targeting_switch_warning.phpt index 379108ad1f8e5..079042fcbd0bb 100644 --- a/Zend/tests/switch/continue_targeting_switch_warning.phpt +++ b/Zend/tests/switch/continue_targeting_switch_warning.phpt @@ -26,11 +26,11 @@ function test() { case 0: while ($xyz) { continue 2; // INVALID - } + } fallthrough; case 1: while ($xyz) { continue; - } + } fallthrough; case 2: while ($xyz) { break 2; @@ -42,11 +42,11 @@ function test() { case 0: while ($xyz) { continue 2; // INVALID - } + } fallthrough; case 1: while ($xyz) { continue 3; - } + } fallthrough; case 2: while ($xyz) { break 2; diff --git a/Zend/tests/switch/fallthrough_warning.phpt b/Zend/tests/switch/fallthrough_warning.phpt new file mode 100644 index 0000000000000..f4d9295ce74a4 --- /dev/null +++ b/Zend/tests/switch/fallthrough_warning.phpt @@ -0,0 +1,64 @@ +--TEST-- +Warning on switch case falling through +--FILE-- +getMessage(), PHP_EOL; + } +} + +?> +--EXPECTF-- +Warning: Non-empty case falls through to the next case, terminate the case with "fallthrough;" if this is intentional in %s on line 9 + +Warning: "continue" targeting switch is equivalent to "break" in %s on line 25 +a +a +b +c +c +d +e +e +f +g +h +i +Exception: exception diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 78cc114223d0e..d9d7871883e56 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6616,6 +6616,7 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */ jmpnz_opnums = safe_emalloc(sizeof(uint32_t), cases->children, 0); for (i = 0; i < cases->children; ++i) { zend_ast *case_ast = cases->child[i]; + ZEND_ASSERT(case_ast->kind == ZEND_AST_SWITCH_CASE); zend_ast *cond_ast = case_ast->child[0]; znode cond_node; @@ -6659,8 +6660,9 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */ for (i = 0; i < cases->children; ++i) { zend_ast *case_ast = cases->child[i]; + ZEND_ASSERT(case_ast->kind == ZEND_AST_SWITCH_CASE); zend_ast *cond_ast = case_ast->child[0]; - zend_ast *stmt_ast = case_ast->child[1]; + zend_ast_list *stmt_ast = zend_ast_get_list(case_ast->child[1]); if (cond_ast) { zend_update_jump_target_to_next(jmpnz_opnums[i]); @@ -6688,7 +6690,27 @@ static void zend_compile_switch(zend_ast *ast) /* {{{ */ } } - zend_compile_stmt(stmt_ast); + if (stmt_ast->children > 0 && i < (cases->children - 1)) { + zend_ast *last_stmt = stmt_ast->child[stmt_ast->children - 1]; + while (last_stmt && last_stmt->kind == ZEND_AST_STMT_LIST) { + zend_ast_list *list = zend_ast_get_list(last_stmt); + last_stmt = list->child[list->children - 1]; + } + if (last_stmt == NULL + || (last_stmt->kind != ZEND_AST_BREAK + && last_stmt->kind != ZEND_AST_CONTINUE + && last_stmt->kind != ZEND_AST_RETURN + && last_stmt->kind != ZEND_AST_THROW + && last_stmt->kind != ZEND_AST_GOTO)) { + if (!(last_stmt->kind == ZEND_AST_CONST && zend_string_equals_literal(zend_ast_get_str(last_stmt->child[0]), "fallthrough"))) { + CG(zend_lineno) = case_ast->lineno; + zend_error(E_WARNING, + "Non-empty case falls through to the next case, terminate the case with \"fallthrough;\" if this is intentional"); + } + } + } + + zend_compile_stmt((zend_ast*)stmt_ast); } if (!has_default_case) { diff --git a/Zend/zend_constants.stub.php b/Zend/zend_constants.stub.php index e511dc8808668..82155890feac2 100644 --- a/Zend/zend_constants.stub.php +++ b/Zend/zend_constants.stub.php @@ -141,3 +141,5 @@ * @undocumentable */ const NULL = null; + +const fallthrough = null; diff --git a/Zend/zend_constants_arginfo.h b/Zend/zend_constants_arginfo.h index b10adc02d28af..394c2ca022ef4 100644 --- a/Zend/zend_constants_arginfo.h +++ b/Zend/zend_constants_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_constants.stub.php instead. - * Stub hash: 569ccba4e0a93a9ce49c81c76955413188df390e */ + * Stub hash: c29c64a7cc3bdec6c7227a8f6e4583d050e3e81c */ static void register_zend_constants_symbols(int module_number) { @@ -27,6 +27,7 @@ static void register_zend_constants_symbols(int module_number) REGISTER_BOOL_CONSTANT("TRUE", true, CONST_PERSISTENT); REGISTER_BOOL_CONSTANT("FALSE", false, CONST_PERSISTENT); REGISTER_NULL_CONSTANT("NULL", CONST_PERSISTENT); + REGISTER_NULL_CONSTANT("fallthrough", CONST_PERSISTENT); zend_attribute *attribute_Deprecated_const_E_STRICT_0 = zend_add_global_constant_attribute(const_E_STRICT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); diff --git a/ext/mysqli/tests/mysqli_fetch_array_large.phpt b/ext/mysqli/tests/mysqli_fetch_array_large.phpt index fa8d33b769642..7016f84f1baae 100644 --- a/ext/mysqli/tests/mysqli_fetch_array_large.phpt +++ b/ext/mysqli/tests/mysqli_fetch_array_large.phpt @@ -86,26 +86,6 @@ memory_limit=-1 return true; } - function parse_memory_limit($limit) { - - $val = trim($limit); - $last = strtolower($val[strlen($val)-1]); - - switch($last) { - // The 'G' modifier is available since PHP 5.1.0 - case 'g': - $val *= 1024; - case 'm': - $val *= 1024; - case 'k': - $val *= 1024; - default: - break; - } - return $val; - } - - function test_fetch($host, $user, $passwd, $db, $port, $socket, $engine, $flags = null) { $link = mysqli_init(); @@ -123,7 +103,7 @@ memory_limit=-1 $package_size = 524288; $offset = 3; - $limit = (ini_get('memory_limit') > 0) ? parse_memory_limit(ini_get('memory_limit')) : pow(2, 32); + $limit = (ini_get('memory_limit') > 0) ? ini_parse_quantity(ini_get('memory_limit')) : pow(2, 32); /* try to respect php.ini but make run time a soft limit */ $max_runtime = (ini_get('max_execution_time') > 0) ? ini_get('max_execution_time') : 30; diff --git a/ext/mysqli/tests/mysqli_fetch_field_flags.phpt b/ext/mysqli/tests/mysqli_fetch_field_flags.phpt index 935defa484d2c..ab1bd0fe35317 100644 --- a/ext/mysqli/tests/mysqli_fetch_field_flags.phpt +++ b/ext/mysqli/tests/mysqli_fetch_field_flags.phpt @@ -158,9 +158,6 @@ mysqli_close($link); // TODO - check exact version! $expected_flags = trim(str_replace('UNSIGNED', '', $expected_flags)); } - - default: - break; } list($missing_flags, $unexpected_flags, $flags_found) = checkFlags($field->flags, $expected_flags, $flags); diff --git a/ext/mysqli/tests/mysqli_fork.phpt b/ext/mysqli/tests/mysqli_fork.phpt index f912e993a12ec..00ec7ebae2e2e 100644 --- a/ext/mysqli/tests/mysqli_fork.phpt +++ b/ext/mysqli/tests/mysqli_fork.phpt @@ -32,6 +32,7 @@ if (!have_innodb($link)) case 0: /* child */ exit(0); + break; default: /* parent */ @@ -124,6 +125,7 @@ if (!have_innodb($link)) exit(mysqli_errno($plink)); exit(0); + break; default: /* parent */ diff --git a/ext/opcache/tests/ssa_bug_007.phpt b/ext/opcache/tests/ssa_bug_007.phpt index 59f98d901bc7a..5d63a224f3182 100644 --- a/ext/opcache/tests/ssa_bug_007.phpt +++ b/ext/opcache/tests/ssa_bug_007.phpt @@ -20,5 +20,6 @@ function render($properties) { } ?> OK ---EXPECT-- +--EXPECTF-- +Warning: Non-empty case falls through to the next case, terminate the case with "fallthrough;" if this is intentional in %s on line %d OK diff --git a/ext/opcache/tests/switch_string_free_opt.phpt b/ext/opcache/tests/switch_string_free_opt.phpt index 9baacfed3ae7c..0e5c0db88af31 100644 --- a/ext/opcache/tests/switch_string_free_opt.phpt +++ b/ext/opcache/tests/switch_string_free_opt.phpt @@ -11,5 +11,6 @@ function test($a) { } ?> ===DONE=== ---EXPECT-- +--EXPECTF-- +Warning: Non-empty case falls through to the next case, terminate the case with "fallthrough;" if this is intentional in %s on line %d ===DONE=== diff --git a/run-tests.php b/run-tests.php index c08d07cdd7c18..6634ca1af0781 100755 --- a/run-tests.php +++ b/run-tests.php @@ -438,7 +438,7 @@ function main(): void break; } $i--; - // no break + fallthrough; case 'w': $failed_tests_file = fopen($argv[++$i], 'w+t'); break; @@ -602,10 +602,11 @@ function main(): void case '--version': echo '$Id$' . "\n"; exit(1); + break; default: echo "Illegal switch '$switch' specified!\n"; - // no break + fallthrough; case 'h': case '-help': case '--help': @@ -1520,7 +1521,7 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te } } $junit->mergeResults($message["junit"]); - // no break + fallthrough; case "ready": // Schedule sequential tests only once we are down to one worker. if (count($workerProcs) === 1 && $sequentialTests) { @@ -1616,7 +1617,7 @@ function run_all_tests_parallel(array $test_files, array $env, ?string $redir_te ]; $error_consts = array_combine(array_map('constant', $error_consts), $error_consts); error("Worker $i reported unexpected {$error_consts[$message['errno']]}: $message[errstr] in $message[errfile] on line $message[errline]"); - // no break + fallthrough; default: kill_children($workerProcs); error("Unrecognised message type '$message[type]' from worker $i");