To: vim_dev@googlegroups.com Subject: Patch 8.2.3221 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3221 Problem: Vim9: argument types are not checked at compile time. Solution: Add several more type checks. (Yegappan Lakshmanan, closes #8632) Files: src/evalfunc.c, src/popupwin.c, src/proto/typval.pro, src/testdir/test_assert.vim, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_script.vim, src/testing.c, src/typval.c *** ../vim-8.2.3220/src/evalfunc.c 2021-07-24 21:33:22.391681954 +0200 --- src/evalfunc.c 2021-07-25 15:55:35.014494127 +0200 *************** *** 558,563 **** --- 558,601 ---- } /* + * Check "type" which is the first argument of get() (blob or list or dict or + * funcref) + */ + static int + arg_get1(type_T *type, argcontext_T *context) + { + if (type->tt_type == VAR_ANY + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT + || type->tt_type == VAR_FUNC + || type->tt_type == VAR_PARTIAL) + return OK; + + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; + } + + /* + * Check "type" which is the first argument of len() (number or string or + * blob or list or dict) + */ + static int + arg_len1(type_T *type, argcontext_T *context) + { + if (type->tt_type == VAR_ANY + || type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_BLOB + || type->tt_type == VAR_LIST + || type->tt_type == VAR_DICT) + return OK; + + arg_type_mismatch(&t_list_any, type, context->arg_idx + 1); + return FAIL; + } + + /* * Check "type" which is the second argument of remove() (number or string or * any) */ *************** *** 685,690 **** --- 723,729 ---- static argcheck_T arg2_lnum[] = {arg_lnum, arg_lnum}; static argcheck_T arg2_lnum_number[] = {arg_lnum, arg_number}; static argcheck_T arg2_number[] = {arg_number, arg_number}; + static argcheck_T arg2_number_any[] = {arg_number, NULL}; static argcheck_T arg2_number_bool[] = {arg_number, arg_bool}; static argcheck_T arg2_number_dict_any[] = {arg_number, arg_dict_any}; static argcheck_T arg2_number_list[] = {arg_number, arg_list_any}; *************** *** 716,721 **** --- 755,761 ---- static argcheck_T arg3_number_string_string[] = {arg_number, arg_string, arg_string}; static argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; static argcheck_T arg3_string_any_dict[] = {arg_string, NULL, arg_dict_any}; + static argcheck_T arg3_string_any_string[] = {arg_string, NULL, arg_string}; static argcheck_T arg3_string_bool_bool[] = {arg_string, arg_bool, arg_bool}; static argcheck_T arg3_string_bool_dict[] = {arg_string, arg_bool, arg_dict_any}; static argcheck_T arg3_string_number_bool[] = {arg_string, arg_number, arg_bool}; *************** *** 729,734 **** --- 769,776 ---- static argcheck_T arg4_string_string_number_string[] = {arg_string, arg_string, arg_number, arg_string}; static argcheck_T arg5_number[] = {arg_number, arg_number, arg_number, arg_number, arg_number}; /* Function specific argument types (not covered by the above) */ + static argcheck_T arg15_assert_fails[] = {arg_string_or_nr, arg_string_or_list_any, NULL, arg_number, arg_string}; + static argcheck_T arg34_assert_inrange[] = {arg_float_or_nr, arg_float_or_nr, arg_float_or_nr, arg_string}; static argcheck_T arg4_browse[] = {arg_bool, arg_string, arg_string, arg_string}; static argcheck_T arg23_chanexpr[] = {arg_chan_or_job, NULL, arg_dict_any}; static argcheck_T arg23_chanraw[] = {arg_chan_or_job, arg_string_or_blob, arg_dict_any}; *************** *** 738,752 **** --- 780,797 ---- static argcheck_T arg12_execute[] = {arg_string_or_list_string, arg_string}; static argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; static argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; + static argcheck_T arg23_get[] = {arg_get1, arg_string_or_nr, NULL}; static argcheck_T arg14_glob[] = {arg_string, arg_bool, arg_bool, arg_bool}; static argcheck_T arg25_globpath[] = {arg_string, arg_string, arg_bool, arg_bool, arg_bool}; static argcheck_T arg24_index[] = {arg_list_or_blob, arg_item_of_prev, arg_number, arg_bool}; static argcheck_T arg23_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; + static argcheck_T arg1_len[] = {arg_len1}; static argcheck_T arg3_libcall[] = {arg_string, arg_string, arg_string_or_nr}; static argcheck_T arg14_maparg[] = {arg_string, arg_string, arg_bool, arg_bool}; static argcheck_T arg2_mapfilter[] = {arg_list_or_dict_or_blob, NULL}; static argcheck_T arg25_matchadd[] = {arg_string, arg_string, arg_number, arg_number, arg_dict_any}; static argcheck_T arg25_matchaddpos[] = {arg_string, arg_list_any, arg_number, arg_number, arg_dict_any}; + static argcheck_T arg119_printf[] = {arg_string_or_nr, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; static argcheck_T arg23_reduce[] = {arg_list_or_blob, NULL, NULL}; static argcheck_T arg24_remote_expr[] = {arg_string, arg_string, arg_string, arg_number}; static argcheck_T arg23_remove[] = {arg_list_or_dict_or_blob, arg_remove2, arg_number}; *************** *** 1067,1077 **** ret_number_bool, f_assert_equalfile}, {"assert_exception", 1, 2, 0, arg2_string, ret_number_bool, f_assert_exception}, ! {"assert_fails", 1, 5, FEARG_1, NULL, ret_number_bool, f_assert_fails}, {"assert_false", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_false}, ! {"assert_inrange", 3, 4, FEARG_3, NULL, ret_number_bool, f_assert_inrange}, {"assert_match", 2, 3, FEARG_2, arg3_string, ret_number_bool, f_assert_match}, --- 1112,1122 ---- ret_number_bool, f_assert_equalfile}, {"assert_exception", 1, 2, 0, arg2_string, ret_number_bool, f_assert_exception}, ! {"assert_fails", 1, 5, FEARG_1, arg15_assert_fails, ret_number_bool, f_assert_fails}, {"assert_false", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_false}, ! {"assert_inrange", 3, 4, FEARG_3, arg34_assert_inrange, ret_number_bool, f_assert_inrange}, {"assert_match", 2, 3, FEARG_2, arg3_string, ret_number_bool, f_assert_match}, *************** *** 1325,1331 **** ret_f_function, f_function}, {"garbagecollect", 0, 1, 0, arg1_bool, ret_void, f_garbagecollect}, ! {"get", 2, 3, FEARG_1, NULL, ret_any, f_get}, {"getbufinfo", 0, 1, FEARG_1, arg1_buffer_or_dict_any, ret_list_dict_any, f_getbufinfo}, --- 1370,1376 ---- ret_f_function, f_function}, {"garbagecollect", 0, 1, 0, arg1_bool, ret_void, f_garbagecollect}, ! {"get", 2, 3, FEARG_1, arg23_get, ret_any, f_get}, {"getbufinfo", 0, 1, FEARG_1, arg1_buffer_or_dict_any, ret_list_dict_any, f_getbufinfo}, *************** *** 1515,1521 **** ret_list_string, f_keys}, {"last_buffer_nr", 0, 0, 0, NULL, // obsolete ret_number, f_last_buffer_nr}, ! {"len", 1, 1, FEARG_1, NULL, ret_number, f_len}, {"libcall", 3, 3, FEARG_3, arg3_libcall, ret_string, f_libcall}, --- 1560,1566 ---- ret_list_string, f_keys}, {"last_buffer_nr", 0, 0, 0, NULL, // obsolete ret_number, f_last_buffer_nr}, ! {"len", 1, 1, FEARG_1, arg1_len, ret_number, f_len}, {"libcall", 3, 3, FEARG_3, arg3_libcall, ret_string, f_libcall}, *************** *** 1541,1547 **** ret_float, FLOAT_FUNC(f_log)}, {"log10", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log10)}, ! {"luaeval", 1, 2, FEARG_1, NULL, ret_any, #ifdef FEAT_LUA f_luaeval --- 1586,1592 ---- ret_float, FLOAT_FUNC(f_log)}, {"log10", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log10)}, ! {"luaeval", 1, 2, FEARG_1, arg2_string_any, ret_any, #ifdef FEAT_LUA f_luaeval *************** *** 1627,1633 **** ret_number, PROP_FUNC(f_popup_beval)}, {"popup_clear", 0, 1, 0, arg1_bool, ret_void, PROP_FUNC(f_popup_clear)}, ! {"popup_close", 1, 2, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_close)}, {"popup_create", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_create)}, --- 1672,1678 ---- ret_number, PROP_FUNC(f_popup_beval)}, {"popup_clear", 0, 1, 0, arg1_bool, ret_void, PROP_FUNC(f_popup_clear)}, ! {"popup_close", 1, 2, FEARG_1, arg2_number_any, ret_void, PROP_FUNC(f_popup_close)}, {"popup_create", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_create)}, *************** *** 1667,1673 **** ret_float, FLOAT_FUNC(f_pow)}, {"prevnonblank", 1, 1, FEARG_1, arg1_lnum, ret_number, f_prevnonblank}, ! {"printf", 1, 19, FEARG_2, NULL, ret_string, f_printf}, {"prompt_getprompt", 1, 1, FEARG_1, arg1_buffer, ret_string, JOB_FUNC(f_prompt_getprompt)}, --- 1712,1718 ---- ret_float, FLOAT_FUNC(f_pow)}, {"prevnonblank", 1, 1, FEARG_1, arg1_lnum, ret_number, f_prevnonblank}, ! {"printf", 1, 19, FEARG_2, arg119_printf, ret_string, f_printf}, {"prompt_getprompt", 1, 1, FEARG_1, arg1_buffer, ret_string, JOB_FUNC(f_prompt_getprompt)}, *************** *** 1829,1835 **** ret_bool, f_setdigraph}, {"setdigraphlist", 1, 1, FEARG_1, arg1_list_string, ret_bool, f_setdigraphlist}, ! {"setenv", 2, 2, FEARG_2, NULL, ret_void, f_setenv}, {"setfperm", 2, 2, FEARG_1, arg2_string, ret_number_bool, f_setfperm}, --- 1874,1880 ---- ret_bool, f_setdigraph}, {"setdigraphlist", 1, 1, FEARG_1, arg1_list_string, ret_bool, f_setdigraphlist}, ! {"setenv", 2, 2, FEARG_2, arg2_string_any, ret_void, f_setenv}, {"setfperm", 2, 2, FEARG_1, arg2_string, ret_number_bool, f_setfperm}, *************** *** 1843,1849 **** ret_number_bool, f_setpos}, {"setqflist", 1, 3, FEARG_1, arg13_setqflist, ret_number_bool, f_setqflist}, ! {"setreg", 2, 3, FEARG_2, NULL, ret_number_bool, f_setreg}, {"settabvar", 3, 3, FEARG_3, arg3_number_string_any, ret_void, f_settabvar}, --- 1888,1894 ---- ret_number_bool, f_setpos}, {"setqflist", 1, 3, FEARG_1, arg13_setqflist, ret_number_bool, f_setqflist}, ! {"setreg", 2, 3, FEARG_2, arg3_string_any_string, ret_number_bool, f_setreg}, {"settabvar", 3, 3, FEARG_3, arg3_number_string_any, ret_void, f_settabvar}, *************** *** 6563,6568 **** --- 6608,6616 ---- if (check_restricted() || check_secure()) return; + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + str = tv_get_string_buf(&argvars[0], buf); do_luaeval(str, argvars + 1, rettv); } *************** *** 7139,7144 **** --- 7187,7195 ---- rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + if (in_vim9script() && check_for_string_or_number_arg(argvars, 0) == FAIL) + return; + // Get the required length, allocate the buffer and do it for real. did_emsg = FALSE; fmt = (char *)tv_get_string_buf(&argvars[0], buf); *************** *** 8515,8522 **** { char_u namebuf[NUMBUFLEN]; char_u valbuf[NUMBUFLEN]; ! char_u *name = tv_get_string_buf(&argvars[0], namebuf); if (argvars[1].v_type == VAR_SPECIAL && argvars[1].vval.v_number == VVAL_NULL) vim_unsetenv(name); --- 8566,8577 ---- { char_u namebuf[NUMBUFLEN]; char_u valbuf[NUMBUFLEN]; ! char_u *name; ! ! if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) ! return; + name = tv_get_string_buf(&argvars[0], namebuf); if (argvars[1].v_type == VAR_SPECIAL && argvars[1].vval.v_number == VVAL_NULL) vim_unsetenv(name); *************** *** 8616,8621 **** --- 8671,8681 ---- typval_T *regcontents; int pointreg; + if (in_vim9script() + && (check_for_string_arg(argvars, 0) == FAIL + || check_for_opt_string_arg(argvars, 2) == FAIL)) + return; + pointreg = 0; regcontents = NULL; block_len = -1; *** ../vim-8.2.3220/src/popupwin.c 2021-07-23 20:37:52.022322433 +0200 --- src/popupwin.c 2021-07-25 15:55:35.014494127 +0200 *************** *** 2516,2524 **** void f_popup_close(typval_T *argvars, typval_T *rettv UNUSED) { ! int id = (int)tv_get_number(argvars); win_T *wp; if ( # ifdef FEAT_TERMINAL // if the popup contains a terminal it will become hidden --- 2516,2528 ---- void f_popup_close(typval_T *argvars, typval_T *rettv UNUSED) { ! int id; win_T *wp; + if (in_vim9script() && check_for_number_arg(argvars, 0) == FAIL) + return; + + id = (int)tv_get_number(argvars); if ( # ifdef FEAT_TERMINAL // if the popup contains a terminal it will become hidden *** ../vim-8.2.3220/src/proto/typval.pro 2021-07-24 16:16:11.542239515 +0200 --- src/proto/typval.pro 2021-07-25 15:55:35.014494127 +0200 *************** *** 14,19 **** --- 14,20 ---- int check_for_opt_string_arg(typval_T *args, int idx); int check_for_number_arg(typval_T *args, int idx); int check_for_opt_number_arg(typval_T *args, int idx); + int check_for_float_or_nr_arg(typval_T *args, int idx); int check_for_bool_arg(typval_T *args, int idx); int check_for_opt_bool_arg(typval_T *args, int idx); int check_for_list_arg(typval_T *args, int idx); *************** *** 31,36 **** --- 32,38 ---- int check_for_opt_string_or_number_arg(typval_T *args, int idx); int check_for_string_or_blob_arg(typval_T *args, int idx); int check_for_string_or_list_arg(typval_T *args, int idx); + int check_for_opt_string_or_list_arg(typval_T *args, int idx); int check_for_list_or_blob_arg(typval_T *args, int idx); int check_for_list_or_dict_or_blob_arg(typval_T *args, int idx); int check_for_buffer_or_dict_arg(typval_T *args, int idx); *** ../vim-8.2.3220/src/testdir/test_assert.vim 2021-06-22 19:52:23.901800877 +0200 --- src/testdir/test_assert.vim 2021-07-25 15:55:35.014494127 +0200 *************** *** 268,288 **** catch let exp = v:exception endtry ! call assert_match("E856: \"assert_fails()\" second argument", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp')) catch let exp = v:exception endtry ! call assert_match("E1115: \"assert_fails()\" fourth argument must be a number", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123)) catch let exp = v:exception endtry ! call assert_match("E1116: \"assert_fails()\" fifth argument must be a string", exp) endfunc func Test_assert_fails_in_try_block() --- 268,288 ---- catch let exp = v:exception endtry ! call assert_match("E1174: String required for argument 2", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp')) catch let exp = v:exception endtry ! call assert_match("E1210: Number required for argument 4", exp) try call assert_equal(1, assert_fails('xxx', 'E492', '', 54, 123)) catch let exp = v:exception endtry ! call assert_match("E1174: String required for argument 5", exp) endfunc func Test_assert_fails_in_try_block() *** ../vim-8.2.3220/src/testdir/test_vim9_builtin.vim 2021-07-25 15:41:40.996569003 +0200 --- src/testdir/test_vim9_builtin.vim 2021-07-25 15:55:35.014494127 +0200 *************** *** 234,239 **** --- 234,253 ---- CheckDefFailure(['assert_exception("E1:", v:null)'], 'E1013: Argument 2: type mismatch, expected string but got special') enddef + def Test_assert_fails() + CheckDefAndScriptFailure2(['assert_fails([])'], 'E1013: Argument 1: type mismatch, expected string but got list', 'E1174: String required for argument 1') + CheckDefAndScriptFailure2(['assert_fails("a", true)'], 'E1013: Argument 2: type mismatch, expected string but got bool', 'E1174: String required for argument 2') + CheckDefAndScriptFailure2(['assert_fails("a", "b", "c", "d")'], 'E1013: Argument 4: type mismatch, expected number but got string', 'E1210: Number required for argument 4') + CheckDefAndScriptFailure2(['assert_fails("a", "b", "c", 4, 5)'], 'E1013: Argument 5: type mismatch, expected string but got number', 'E1174: String required for argument 5') + enddef + + def Test_assert_inrange() + CheckDefAndScriptFailure2(['assert_inrange("a", 2, 3)'], 'E1013: Argument 1: type mismatch, expected number but got string', 'E1210: Number required for argument 1') + CheckDefAndScriptFailure2(['assert_inrange(1, "b", 3)'], 'E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2') + CheckDefAndScriptFailure2(['assert_inrange(1, 2, "c")'], 'E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3') + CheckDefAndScriptFailure2(['assert_inrange(1, 2, 3, 4)'], 'E1013: Argument 4: type mismatch, expected string but got number', 'E1174: String required for argument 4') + enddef + def Test_assert_match() CheckDefFailure(['assert_match({}, "b")'], 'E1013: Argument 1: type mismatch, expected string but got dict') CheckDefFailure(['assert_match("a", 1)'], 'E1013: Argument 2: type mismatch, expected string but got number') *************** *** 1144,1149 **** --- 1158,1177 ---- CheckDefAndScriptFailure2(['garbagecollect(20)'], 'E1013: Argument 1: type mismatch, expected bool but got number', 'E1023: Using a Number as a Bool') enddef + def Test_get() + CheckDefAndScriptFailure2(['get("a", 1)'], 'E1013: Argument 1: type mismatch, expected list but got string', 'E896: Argument of get() must be a List, Dictionary or Blob') + [3, 5, 2]->get(1)->assert_equal(5) + [3, 5, 2]->get(3)->assert_equal(0) + [3, 5, 2]->get(3, 9)->assert_equal(9) + assert_equal(get(0z102030, 2), 0x30) + {a: 7, b: 11, c: 13}->get('c')->assert_equal(13) + {10: 'a', 20: 'b', 30: 'd'}->get(20)->assert_equal('b') + function('max')->get('name')->assert_equal('max') + var F: func = function('min', [[5, 8, 6]]) + F->get('name')->assert_equal('min') + F->get('args')->assert_equal([[5, 8, 6]]) + enddef + def Test_getbufinfo() var bufinfo = getbufinfo(bufnr()) getbufinfo('%')->assert_equal(bufinfo) *************** *** 1703,1708 **** --- 1731,1745 ---- var->assert_equal(['a', 'b']) enddef + def Test_len() + CheckDefAndScriptFailure2(['len(true)'], 'E1013: Argument 1: type mismatch, expected list but got bool', 'E701: Invalid type for len()') + assert_equal(2, "ab"->len()) + assert_equal(3, 456->len()) + assert_equal(0, []->len()) + assert_equal(1, {a: 10}->len()) + assert_equal(4, 0z20304050->len()) + enddef + def Test_libcall() CheckFeature libcall CheckDefAndScriptFailure2(['libcall(1, "b", 3)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') *************** *** 1764,1769 **** --- 1801,1813 ---- CheckDefAndScriptFailure2(['listener_remove("x")'], 'E1013: Argument 1: type mismatch, expected number but got string', 'E1030: Using a String as a Number') enddef + def Test_lua() + if !has('lua') + CheckFeature lua + endif + CheckDefAndScriptFailure2(['luaeval(10)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') + enddef + def Test_map() CheckDefAndScriptFailure2(['map("x", "1")'], 'E1013: Argument 1: type mismatch, expected list but got string', 'E1211: List required for argument 1') CheckDefAndScriptFailure2(['map(1, "1")'], 'E1013: Argument 1: type mismatch, expected list but got number', 'E1211: List required for argument 1') *************** *** 2112,2117 **** --- 2156,2165 ---- CheckDefAndScriptFailure2(['popup_clear(2)'], 'E1013: Argument 1: type mismatch, expected bool but got number', 'E1023: Using a Number as a Bool') enddef + def Test_popup_close() + CheckDefAndScriptFailure2(['popup_close("a")'], 'E1013: Argument 1: type mismatch, expected number but got string', 'E1210: Number required for argument 1') + enddef + def Test_popup_create() # Pass variable of type 'any' to popup_create() var what: any = 'Hello' *************** *** 2190,2195 **** --- 2238,2249 ---- assert_equal(0, prevnonblank(1)) enddef + def Test_printf() + CheckDefAndScriptFailure2(['printf([1])'], 'E1013: Argument 1: type mismatch, expected string but got list', 'E1174: String required for argument 1') + printf(0x10)->assert_equal('16') + assert_equal(" abc", "abc"->printf("%4s")) + enddef + def Test_prompt_getprompt() if !has('channel') CheckFeature channel *************** *** 2798,2803 **** --- 2852,2861 ---- CheckDefAndScriptFailure2(['setcursorcharpos(1, 2, "3")'], 'E1013: Argument 3: type mismatch, expected number but got string', 'E1210: Number required for argument 3') enddef + def Test_setenv() + CheckDefAndScriptFailure2(['setenv(1, 2)'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') + enddef + def Test_setfperm() CheckDefFailure(['setfperm(1, "b")'], 'E1013: Argument 1: type mismatch, expected string but got number') CheckDefFailure(['setfperm("a", 0z10)'], 'E1013: Argument 2: type mismatch, expected string but got blob') *************** *** 2851,2856 **** --- 2909,2916 ---- setreg('a', reginfo) getreginfo('a')->assert_equal(reginfo) assert_fails('setreg("ab", 0)', 'E1162:') + CheckDefAndScriptFailure2(['setreg(1, "b")'], 'E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1') + CheckDefAndScriptFailure2(['setreg("a", "b", 3)'], 'E1013: Argument 3: type mismatch, expected string but got number', 'E1174: String required for argument 3') enddef def Test_settabvar() *** ../vim-8.2.3220/src/testdir/test_vim9_script.vim 2021-07-25 14:13:50.040566339 +0200 --- src/testdir/test_vim9_script.vim 2021-07-25 15:55:35.014494127 +0200 *************** *** 521,528 **** assert_equal(344, n) try ! echo len(v:true) ! catch /E701:/ n = 355 endtry assert_equal(355, n) --- 521,528 ---- assert_equal(344, n) try ! echo range(1, 2, 0) ! catch /E726:/ n = 355 endtry assert_equal(355, n) *** ../vim-8.2.3220/src/testing.c 2021-07-24 21:33:22.391681954 +0200 --- src/testing.c 2021-07-25 15:55:35.014494127 +0200 *************** *** 566,577 **** void f_assert_fails(typval_T *argvars, typval_T *rettv) { ! char_u *cmd = tv_get_string_chk(&argvars[0]); garray_T ga; int save_trylevel = trylevel; int called_emsg_before = called_emsg; char *wrong_arg_msg = NULL; // trylevel must be zero for a ":throw" command to be considered failed trylevel = 0; suppress_errthrow = TRUE; --- 566,588 ---- void f_assert_fails(typval_T *argvars, typval_T *rettv) { ! char_u *cmd; garray_T ga; int save_trylevel = trylevel; int called_emsg_before = called_emsg; char *wrong_arg_msg = NULL; + if (check_for_string_or_number_arg(argvars, 0) == FAIL + || check_for_opt_string_or_list_arg(argvars, 1) == FAIL + || (argvars[1].v_type != VAR_UNKNOWN + && (argvars[2].v_type != VAR_UNKNOWN + && (check_for_opt_number_arg(argvars, 3) == FAIL + || (argvars[3].v_type != VAR_UNKNOWN + && check_for_opt_string_arg(argvars, 4) == FAIL))))) + return; + + cmd = tv_get_string_chk(&argvars[0]); + // trylevel must be zero for a ":throw" command to be considered failed trylevel = 0; suppress_errthrow = TRUE; *************** *** 799,804 **** --- 810,821 ---- void f_assert_inrange(typval_T *argvars, typval_T *rettv) { + if (check_for_float_or_nr_arg(argvars, 0) == FAIL + || check_for_float_or_nr_arg(argvars, 1) == FAIL + || check_for_float_or_nr_arg(argvars, 2) == FAIL + || check_for_opt_string_arg(argvars, 3) == FAIL) + return; + rettv->vval.v_number = assert_inrange(argvars); } *** ../vim-8.2.3220/src/typval.c 2021-07-24 16:16:11.542239515 +0200 --- src/typval.c 2021-07-25 15:55:35.014494127 +0200 *************** *** 422,427 **** --- 422,444 ---- } /* + * Give an error and return FAIL unless "args[idx]" is a float or a number. + */ + int + check_for_float_or_nr_arg(typval_T *args, int idx) + { + if (args[idx].v_type != VAR_FLOAT && args[idx].v_type != VAR_NUMBER) + { + if (idx >= 0) + semsg(_(e_number_required_for_argument_nr), idx + 1); + else + emsg(_(e_numberreq)); + return FAIL; + } + return OK; + } + + /* * Give an error and return FAIL unless "args[idx]" is a bool. */ int *************** *** 653,658 **** --- 670,685 ---- } /* + * Check for an optional string or list argument at 'idx' + */ + int + check_for_opt_string_or_list_arg(typval_T *args, int idx) + { + return (args[idx].v_type == VAR_UNKNOWN + || check_for_string_or_list_arg(args, idx)); + } + + /* * Give an error and return FAIL unless "args[idx]" is a list or a blob. */ int *** ../vim-8.2.3220/src/version.c 2021-07-25 15:41:40.996569003 +0200 --- src/version.c 2021-07-25 15:56:53.050306622 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3221, /**/ -- You are Dead. Do you wish to restart, load, or quit? /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///