From 16f6c8ac94d8412075060945aa90ba90be08656f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 22 Feb 2022 15:12:14 +0000 Subject: [PATCH] patch 8.2.4441: Vim9: function argument of filter() not checked like map() Problem: Vim9: function argument of filter() not checked like map(). Solution: Also check the function argument of filter(). --- src/evalfunc.c | 166 ++++++++++++++---------------- src/testdir/test_vim9_builtin.vim | 62 +++++++++-- src/version.c | 2 + 3 files changed, 135 insertions(+), 95 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 38a7aa27c..69b4a23ac 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -486,41 +486,84 @@ arg_list_or_dict_or_blob_or_string(type_T *type, type_T *decl_type UNUSED, argco } /* - * Check second argument of filter(): func must return a bool. + * Check second argument of map() or filter(). */ static int -arg_filter_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +check_map_filter_arg2(type_T *type, argcontext_T *context, int is_map) { - if (type->tt_type == VAR_STRING - || type->tt_type == VAR_PARTIAL - || type == &t_unknown - || type == &t_any) - return OK; + type_T *expected_member = NULL; + type_T *(args[2]); + type_T t_func_exp = {VAR_FUNC, 2, 0, 0, NULL, args}; - if (type->tt_type == VAR_FUNC) + if (context->arg_types[0].type_curr->tt_type == VAR_LIST + || context->arg_types[0].type_curr->tt_type == VAR_DICT) { - 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)) + // 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)))) { - arg_type_mismatch(&t_func_bool, type, context->arg_idx + 1); + 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; + } } - else + + if ((type->tt_member != &t_any && type->tt_member != &t_unknown) + || args[0] != NULL) { - semsg(_(e_string_or_function_required_for_argument_nr), 2); - return FAIL; + 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 map(), the function. + * Check second argument of filter(): func must return a bool. */ static int -arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) +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,76 +572,27 @@ arg_map_func(type_T *type, type_T *decl_type UNUSED, argcontext_T *context) 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; + return check_map_filter_arg2(type, context, FALSE); + semsg(_(e_string_or_function_required_for_argument_nr), 2); + return FAIL; +} - 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; +/* + * 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; - 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; + 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; } /* diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 41060e1a2..b91e9f2dc 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -1315,6 +1315,16 @@ def Wrong_dict_key_type(items: list): list 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,17 +1334,51 @@ def Test_filter() 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:']) + 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:']) - 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')) + # 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']) - def GetFiltered(): list - var Odd: func = (_, v) => v % 2 - return range(3)->filter(Odd) - enddef - assert_equal([1], GetFiltered()) + # 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() diff --git a/src/version.c b/src/version.c index 809b03f3e..000183401 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4441, /**/ 4440, /**/ -- 2.40.0