# HG changeset patch # User Bram Moolenaar # Date 1610820904 -3600 # Node ID 5d77a758792707f7532b159976b9924b80c28cb1 # Parent 541984a4d2e7c4f9a5503fce8ffdbeed00c22cbe patch 8.2.2365: Vim9: no check for map() changing item type at script level Commit: https://github.com/vim/vim/commit/70250fb4d2ffc2e92db224c6374db418f70691fd Author: Bram Moolenaar Date: Sat Jan 16 19:01:53 2021 +0100 patch 8.2.2365: Vim9: no check for map() changing item type at script level Problem: Vim9: no check for map() changing item type at script level. Solution: Check the new value type. diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -1985,10 +1985,18 @@ filter_map(typval_T *argvars, typval_T * : N_("filter() argument")); int save_did_emsg; int idx = 0; + type_T *type = NULL; + garray_T type_list; // map() and filter() return the first argument, also on failure. if (filtermap != FILTERMAP_MAPNEW) copy_tv(&argvars[0], rettv); + if (filtermap == FILTERMAP_MAP && in_vim9script()) + { + // Check that map() does not change the type of the dict. + ga_init2(&type_list, sizeof(type_T *), 10); + type = typval2type(argvars, &type_list); + } if (argvars[0].v_type == VAR_BLOB) { @@ -1998,7 +2006,7 @@ filter_map(typval_T *argvars, typval_T * rettv->vval.v_blob = NULL; } if ((b = argvars[0].vval.v_blob) == NULL) - return; + goto theend; } else if (argvars[0].v_type == VAR_LIST) { @@ -2010,7 +2018,7 @@ filter_map(typval_T *argvars, typval_T * if ((l = argvars[0].vval.v_list) == NULL || (filtermap == FILTERMAP_FILTER && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) - return; + goto theend; } else if (argvars[0].v_type == VAR_DICT) { @@ -2022,12 +2030,12 @@ filter_map(typval_T *argvars, typval_T * if ((d = argvars[0].vval.v_dict) == NULL || (filtermap == FILTERMAP_FILTER && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) - return; + goto theend; } else { semsg(_(e_listdictblobarg), ermsg); - return; + goto theend; } expr = &argvars[1]; @@ -2055,7 +2063,7 @@ filter_map(typval_T *argvars, typval_T * if (filtermap == FILTERMAP_MAPNEW) { if (rettv_dict_alloc(rettv) == FAIL) - return; + goto theend; d_ret = rettv->vval.v_dict; } @@ -2090,6 +2098,12 @@ filter_map(typval_T *argvars, typval_T * } if (filtermap == FILTERMAP_MAP) { + if (type != NULL && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(): replace the dict item value clear_tv(&di->di_tv); newtv.v_lock = 0; @@ -2126,7 +2140,7 @@ filter_map(typval_T *argvars, typval_T * if (filtermap == FILTERMAP_MAPNEW) { if (blob_copy(b, rettv) == FAIL) - return; + goto theend; b_ret = rettv->vval.v_blob; } @@ -2175,7 +2189,7 @@ filter_map(typval_T *argvars, typval_T * if (filtermap == FILTERMAP_MAPNEW) { if (rettv_list_alloc(rettv) == FAIL) - return; + goto theend; l_ret = rettv->vval.v_list; } // set_vim_var_nr() doesn't set the type @@ -2218,6 +2232,13 @@ filter_map(typval_T *argvars, typval_T * } if (filtermap != FILTERMAP_FILTER) { + if (filtermap == FILTERMAP_MAP && type != NULL + && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(), mapnew(): always append the new value to the // list if (list_append_tv_move(filtermap == FILTERMAP_MAP @@ -2256,6 +2277,12 @@ filter_map(typval_T *argvars, typval_T * } if (filtermap == FILTERMAP_MAP) { + if (type != NULL && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(): replace the list item value clear_tv(&li->li_tv); newtv.v_lock = 0; @@ -2281,6 +2308,10 @@ filter_map(typval_T *argvars, typval_T * did_emsg |= save_did_emsg; } + +theend: + if (type != NULL) + clear_type_list(&type_list); } /* diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1351,7 +1351,7 @@ def Test_var_list_dict_type() var ll: list ll = [1, 2, 3]->map('"one"') END - CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list but got list') + CheckDefExecFailure(lines, 'E1012: Type mismatch; expected number but got string') enddef def Test_cannot_use_let() diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -331,27 +331,6 @@ def Wrong_dict_key_type(items: list get({[val]: 1}, 'x')) enddef -def Test_map_function_arg() - var lines =<< trim END - def MapOne(i: number, v: string): string - return i .. ':' .. v - enddef - var l = ['a', 'b', 'c'] - map(l, MapOne) - assert_equal(['0:a', '1:b', '2:c'], l) - END - CheckDefAndScriptSuccess(lines) -enddef - -def Test_map_item_type() - var lines =<< trim END - var l = ['a', 'b', 'c'] - map(l, (k, v) => k .. '/' .. v ) - assert_equal(['0/a', '1/b', '2/c'], l) - END - CheckDefAndScriptSuccess(lines) -enddef - def Test_filereadable() assert_false(filereadable("")) assert_false(filereadable(test_null_string())) @@ -584,6 +563,45 @@ def SID(): number ->str2nr() enddef +def Test_map_function_arg() + var lines =<< trim END + def MapOne(i: number, v: string): string + return i .. ':' .. v + enddef + var l = ['a', 'b', 'c'] + map(l, MapOne) + assert_equal(['0:a', '1:b', '2:c'], l) + END + CheckDefAndScriptSuccess(lines) +enddef + +def Test_map_item_type() + var lines =<< trim END + var l = ['a', 'b', 'c'] + map(l, (k, v) => k .. '/' .. v ) + assert_equal(['0/a', '1/b', '2/c'], l) + END + CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var l: list = [0] + echo map(l, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + + lines =<< trim END + var l: list = range(2) + echo map(l, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + + lines =<< trim END + var d: dict = {key: 0} + echo map(d, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) +enddef + def Test_maparg() var lnum = str2nr(expand('')) map foo bar diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2365, +/**/ 2364, /**/ 2363,