Skip to content

Commit

Permalink
Implemented RFC: Replace "Missing argument" warning with "Too few arg…
Browse files Browse the repository at this point in the history
…uments" exception

Squashed commit of the following:

commit 8b45fa2
Author: Dmitry Stogov <[email protected]>
Date:   Thu Jun 16 01:52:50 2016 +0300

    Separate slow path of ZEND_RECV into a cold function.

commit 9e18895
Author: Dmitry Stogov <[email protected]>
Date:   Wed Jun 15 23:26:28 2016 +0300

    Required argument can't be IS_UNDEF anymore.

commit 662db66
Author: Dmitry Stogov <[email protected]>
Date:   Tue May 31 17:14:50 2016 +0300

    Replace "Missing argument" warning by "Too few arguments" exception.
  • Loading branch information
dstogov committed Jun 15, 2016
1 parent a9512af commit ff363e2
Show file tree
Hide file tree
Showing 48 changed files with 341 additions and 438 deletions.
21 changes: 13 additions & 8 deletions Zend/tests/001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@ function test3($a, $b) {

test1();
test2(1);
test2();
try {
test2();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}

test3(1,2);

call_user_func("test1");
call_user_func("test3", 1);
try {
call_user_func("test3", 1);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
call_user_func("test3", 1, 2);

class test {
Expand All @@ -38,14 +47,10 @@ echo "Done\n";
--EXPECTF--
int(0)
int(1)

Warning: Missing argument 1 for test2(), called in %s on line %d
int(0)
Exception: Too few arguments to function test2(), 0 passed in %s001.php on line 18 and exactly 1 expected
int(2)
int(0)

Warning: Missing argument 2 for test3()%s
int(1)
Exception: Too few arguments to function test3(), 1 passed in %s001.php on line 27 and exactly 2 expected
int(2)
int(1)

Expand Down
31 changes: 12 additions & 19 deletions Zend/tests/002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@ function test3($a, $b) {
test1();
test1(10);
test2(1);
test2();
try {
test2();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
test3(1,2);

call_user_func("test1");
call_user_func("test3", 1);
try {
call_user_func("test3", 1);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
call_user_func("test3", 1, 2);

class test {
Expand Down Expand Up @@ -62,14 +70,7 @@ int(1)

Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)

Warning: Missing argument 1 for test2(), called in %s on line %d and defined in %s on line %d

Warning: func_get_arg(): Argument 0 not passed to function in %s on line %d
bool(false)

Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)
Exception: Too few arguments to function test2(), 0 passed in %s002.php on line %d and exactly 1 expected
int(1)
int(2)

Expand All @@ -84,15 +85,7 @@ bool(false)

Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)

Warning: Missing argument 2 for test3()%s
int(1)

Warning: func_get_arg(): Argument 1 not passed to function in %s on line %d
bool(false)

Warning: func_get_arg(): Argument 2 not passed to function in %s on line %d
bool(false)
Exception: Too few arguments to function test3(), 1 passed in %s002.php on line %d and exactly 2 expected
int(1)
int(2)

Expand Down
24 changes: 12 additions & 12 deletions Zend/tests/003.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,19 @@ function test3($a, $b) {
test1();
test1(10);
test2(1);
test2();
try {
test2();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
test3(1,2);

call_user_func("test1");
call_user_func("test3", 1);
try {
call_user_func("test3", 1);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
call_user_func("test3", 1, 2);

class test {
Expand All @@ -47,10 +55,7 @@ array(1) {
[0]=>
int(1)
}

Warning: Missing argument 1 for test2(), called in %s on line %d and defined in %s on line %d
array(0) {
}
Exception: Too few arguments to function test2(), 0 passed in %s003.php on line %d and exactly 1 expected
array(2) {
[0]=>
int(1)
Expand All @@ -59,12 +64,7 @@ array(2) {
}
array(0) {
}

Warning: Missing argument 2 for test3()%s
array(1) {
[0]=>
int(1)
}
Exception: Too few arguments to function test3(), 1 passed in %s003.php on line %d and exactly 2 expected
array(2) {
[0]=>
int(1)
Expand Down
22 changes: 13 additions & 9 deletions Zend/tests/bug33996.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@ function NormalTest($a)
echo "Hi!";
}

NormalTest();
FooTest();
try {
NormalTest();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
try {
FooTest();
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}
FooTest(new Foo());
?>
--EXPECTF--
Warning: Missing argument 1 for NormalTest(), called in %sbug33996.php on line %d and defined in %sbug33996.php on line %d
Hi!
Fatal error: Uncaught TypeError: Argument 1 passed to FooTest() must be an instance of Foo, none given, called in %sbug33996.php on line %d and defined in %sbug33996.php:%d
Stack trace:
#0 %s(%d): FooTest()
#1 {main}
thrown in %sbug33996.php on line %d
Exception: Too few arguments to function NormalTest(), 0 passed in %sbug33996.php on line 18 and exactly 1 expected
Exception: Too few arguments to function FooTest(), 0 passed in %sbug33996.php on line 23 and exactly 1 expected
Hello!
10 changes: 6 additions & 4 deletions Zend/tests/bug38047.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ Non-static method A::A_ftk() should not be called statically
1 %sbug38047.php:13 get_error_context()
2 %sbug38047.php:36 kalus_error_handler()

Missing argument 1 for A::A_ftk(), called in %sbug38047.php on line 36 and defined
1 %sbug38047.php:13 get_error_context()
2 %sbug38047.php:7 kalus_error_handler()
3 %sbug38047.php:36 A_ftk()

Fatal error: Uncaught Error: Too few arguments to function A::A_ftk(), 0 passed in %sbug38047.php on line 36 and exactly 1 expected in %sbug38047.php:7
Stack trace:
#0 %sbug38047.php(36): A::A_ftk()
#1 {main}
thrown in %sbug38047.php on line 7
2 changes: 1 addition & 1 deletion Zend/tests/bug55705.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function f(callable $c) {}
f();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Argument 1 passed to f() must be callable, none given, called in %s on line 3 and defined in %s:%d
Fatal error: Uncaught Error: Too few arguments to function f(), 0 passed in %s on line 3 and exactly 1 expected in %s:2
Stack trace:
#0 %s(%d): f()
#1 {main}
Expand Down
6 changes: 5 additions & 1 deletion Zend/tests/bug70689.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ try {

?>
--EXPECTF--
Missing argument 1 for foo(), called in %sbug70689.php on line %d and defined
Fatal error: Uncaught Error: Too few arguments to function foo(), 0 passed in %sbug70689.php on line 12 and exactly 1 expected in %sbug70689.php:3
Stack trace:
#0 %sbug70689.php(12): foo()
#1 {main}
thrown in %sbug70689.php on line 3
10 changes: 6 additions & 4 deletions Zend/tests/closure_027.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ test(function() { return new stdclass; });
test(function() { });

$a = function($x) use ($y) {};
test($a);
try {
test($a);
} catch (Throwable $e) {
echo "Exception: " . $e->getMessage() . "\n";
}

test(new stdclass);

Expand All @@ -24,9 +28,7 @@ object(stdClass)#%d (0) {
NULL

Notice: Undefined variable: y in %s on line %d

Warning: Missing argument 1 for {closure}(), called in %s on line %d and defined in %s on line %d
NULL
Exception: Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected

Fatal error: Uncaught TypeError: Argument 1 passed to test() must be an instance of Closure, instance of stdClass given, called in %s on line %d and defined in %s:%d
Stack trace:
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/error_reporting06.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}

function foo3($arg) {
function foo3() {
echo $undef3;
throw new Exception("test");
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/error_reporting07.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}

function foo3($arg) {
function foo3() {
echo $undef3;
throw new Exception("test");
}
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/error_reporting08.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function foo1($arg) {
function foo2($arg) {
}

function foo3($arg) {
function foo3() {
error_reporting(E_ALL|E_STRICT);
echo $undef3;
throw new Exception("test");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function f(?callable $p) {}
f();

--EXPECTF--
Fatal error: Uncaught TypeError: Argument 1 passed to f() must be callable, none given, called in %s on line %d and defined in %s:%d
Fatal error: Uncaught Error: Too few arguments to function f(), 0 passed in %snullable_type_parameters_do_not_have_default_value.php on line %d and exactly 1 expected in %s:%d
Stack trace:
#%d %s
#%d %s
Expand Down
10 changes: 5 additions & 5 deletions Zend/tests/type_declarations/scalar_none.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,20 @@ foreach ($functions as $type => $function) {
echo "Testing $type:", PHP_EOL;
try {
var_dump($function());
} catch (TypeError $e) {
} catch (Throwable $e) {
echo "*** Caught " . $e->getMessage() . PHP_EOL;
}
}
echo PHP_EOL . "Done";
--EXPECTF--
Testing int:
*** Caught Argument 1 passed to {closure}() must be of the type integer, none given, called in %s on line %d
*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing float:
*** Caught Argument 1 passed to {closure}() must be of the type float, none given, called in %s on line %d
*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing string:
*** Caught Argument 1 passed to {closure}() must be of the type string, none given, called in %s on line %d
*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing bool:
*** Caught Argument 1 passed to {closure}() must be of the type boolean, none given, called in %s on line %d
*** Caught Too few arguments to function {closure}(), 0 passed in %s on line %d and exactly 1 expected
Testing int nullable:
NULL
Testing float nullable:
Expand Down
79 changes: 21 additions & 58 deletions Zend/zend_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,60 +861,28 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
return 1;
}

static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_num, void **cache_slot)
{
zend_arg_info *cur_arg_info;
char *need_msg;
zend_class_entry *ce;

if (EXPECTED(arg_num <= zf->common.num_args)) {
cur_arg_info = &zf->common.arg_info[arg_num-1];
} else if (UNEXPECTED(zf->common.fn_flags & ZEND_ACC_VARIADIC)) {
cur_arg_info = &zf->common.arg_info[zf->common.num_args];
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data)
{
zend_execute_data *ptr = EX(prev_execute_data);

if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
zend_throw_error(NULL, "Too few arguments to function %s%s%s(), %d passed in %s on line %d and %s %d expected",
EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "",
EX(func)->common.scope ? "::" : "",
ZSTR_VAL(EX(func)->common.function_name),
EX_NUM_ARGS(),
ZSTR_VAL(ptr->func->op_array.filename),
ptr->opline->lineno,
EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least",
EX(func)->common.required_num_args);
} else {
return 1;
}

if (cur_arg_info->type_hint) {
if (cur_arg_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
} else {
ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) {
zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "none", "");
return 0;
}
*cache_slot = (void*)ce;
}
need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of ";
zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "none", "");
} else if (cur_arg_info->type_hint == IS_CALLABLE) {
zend_verify_arg_error(zf, arg_num, "be callable", "", "none", "");
} else {
zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), "none", "");
}
return 0;
}
return 1;
}

static ZEND_COLD void zend_verify_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
{
if (EXPECTED(!(EX(func)->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) ||
UNEXPECTED(zend_verify_missing_arg_type(EX(func), arg_num, cache_slot))) {
const char *class_name = EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "";
const char *space = EX(func)->common.scope ? "::" : "";
const char *func_name = EX(func)->common.function_name ? ZSTR_VAL(EX(func)->common.function_name) : "main";
zend_execute_data *ptr = EX(prev_execute_data);

if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
zend_error(E_WARNING, "Missing argument %u for %s%s%s(), called in %s on line %d and defined", arg_num, class_name, space, func_name, ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno);
} else {
zend_error(E_WARNING, "Missing argument %u for %s%s%s()", arg_num, class_name, space, func_name);
}
zend_throw_error(NULL, "Too few arguments to function %s%s%s(), %d passed and %s %d expected",
EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "",
EX(func)->common.scope ? "::" : "",
ZSTR_VAL(EX(func)->common.function_name),
EX_NUM_ARGS(),
EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least",
EX(func)->common.required_num_args);
}
}

Expand Down Expand Up @@ -3073,11 +3041,6 @@ ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_n
return zend_verify_arg_type(zf, arg_num, arg, default_value, cache_slot);
}

ZEND_API void ZEND_FASTCALL zend_check_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot)
{
zend_verify_missing_arg(execute_data, arg_num, cache_slot);
}

/*
* Local variables:
* tab-width: 4
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ extern ZEND_API const zend_internal_function zend_pass_function;

ZEND_API void ZEND_FASTCALL zend_check_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg);
ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot);
ZEND_API void ZEND_FASTCALL zend_check_missing_arg(zend_execute_data *execute_data, uint32_t arg_num, void **cache_slot);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *execute_data);

static zend_always_inline zval* zend_assign_to_variable(zval *variable_ptr, zval *value, zend_uchar value_type)
{
Expand Down
Loading

0 comments on commit ff363e2

Please sign in to comment.