# HG changeset patch # User Bram Moolenaar # Date 1608495303 -3600 # Node ID 35583da6397e66f36dc2434a6c7bee01fa307977 # Parent 8caba5736a9a52f8f58f14e77bab5abac6e207ca patch 8.2.2172: Vim9: number of arguments is not always checked Commit: https://github.com/vim/vim/commit/5082471f91dd42ed8c35e0f649d0a6572e6fe3fc Author: Bram Moolenaar Date: Sun Dec 20 21:10:17 2020 +0100 patch 8.2.2172: Vim9: number of arguments is not always checked Problem: Vim9: number of arguments is not always checked. (Yegappan Lakshmanan) Solution: Check number of arguments when calling function by name. diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -18,6 +18,7 @@ int funcdepth_increment(void); void funcdepth_decrement(void); int funcdepth_get(void); void funcdepth_restore(int depth); +int check_user_func_argcount(ufunc_T *fp, int argcount); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); void restore_funccal(void); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -470,6 +470,25 @@ def Test_call_wrong_args() delete('Xscript') enddef +def Test_call_funcref_wrong_args() + var head =<< trim END + vim9script + def Func3(a1: string, a2: number, a3: list) + echo a1 .. a2 .. a3[0] + enddef + def Testme() + var funcMap: dict = {func: Func3} + END + var tail =<< trim END + enddef + Testme() + END + CheckScriptSuccess(head + ["funcMap['func']('str', 123, [1, 2, 3])"] + tail) + + CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:') + CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:') +enddef + def Test_call_lambda_args() CheckDefFailure(['echo {i -> 0}()'], 'E119: Not enough arguments for function: {i -> 0}()') diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1312,12 +1312,12 @@ def Test_vim9script_reload_delfunc() # FuncNo() is not redefined writefile(first_lines + nono_lines, 'Xreloaded.vim') source Xreloaded.vim - g:DoCheck() + g:DoCheck(false) # FuncNo() is back writefile(first_lines + withno_lines, 'Xreloaded.vim') source Xreloaded.vim - g:DoCheck() + g:DoCheck(false) delete('Xreloaded.vim') enddef diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1834,6 +1834,22 @@ call_user_func( } /* + * Check the argument count for user function "fp". + * Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise. + */ + int +check_user_func_argcount(ufunc_T *fp, int argcount) +{ + int regular_args = fp->uf_args.ga_len; + + if (argcount < regular_args - fp->uf_def_args.ga_len) + return FCERR_TOOFEW; + else if (!has_varargs(fp) && argcount > regular_args) + return FCERR_TOOMANY; + return FCERR_UNKNOWN; +} + +/* * Call a user function after checking the arguments. */ int @@ -1846,15 +1862,13 @@ call_user_func_check( dict_T *selfdict) { int error; - int regular_args = fp->uf_args.ga_len; if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) *funcexe->doesrange = TRUE; - if (argcount < regular_args - fp->uf_def_args.ga_len) - error = FCERR_TOOFEW; - else if (!has_varargs(fp) && argcount > regular_args) - error = FCERR_TOOMANY; - else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) + error = check_user_func_argcount(fp, argcount); + if (error != FCERR_UNKNOWN) + return error; + if ((fp->uf_flags & FC_DICT) && selfdict == NULL) error = FCERR_DICT; else { 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 */ /**/ + 2172, +/**/ 2171, /**/ 2170, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -606,6 +606,17 @@ call_ufunc(ufunc_T *ufunc, int argcount, return FAIL; if (ufunc->uf_def_status == UF_COMPILED) { + int error = check_user_func_argcount(ufunc, argcount); + + if (error != FCERR_UNKNOWN) + { + if (error == FCERR_TOOMANY) + semsg(_(e_toomanyarg), ufunc->uf_name); + else + semsg(_(e_toofewarg), ufunc->uf_name); + return FAIL; + } + // The function has been compiled, can call it quickly. For a function // that was defined later: we can call it directly next time. if (iptr != NULL)