To: vim_dev@googlegroups.com Subject: Patch 8.2.4425 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4425 Problem: map() function does not check function arguments at compile time. Solution: Give an error if the arguments of a map() function are wrong. Files: src/evalfunc.c, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_func.vim *** ../vim-8.2.4424/src/evalfunc.c 2022-02-16 19:24:03.622162411 +0000 --- src/evalfunc.c 2022-02-20 13:16:01.197214578 +0000 *************** *** 517,523 **** } /* ! * Check second argument of map(). */ static int arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) --- 517,523 ---- } /* ! * Check second argument of map(), the function. */ static int arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) *************** *** 530,564 **** if (type->tt_type == VAR_FUNC) { ! if (type->tt_member != &t_any && type->tt_member != &t_unknown) { ! type_T *expected = NULL; ! 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, 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 = context->arg_types[0].type_decl->tt_member; ! else ! expected = context->arg_types[0].type_curr->tt_member; } ! else if (context->arg_types[0].type_curr->tt_type == VAR_STRING) ! expected = &t_string; ! else if (context->arg_types[0].type_curr->tt_type == VAR_BLOB) ! expected = &t_number; ! if (expected != NULL) { ! type_T t_func_exp = {VAR_FUNC, -1, 0, TTFLAG_STATIC, ! NULL, NULL}; ! ! t_func_exp.tt_member = expected; ! return check_arg_type(&t_func_exp, type, context); } } } else { --- 530,596 ---- 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_decl->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; + return check_arg_type(&t_func_exp, type, context); + + where.wt_index = 2; + return check_type(&t_func_exp, type, TRUE, where); + } } else { *** ../vim-8.2.4424/src/testdir/test_vim9_builtin.vim 2022-02-15 15:37:07.319841654 +0000 --- src/testdir/test_vim9_builtin.vim 2022-02-20 15:43:20.953367937 +0000 *************** *** 2289,2299 **** lines =<< trim END range(3)->map((a, b, c) => a + b + c) END ! v9.CheckDefExecAndScriptFailure(lines, 'E1190: One argument too few') lines =<< trim END range(3)->map((a, b, c, d) => a + b + c + d) END ! v9.CheckDefExecAndScriptFailure(lines, 'E1190: 2 arguments too few') # declared list cannot change type lines =<< trim END --- 2289,2299 ---- lines =<< trim END range(3)->map((a, b, c) => a + b + c) END ! v9.CheckDefAndScriptFailure(lines, ['E176:', 'E1190: One argument too few']) lines =<< trim END range(3)->map((a, b, c, d) => a + b + c + d) END ! v9.CheckDefAndScriptFailure(lines, ['E176:', 'E1190: 2 arguments too few']) # declared list cannot change type lines =<< trim END *************** *** 2303,2309 **** var ll: list = [1, 2, 3] echo map(ll, Map) END ! v9.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()']) # not declared list can change type echo [1, 2, 3]->map((..._) => 'x') --- 2303,2309 ---- var ll: list = [1, 2, 3] echo map(ll, Map) END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): number but got func(number, number): string', 'E1012: Type mismatch; expected number but got string in map()']) # not declared list can change type echo [1, 2, 3]->map((..._) => 'x') *************** *** 2321,2339 **** var l: list = [0] echo map(l, (_, v) => []) END ! v9.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 ! v9.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 ! v9.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() --- 2321,2339 ---- var l: list = [0] echo map(l, (_, v) => []) END ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): 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 ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?number, ?number): 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 ! v9.CheckDefAndScriptFailure(lines, ['E1013: Argument 2: type mismatch, expected func(?string, ?number): number but got func(any, any): list', 'E1012: Type mismatch; expected number but got list in map()'], 2) enddef def Test_maparg() *************** *** 2359,2364 **** --- 2359,2380 ---- v9.CheckDefAndScriptFailure(['maparg("a", "b", 2)'], ['E1013: Argument 3: type mismatch, expected bool but got number', 'E1212: Bool required for argument 3']) v9.CheckDefAndScriptFailure(['maparg("a", "b", true, 2)'], ['E1013: Argument 4: type mismatch, expected bool but got number', 'E1212: Bool required for argument 4']) maparg('')->assert_equal('') + + var lines =<< trim END + var l = [123] + l->map((_, v: string) => 0) + END + v9.CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected func(?number, ?number): number but got func(any, string): number') + + lines =<< trim END + ['x']->map((i: string, v: string) => 'y') + END + v9.CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected func(?number, ?any): any but got func(string, string): string') + + lines =<< trim END + {a: 1}->map((i: number, v: number) => 0) + END + v9.CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected func(?string, ?any): any but got func(number, number): number') enddef def Test_maparg_mapset() *** ../vim-8.2.4424/src/testdir/test_vim9_func.vim 2022-02-17 13:08:22.955193271 +0000 --- src/testdir/test_vim9_func.vim 2022-02-19 21:26:41.511019087 +0000 *************** *** 3732,3743 **** var lines =<< trim END echo [0, 1, 2]->map(() => 123) END ! v9.CheckDefExecAndScriptFailure(lines, 'E1106: 2 arguments too many', 1) lines =<< trim END echo [0, 1, 2]->map((_) => 123) END ! v9.CheckDefExecAndScriptFailure(lines, 'E1106: One argument too many', 1) enddef def Test_closing_brace_at_start_of_line() --- 3732,3743 ---- var lines =<< trim END echo [0, 1, 2]->map(() => 123) END ! v9.CheckDefAndScriptFailure(lines, ['E176:', 'E1106: 2 arguments too many'], 1) lines =<< trim END echo [0, 1, 2]->map((_) => 123) END ! v9.CheckDefAndScriptFailure(lines, ['E176', 'E1106: One argument too many'], 1) enddef def Test_closing_brace_at_start_of_line() *** ../vim-8.2.4424/src/version.c 2022-02-19 16:35:54.255967306 +0000 --- src/version.c 2022-02-19 19:55:21.094378363 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4425, /**/ -- hundred-and-one symptoms of being an internet addict: 84. Books in your bookcase bear the names Bongo, WinSock and Inside OLE /// 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 ///