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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220222151243.BB01F1C14C0%40moolenaar.net.

Raspunde prin e-mail lui