Patch 8.2.4441

6 views
Skip to first unread message

Bram Moolenaar

unread,
Feb 22, 2022, 10:12:49 AM2/22/22
to vim...@googlegroups.com

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<number>
+ 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<any> 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<number>
! 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 -- Br...@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 ///
Reply all
Reply to author
Forward
0 new messages