To: vim_dev@googlegroups.com Subject: Patch 8.2.4441 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4441 Problem: Vim9: function argument of filter() not checked like map(). Solution: Also check the function argument of filter(). Files: src/evalfunc.c, src/testdir/test_vim9_builtin.vim *** ../vim-8.2.4440/src/evalfunc.c 2022-02-21 18:34:25.874252449 +0000 --- src/evalfunc.c 2022-02-22 14:53:37.344910004 +0000 *************** *** 486,526 **** } /* ! * Check second argument of filter(): func must return a bool. */ static int ! arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { ! if (type->tt_type == VAR_STRING ! || type->tt_type == VAR_PARTIAL ! || type == &t_unknown ! || type == &t_any) ! return OK; ! if (type->tt_type == VAR_FUNC) { ! if (!(type->tt_member->tt_type == VAR_BOOL ! || type->tt_member->tt_type == VAR_NUMBER ! || type->tt_member->tt_type == VAR_UNKNOWN ! || type->tt_member->tt_type == VAR_ANY)) { ! arg_type_mismatch(&t_func_bool, type, context->arg_idx + 1); return FAIL; } } ! else { ! semsg(_(e_string_or_function_required_for_argument_nr), 2); ! return FAIL; } return OK; } /* ! * Check second argument of map(), the function. */ static int ! arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_PARTIAL --- 486,569 ---- } /* ! * Check second argument of map() or filter(). */ static int ! check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map) { ! type_T *expected_member = NULL; ! type_T *(args[2]); ! type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, args}; ! if (context->arg_types[0].type_curr->tt_type == VAR_LIST ! || context->arg_types[0].type_curr->tt_type == VAR_DICT) ! { ! // Use the declared type if possible, so that an error is given if ! // a declared list changes type, but not if a constant list changes ! // type. ! if (context->arg_types[0].type_decl->tt_type == VAR_LIST ! || context->arg_types[0].type_decl->tt_type == VAR_DICT) ! expected_member = context->arg_types[0].type_decl->tt_member; ! else ! expected_member = context->arg_types[0].type_curr->tt_member; ! } ! else if (context->arg_types[0].type_curr->tt_type == VAR_STRING) ! expected_member = &t_string; ! else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB) ! expected_member = &t_number; ! ! args[0] = NULL; ! args[1] = &t_unknown; ! if (type->tt_argcount != -1) { ! if (!(type->tt_argcount == 2 || (type->tt_argcount == 1 ! && (type->tt_flags & TTFLAG_VARARGS)))) { ! emsg(_(e_invalid_number_of_arguments)); return FAIL; } + if (type->tt_flags & TTFLAG_VARARGS) + // check the argument types at runtime + t_func_exp.tt_argcount = -1; + else + { + if (context->arg_types[0].type_curr->tt_type == VAR_STRING + || context->arg_types[0].type_curr->tt_type == VAR_BLOB + || context->arg_types[0].type_curr->tt_type == VAR_LIST) + args[0] = &t_number; + else if (context->arg_types[0].type_decl->tt_type == VAR_DICT) + args[0] = &t_string; + if (args[0] != NULL) + args[1] = expected_member; + } } ! ! if ((type->tt_member != &t_any && type->tt_member != &t_unknown) ! || args[0] != NULL) { ! where_T where = WHERE_INIT; ! ! if (is_map) ! t_func_exp.tt_member = expected_member == NULL ! || type->tt_member == &t_any ! || type->tt_member == &t_unknown ! ? &t_any : expected_member; ! else ! t_func_exp.tt_member = &t_bool; ! if (args[0] == NULL) ! args[0] = &t_unknown; ! ! where.wt_index = 2; ! return check_type(&t_func_exp, type, TRUE, where); } return OK; } /* ! * Check second argument of filter(): func must return a bool. */ static int ! arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) { if (type->tt_type == VAR_STRING || type->tt_type == VAR_PARTIAL *************** *** 529,604 **** return OK; if (type->tt_type == VAR_FUNC) ! { ! type_T *expected_ret = NULL; ! type_T *(args[2]); ! type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, args}; ! ! if (context->arg_types[0].type_curr->tt_type == VAR_LIST ! || context->arg_types[0].type_curr->tt_type == VAR_DICT) ! { ! // Use the declared type if possible, so that an error is given if ! // a declared list changes type, but not if a constant list changes ! // type. ! if (context->arg_types[0].type_decl->tt_type == VAR_LIST ! || context->arg_types[0].type_decl->tt_type == VAR_DICT) ! expected_ret = context->arg_types[0].type_decl->tt_member; ! else ! expected_ret = context->arg_types[0].type_curr->tt_member; ! } ! else if (context->arg_types[0].type_curr->tt_type == VAR_STRING) ! expected_ret = &t_string; ! else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB) ! expected_ret = &t_number; ! ! args[0] = NULL; ! args[1] = &t_unknown; ! if (type->tt_argcount != -1) ! { ! if (!(type->tt_argcount == 2 || (type->tt_argcount == 1 ! && (type->tt_flags & TTFLAG_VARARGS)))) ! { ! emsg(_(e_invalid_number_of_arguments)); ! return FAIL; ! } ! if (type->tt_flags & TTFLAG_VARARGS) ! // check the argument types at runtime ! t_func_exp.tt_argcount = -1; ! else ! { ! if (context->arg_types[0].type_curr->tt_type == VAR_STRING ! || context->arg_types[0].type_curr->tt_type == VAR_BLOB ! || context->arg_types[0].type_curr->tt_type == VAR_LIST) ! args[0] = &t_number; ! else if (context->arg_types[0].type_decl->tt_type == VAR_DICT) ! args[0] = &t_string; ! if (args[0] != NULL) ! args[1] = expected_ret; ! } ! } ! ! if ((type->tt_member != &t_any && type->tt_member != &t_unknown) ! || args[0] != NULL) ! { ! where_T where = WHERE_INIT; ! t_func_exp.tt_member = expected_ret == NULL ! || type->tt_member == &t_any ! || type->tt_member == &t_unknown ! ? &t_any : expected_ret; ! if (args[0] == NULL) ! args[0] = &t_unknown; ! where.wt_index = 2; ! return check_type(&t_func_exp, type, TRUE, where); ! } ! } ! else ! { ! semsg(_(e_string_or_function_required_for_argument_nr), 2); ! return FAIL; ! } ! return OK; } /* --- 572,598 ---- return OK; if (type->tt_type == VAR_FUNC) ! return check_map_filter_arg2(type, context, FALSE); ! semsg(_(e_string_or_function_required_for_argument_nr), 2); ! return FAIL; ! } ! /* ! * Check second argument of map(), the function. ! */ ! static int ! arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) ! { ! if (type->tt_type == VAR_STRING ! || type->tt_type == VAR_PARTIAL ! || type == &t_unknown ! || type == &t_any) ! return OK; ! if (type->tt_type == VAR_FUNC) ! return check_map_filter_arg2(type, context, TRUE); ! semsg(_(e_string_or_function_required_for_argument_nr), 2); ! return FAIL; } /* *** ../vim-8.2.4440/src/testdir/test_vim9_builtin.vim 2022-02-20 18:26:43.107537899 +0000 --- src/testdir/test_vim9_builtin.vim 2022-02-22 15:11:24.223199599 +0000 *************** *** 1315,1320 **** --- 1315,1330 ---- enddef def Test_filter() + 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')) + + def GetFiltered(): list + var Odd: func = (_, v) => v % 2 + return range(3)->filter(Odd) + enddef + assert_equal([1], GetFiltered()) + v9.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']) v9.CheckDefAndScriptFailure(['filter([1, 2], 4)'], ['E1256: String or function required for argument 2', 'E1024: Using a Number as a String']) *************** *** 1324,1340 **** enddef echo filter([1, 2, 3], F) END ! v9.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')) ! def GetFiltered(): list ! var Odd: func = (_, v) => v % 2 ! return range(3)->filter(Odd) ! enddef ! assert_equal([1], GetFiltered()) enddef def Test_filter_wrong_dict_key_type() --- 1334,1384 ---- enddef echo filter([1, 2, 3], F) END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?any): bool but got func(number, any): string', 'E1135: Using a String as a Bool:']) ! # check first function argument type ! lines =<< trim END ! var l = [1, 2, 3] ! filter(l, (i: string, v: number) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): bool but got func(string, number): bool', 'E1013: Argument 1: type mismatch, expected string but got number']) ! lines =<< trim END ! var d = {a: 1} ! filter(d, (i: number, v: number) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?string, ?number): bool but got func(number, number): bool', 'E1013: Argument 1: type mismatch, expected number but got string']) ! lines =<< trim END ! var b = 0z1122 ! filter(b, (i: string, v: number) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): bool but got func(string, number): bool', 'E1013: Argument 1: type mismatch, expected string but got number']) ! lines =<< trim END ! var s = 'text' ! filter(s, (i: string, v: string) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?string): bool but got func(string, string): bool', 'E1013: Argument 1: type mismatch, expected string but got number']) ! # check second function argument type ! lines =<< trim END ! var l = [1, 2, 3] ! filter(l, (i: number, v: string) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): bool but got func(number, string): bool', 'E1013: Argument 2: type mismatch, expected string but got number']) ! lines =<< trim END ! var d = {a: 1} ! filter(d, (i: string, v: string) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?string, ?number): bool but got func(string, string): bool', 'E1013: Argument 2: type mismatch, expected string but got number']) ! lines =<< trim END ! var b = 0z1122 ! filter(b, (i: number, v: string) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): bool but got func(number, string): bool', 'E1013: Argument 2: type mismatch, expected string but got number']) ! lines =<< trim END ! var s = 'text' ! filter(s, (i: number, v: number) => true) ! END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?string): bool but got func(number, number): bool', 'E1013: Argument 2: type mismatch, expected number but got string']) enddef def Test_filter_wrong_dict_key_type() *** ../vim-8.2.4440/src/version.c 2022-02-22 13:37:26.339255247 +0000 --- src/version.c 2022-02-22 14:55:04.264797631 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4441, /**/ -- From "know your smileys": ...---... SOS /// 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 ///