# HG changeset patch # User Bram Moolenaar # Date 1626025503 -7200 # Node ID a703b3f28ef4e730ac8eb14ab02f10f778f41ffd # Parent 51804c8bad5c1b8bab325428ff7a468ce545a950 patch 8.2.3150: Vim9: argument types are not checked at compile time Commit: https://github.com/vim/vim/commit/c72bdd28ac5fe079825155930af8e792580139bb Author: Yegappan Lakshmanan Date: Sun Jul 11 19:44:18 2021 +0200 patch 8.2.3150: Vim9: argument types are not checked at compile time Problem: Vim9: argument types are not checked at compile time. Solution: Add more type checks. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/8545) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -322,7 +322,7 @@ arg_string_or_nr(type_T *type, argcontex * Check "type" is a string or a list of strings. */ static int -arg_string_or_list(type_T *type, argcontext_T *context) +arg_string_or_list_string(type_T *type, argcontext_T *context) { if (type->tt_type == VAR_ANY || type->tt_type == VAR_STRING) return OK; @@ -340,6 +340,19 @@ arg_string_or_list(type_T *type, argcont } /* + * Check "type" is a string or a list of 'any' + */ + static int +arg_string_or_list_any(type_T *type, argcontext_T *context) +{ + if (type->tt_type == VAR_ANY + || type->tt_type == VAR_STRING || type->tt_type == VAR_LIST) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* * Check "type" is a list or a dict. */ static int @@ -413,6 +426,20 @@ arg_item_of_prev(type_T *type, argcontex } /* + * Check "type" is a string or a number or a list + */ + static int +arg_str_or_nr_or_list(type_T *type, argcontext_T *context) +{ + if (type->tt_type == VAR_STRING + || type->tt_type == VAR_NUMBER + || type->tt_type == VAR_LIST) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} + +/* * Check "type" which is the third argument of extend(). */ static int @@ -438,7 +465,8 @@ argcheck_T arg1_list_nr[] = {arg_list_nu argcheck_T arg1_list_string[] = {arg_list_string}; argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; argcheck_T arg1_string_or_nr[] = {arg_string_or_nr}; -argcheck_T arg1_string_or_list[] = {arg_string_or_list}; +argcheck_T arg1_string_or_list_any[] = {arg_string_or_list_any}; +argcheck_T arg1_string_or_list_string[] = {arg_string_or_list_string}; argcheck_T arg1_list_or_blob[] = {arg_list_or_blob}; argcheck_T arg1_chan_or_job[] = {arg_chan_or_job}; argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; @@ -449,14 +477,18 @@ argcheck_T arg2_dict_string[] = {arg_dic argcheck_T arg2_dict_string_or_nr[] = {arg_dict_any, arg_string_or_nr}; argcheck_T arg2_string_dict[] = {arg_string, arg_dict_any}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; -argcheck_T arg2_execute[] = {arg_string_or_list, arg_string}; -argcheck_T arg23_win_execute[] = {arg_number, arg_string_or_list, arg_string}; -argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; -argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; +argcheck_T arg2_str_or_nr_or_list_dict[] = {arg_str_or_nr_or_list, arg_dict_any}; +argcheck_T arg2_string_or_list_dict[] = {arg_string_or_list_any, arg_dict_any}; argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; argcheck_T arg3_number[] = {arg_number, arg_number, arg_number}; argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; argcheck_T arg3_string_string_nr[] = {arg_string, arg_string, arg_number}; +argcheck_T arg2_execute[] = {arg_string_or_list_string, arg_string}; +argcheck_T arg23_win_execute[] = {arg_number, arg_string_or_list_string, arg_string}; +argcheck_T arg2_setline[] = {arg_string_or_nr, arg_string_or_list_any}; +argcheck_T arg3_setbufline[] = {arg_string_or_nr, arg_string_or_nr, arg_string_or_list_any}; +argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; +argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; /* @@ -765,7 +797,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_assert_notequal}, {"assert_notmatch", 2, 3, FEARG_2, arg3_string, ret_number_bool, f_assert_notmatch}, - {"assert_report", 1, 1, FEARG_1, NULL, + {"assert_report", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_assert_report}, {"assert_true", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_true}, @@ -781,7 +813,7 @@ static funcentry_T global_functions[] = NULL #endif }, - {"balloon_show", 1, 1, FEARG_1, arg1_string_or_list, + {"balloon_show", 1, 1, FEARG_1, arg1_string_or_list_any, ret_void, #ifdef FEAT_BEVAL f_balloon_show @@ -877,7 +909,7 @@ static funcentry_T global_functions[] = ret_number, f_char2nr}, {"charclass", 1, 1, FEARG_1, arg1_string, ret_number, f_charclass}, - {"charcol", 1, 1, FEARG_1, arg1_string_or_list, + {"charcol", 1, 1, FEARG_1, arg1_string_or_list_any, ret_number, f_charcol}, {"charidx", 2, 3, FEARG_1, arg3_string_nr_bool, ret_number, f_charidx}, @@ -887,7 +919,7 @@ static funcentry_T global_functions[] = ret_number, f_cindent}, {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, - {"col", 1, 1, FEARG_1, arg1_string_or_list, + {"col", 1, 1, FEARG_1, arg1_string_or_list_any, ret_number, f_col}, {"complete", 2, 2, FEARG_2, NULL, ret_void, f_complete}, @@ -1301,17 +1333,17 @@ static funcentry_T global_functions[] = NULL #endif }, - {"popup_atcursor", 2, 2, FEARG_1, NULL, + {"popup_atcursor", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_atcursor)}, - {"popup_beval", 2, 2, FEARG_1, NULL, + {"popup_beval", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_beval)}, {"popup_clear", 0, 1, 0, NULL, ret_void, PROP_FUNC(f_popup_clear)}, {"popup_close", 1, 2, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_close)}, - {"popup_create", 2, 2, FEARG_1, NULL, + {"popup_create", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_create)}, - {"popup_dialog", 2, 2, FEARG_1, NULL, + {"popup_dialog", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_dialog)}, {"popup_filter_menu", 2, 2, 0, NULL, ret_bool, PROP_FUNC(f_popup_filter_menu)}, @@ -1331,11 +1363,11 @@ static funcentry_T global_functions[] = ret_list_number, PROP_FUNC(f_popup_list)}, {"popup_locate", 2, 2, 0, arg2_number, ret_number, PROP_FUNC(f_popup_locate)}, - {"popup_menu", 2, 2, FEARG_1, NULL, + {"popup_menu", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_menu)}, {"popup_move", 2, 2, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_move)}, - {"popup_notification", 2, 2, FEARG_1, NULL, + {"popup_notification", 2, 2, FEARG_1, arg2_str_or_nr_or_list_dict, ret_number, PROP_FUNC(f_popup_notification)}, {"popup_setoptions", 2, 2, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_setoptions)}, @@ -1541,7 +1573,7 @@ static funcentry_T global_functions[] = ret_string, f_shellescape}, {"shiftwidth", 0, 1, FEARG_1, arg1_number, ret_number, f_shiftwidth}, - {"sign_define", 1, 2, FEARG_1, arg2_string_dict, + {"sign_define", 1, 2, FEARG_1, arg2_string_or_list_dict, ret_any, SIGN_FUNC(f_sign_define)}, {"sign_getdefined", 0, 1, FEARG_1, NULL, ret_list_dict_any, SIGN_FUNC(f_sign_getdefined)}, @@ -1553,7 +1585,7 @@ static funcentry_T global_functions[] = ret_number, SIGN_FUNC(f_sign_place)}, {"sign_placelist", 1, 1, FEARG_1, NULL, ret_list_number, SIGN_FUNC(f_sign_placelist)}, - {"sign_undefine", 0, 1, FEARG_1, arg1_string_or_list, + {"sign_undefine", 0, 1, FEARG_1, arg1_string_or_list_string, ret_number_bool, SIGN_FUNC(f_sign_undefine)}, {"sign_unplace", 1, 2, FEARG_1, arg2_string_dict, ret_number_bool, SIGN_FUNC(f_sign_unplace)}, @@ -1827,7 +1859,7 @@ static funcentry_T global_functions[] = ret_list_any, f_uniq}, {"values", 1, 1, FEARG_1, arg1_dict, ret_list_any, f_values}, - {"virtcol", 1, 1, FEARG_1, arg1_string_or_list, + {"virtcol", 1, 1, FEARG_1, arg1_string_or_list_any, ret_number, f_virtcol}, {"visualmode", 0, 1, 0, NULL, ret_string, f_visualmode}, 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 @@ -213,12 +213,19 @@ def Test_assert_notmatch() CheckDefFailure(['assert_notmatch("a", "b", null)'], 'E1013: Argument 3: type mismatch, expected string but got special') enddef +def Test_assert_report() + CheckDefAndScriptFailure2(['assert_report([1, 2])'], 'E1013: Argument 1: type mismatch, expected string but got list', 'E1174: String required for argument 1') +enddef + def Test_balloon_show() CheckGui CheckFeature balloon_eval assert_fails('balloon_show(10)', 'E1174:') assert_fails('balloon_show(true)', 'E1174:') + + CheckDefAndScriptFailure2(['balloon_show(1.2)'], 'E1013: Argument 1: type mismatch, expected string but got float', 'E1174: String required for argument 1') + CheckDefAndScriptFailure2(['balloon_show({"a": 10})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E1174: String required for argument 1') enddef def Test_balloon_split() @@ -387,6 +394,13 @@ enddef def Test_charcol() CheckDefFailure(['charcol(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') CheckDefFailure(['charcol({a: 10})'], 'E1013: Argument 1: type mismatch, expected string but got dict') + new + setline(1, ['abcdefgh']) + cursor(1, 4) + assert_equal(4, charcol('.')) + assert_equal(9, charcol([1, '$'])) + assert_equal(0, charcol([10, '$'])) + bw! enddef def Test_charidx() @@ -412,8 +426,11 @@ enddef def Test_col() new - setline(1, 'asdf') - col([1, '$'])->assert_equal(5) + setline(1, 'abcdefgh') + cursor(1, 4) + assert_equal(4, col('.')) + col([1, '$'])->assert_equal(9) + assert_equal(0, col([10, '$'])) assert_fails('col(true)', 'E1174:') @@ -1503,11 +1520,36 @@ def Test_or() CheckDefFailure(['or(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') enddef +def Test_popup_atcursor() + CheckDefAndScriptFailure2(['popup_atcursor({"a": 10}, {})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E450: buffer number, text or a list required') + CheckDefAndScriptFailure2(['popup_atcursor("a", [1, 2])'], 'E1013: Argument 2: type mismatch, expected dict but got list', 'E715: Dictionary required') +enddef + +def Test_popup_beval() + CheckDefAndScriptFailure2(['popup_beval({"a": 10}, {})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E450: buffer number, text or a list required') + CheckDefAndScriptFailure2(['popup_beval("a", [1, 2])'], 'E1013: Argument 2: type mismatch, expected dict but got list', 'E715: Dictionary required') +enddef + +def Test_popup_dialog() + CheckDefAndScriptFailure2(['popup_dialog({"a": 10}, {})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E450: buffer number, text or a list required') + CheckDefAndScriptFailure2(['popup_dialog("a", [1, 2])'], 'E1013: Argument 2: type mismatch, expected dict but got list', 'E715: Dictionary required') +enddef + def Test_popup_locate() CheckDefAndScriptFailure2(['popup_locate("a", 20)'], 'E1013: Argument 1: type mismatch, expected number but got string', 'E1030: Using a String as a Number') CheckDefAndScriptFailure2(['popup_locate(10, "b")'], 'E1013: Argument 2: type mismatch, expected number but got string', 'E1030: Using a String as a Number') enddef +def Test_popup_menu() + CheckDefAndScriptFailure2(['popup_menu({"a": 10}, {})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E450: buffer number, text or a list required') + CheckDefAndScriptFailure2(['popup_menu("a", [1, 2])'], 'E1013: Argument 2: type mismatch, expected dict but got list', 'E715: Dictionary required') +enddef + +def Test_popup_notification() + CheckDefAndScriptFailure2(['popup_notification({"a": 10}, {})'], 'E1013: Argument 1: type mismatch, expected string but got dict', 'E450: buffer number, text or a list required') + CheckDefAndScriptFailure2(['popup_notification("a", [1, 2])'], 'E1013: Argument 2: type mismatch, expected dict but got list', 'E715: Dictionary required') +enddef + def Test_prevnonblank() CheckDefFailure(['prevnonblank(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') assert_equal(0, prevnonblank(1)) @@ -1887,6 +1929,17 @@ def Test_setfperm() CheckDefFailure(['setfperm("a", 0z10)'], 'E1013: Argument 2: type mismatch, expected string but got blob') enddef +def Test_setline() + new + setline(1, range(1, 4)) + assert_equal(['1', '2', '3', '4'], getline(1, '$')) + setline(1, ['a', 'b', 'c', 'd']) + assert_equal(['a', 'b', 'c', 'd'], getline(1, '$')) + setline(1, 'one') + assert_equal(['one', 'b', 'c', 'd'], getline(1, '$')) + bw! +enddef + def Test_setloclist() var items = [{filename: '/tmp/file', lnum: 1, valid: true}] var what = {items: items} @@ -2301,6 +2354,13 @@ enddef def Test_virtcol() CheckDefAndScriptFailure2(['virtcol(1.1)'], 'E1013: Argument 1: type mismatch, expected string but got float', 'E1174: String required for argument 1') + new + setline(1, ['abcdefgh']) + cursor(1, 4) + assert_equal(4, virtcol('.')) + assert_equal(9, virtcol([1, '$'])) + assert_equal(0, virtcol([10, '$'])) + bw! enddef def Test_win_execute() diff --git a/src/testing.c b/src/testing.c --- a/src/testing.c +++ b/src/testing.c @@ -824,6 +824,9 @@ f_assert_report(typval_T *argvars, typva { garray_T ga; + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) + return; + prepare_assert_error(&ga); ga_concat(&ga, tv_get_string(&argvars[0])); assert_error(&ga); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3150, +/**/ 3149, /**/ 3148,