To: vim_dev@googlegroups.com Subject: Patch 8.2.3897 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3897 Problem: Vim9: the second argument of map() and filter() is not checked at compile time. Solution: Add more specific type check for the second argument. Files: src/evalfunc.c, src/globals.h, src/list.c, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_expr.vim, src/testdir/test_vim9_script.vim *** ../vim-8.2.3896/src/evalfunc.c 2021-12-25 19:43:39.953098179 +0000 --- src/evalfunc.c 2021-12-25 21:14:37.207062612 +0000 *************** *** 486,491 **** --- 486,537 ---- } /* + * Check second argument of filter(): func must return a bool. + */ + static int + arg_filter_func(type_T *type, argcontext_T *context) + { + if (type->tt_type == VAR_FUNC + && !(type->tt_member->tt_type == VAR_BOOL + || type->tt_member->tt_type == VAR_NUMBER + || type->tt_member->tt_type == VAR_ANY)) + { + arg_type_mismatch(&t_func_bool, type, context->arg_idx + 1); + return FAIL; + } + return OK; + } + + /* + * Check second argument of map(). + */ + static int + arg_map_func(type_T *type, argcontext_T *context) + { + if (type->tt_type == VAR_FUNC + && type->tt_member != &t_any + && type->tt_member != &t_unknown) + { + type_T *expected = NULL; + + if (context->arg_types[0]->tt_type == VAR_LIST + || context->arg_types[0]->tt_type == VAR_DICT) + expected = context->arg_types[0]->tt_member; + else if (context->arg_types[0]->tt_type == VAR_STRING + || context->arg_types[0]->tt_type == VAR_BLOB) + expected = &t_number; + if (expected != NULL) + { + type_T t_func_exp = {VAR_FUNC, -1, 0, TTFLAG_STATIC, + expected, NULL}; + + return check_arg_type(&t_func_exp, type, context); + } + } + return OK; + } + + /* * Check "type" is a list of 'any' or a blob or a string. */ static int *************** *** 859,865 **** 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_or_string, 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}; --- 905,913 ---- 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_filter[] = {arg_list_or_dict_or_blob_or_string, arg_filter_func}; ! static argcheck_T arg2_map[] = {arg_list_or_dict_or_blob_or_string, arg_map_func}; ! static argcheck_T arg2_mapnew[] = {arg_list_or_dict_or_blob_or_string, 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}; *************** *** 1437,1443 **** ret_number_bool, f_filereadable}, {"filewritable", 1, 1, FEARG_1, arg1_string, ret_number, f_filewritable}, ! {"filter", 2, 2, FEARG_1, arg2_mapfilter, ret_first_arg, f_filter}, {"finddir", 1, 3, FEARG_1, arg3_string_string_number, ret_finddir, f_finddir}, --- 1485,1491 ---- ret_number_bool, f_filereadable}, {"filewritable", 1, 1, FEARG_1, arg1_string, ret_number, f_filewritable}, ! {"filter", 2, 2, FEARG_1, arg2_filter, ret_first_arg, f_filter}, {"finddir", 1, 3, FEARG_1, arg3_string_string_number, ret_finddir, f_finddir}, *************** *** 1703,1715 **** NULL #endif }, ! {"map", 2, 2, FEARG_1, arg2_mapfilter, ret_first_cont, f_map}, {"maparg", 1, 4, FEARG_1, arg14_maparg, ret_maparg, f_maparg}, {"mapcheck", 1, 3, FEARG_1, arg3_string_string_bool, ret_string, f_mapcheck}, ! {"mapnew", 2, 2, FEARG_1, arg2_mapfilter, ret_first_cont, f_mapnew}, {"mapset", 3, 3, FEARG_1, arg3_string_bool_dict, ret_void, f_mapset}, --- 1751,1763 ---- NULL #endif }, ! {"map", 2, 2, FEARG_1, arg2_map, ret_first_cont, f_map}, {"maparg", 1, 4, FEARG_1, arg14_maparg, ret_maparg, f_maparg}, {"mapcheck", 1, 3, FEARG_1, arg3_string_string_bool, ret_string, f_mapcheck}, ! {"mapnew", 2, 2, FEARG_1, arg2_mapnew, ret_first_cont, f_mapnew}, {"mapset", 3, 3, FEARG_1, arg3_string_bool_dict, ret_void, f_mapset}, *** ../vim-8.2.3896/src/globals.h 2021-12-21 12:32:13.296529989 +0000 --- src/globals.h 2021-12-25 20:25:37.776816709 +0000 *************** *** 432,437 **** --- 432,438 ---- EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL); EXTERN type_T t_func_string INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_string, NULL); + EXTERN type_T t_func_bool INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_bool, NULL); EXTERN type_T t_func_0_void INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_void, NULL); EXTERN type_T t_func_0_any INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_any, NULL); EXTERN type_T t_func_0_number INIT6(VAR_FUNC, 0, 0, TTFLAG_STATIC, &t_number, NULL); *** ../vim-8.2.3896/src/list.c 2021-12-22 18:19:22.602372473 +0000 --- src/list.c 2021-12-25 20:44:19.638195911 +0000 *************** *** 2280,2286 **** // filter(): when expr is zero remove the item if (in_vim9script()) ! *remp = !tv2bool(newtv); else *remp = (tv_get_number_chk(newtv, &error) == 0); clear_tv(newtv); --- 2280,2286 ---- // filter(): when expr is zero remove the item if (in_vim9script()) ! *remp = !tv_get_bool_chk(newtv, &error); else *remp = (tv_get_number_chk(newtv, &error) == 0); clear_tv(newtv); *** ../vim-8.2.3896/src/testdir/test_vim9_builtin.vim 2021-12-25 19:43:39.953098179 +0000 --- src/testdir/test_vim9_builtin.vim 2021-12-25 21:24:56.018053312 +0000 *************** *** 1241,1246 **** --- 1241,1255 ---- def Test_filter() CheckDefAndScriptFailure(['filter(1.1, "1")'], ['E1013: Argument 1: type mismatch, expected list but got float', 'E1251: List, Dictionary, Blob or String required for argument 1']) + + var lines =<< trim END + def F(i: number, v: any): string + return 'bad' + enddef + echo filter([1, 2, 3], F) + END + CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): bool', 'E1135: Using a String as a Bool:']) + assert_equal([], filter([1, 2, 3], '0')) assert_equal([1, 2, 3], filter([1, 2, 3], '1')) assert_equal({b: 20}, filter({a: 10, b: 20}, 'v:val == 20')) *************** *** 2141,2146 **** --- 2150,2163 ---- range(3)->map((a, b, c, d) => a + b + c + d) END CheckDefExecAndScriptFailure(lines, 'E1190: 2 arguments too few') + + lines =<< trim END + def Map(i: number, v: number): string + return 'bad' + enddef + echo map([1, 2, 3], Map) + END + CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(number, number): string', 'E1012: Type mismatch; expected number but got string in map()']) enddef def Test_map_item_type() *************** *** 2155,2173 **** var l: list = [0] echo map(l, (_, v) => []) END ! CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) lines =<< trim END var l: list = range(2) echo map(l, (_, v) => []) END ! CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) lines =<< trim END var d: dict = {key: 0} echo map(d, (_, v) => []) END ! CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) enddef def Test_maparg() --- 2172,2190 ---- var l: list = [0] echo map(l, (_, v) => []) END ! CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(any, any): list', 'E1012: Type mismatch; expected number but got list in map()'], 2) lines =<< trim END var l: list = range(2) echo map(l, (_, v) => []) END ! CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(any, any): list', 'E1012: Type mismatch; expected number but got list in map()'], 2) lines =<< trim END var d: dict = {key: 0} echo map(d, (_, v) => []) END ! CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(...): number but got func(any, any): list', 'E1012: Type mismatch; expected number but got list in map()'], 2) enddef def Test_maparg() *** ../vim-8.2.3896/src/testdir/test_vim9_expr.vim 2021-12-24 21:36:08.428028943 +0000 --- src/testdir/test_vim9_expr.vim 2021-12-25 21:30:35.573497804 +0000 *************** *** 2077,2083 **** ) assert_equal([111, 222, 111], ll) ! var dl = [{key: 0}, {key: 22}]->filter(( _, v) => v['key'] ) assert_equal([{key: 22}], dl) dl = [{key: 12}, {['foo']: 34}] --- 2077,2083 ---- ) assert_equal([111, 222, 111], ll) ! var dl = [{key: 0}, {key: 22}]->filter(( _, v) => !!v['key'] ) assert_equal([{key: 22}], dl) dl = [{key: 12}, {['foo']: 34}] *************** *** 2236,2242 **** ) assert_equal([111, 222, 111], ll) ! var dl = [{key: 0}, {key: 22}]->filter(( _, v) => v['key'] ) assert_equal([{key: 22}], dl) dl = [{key: 12}, {['foo']: 34}] --- 2236,2242 ---- ) assert_equal([111, 222, 111], ll) ! var dl = [{key: 0}, {key: 22}]->filter(( _, v) => !!v['key'] ) assert_equal([{key: 22}], dl) dl = [{key: 12}, {['foo']: 34}] *************** *** 2308,2314 **** lines =<< trim END search('"', 'cW', 0, 0, () => synstack('.', col('.')) ! ->map((_, v) => synIDattr(v, 'name'))->len()) END CheckDefAndScriptSuccess(lines) enddef --- 2308,2314 ---- lines =<< trim END search('"', 'cW', 0, 0, () => synstack('.', col('.')) ! ->mapnew((_, v) => synIDattr(v, 'name'))->len()) END CheckDefAndScriptSuccess(lines) enddef *** ../vim-8.2.3896/src/testdir/test_vim9_script.vim 2021-12-23 21:14:34.368204908 +0000 --- src/testdir/test_vim9_script.vim 2021-12-25 21:41:04.688507452 +0000 *************** *** 2048,2054 **** return idx % 2 == 1 enddef export def FastFilter(): list ! return range(10)->filter('FilterFunc') enddef export def FastFilterDirect(): list return range(10)->filter(FilterFunc) --- 2048,2054 ---- return idx % 2 == 1 enddef export def FastFilter(): list ! return range(10)->filter('FilterFunc(v:key, v:val)') enddef export def FastFilterDirect(): list return range(10)->filter(FilterFunc) *** ../vim-8.2.3896/src/version.c 2021-12-25 19:58:17.495819172 +0000 --- src/version.c 2021-12-25 20:14:57.433787365 +0000 *************** *** 751,752 **** --- 751,754 ---- { /* Add new patch number below this line */ + /**/ + 3897, /**/ -- hundred-and-one symptoms of being an internet addict: 107. When using your phone you forget that you don't have to use your keyboard. /// 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 ///