diff options
author | Bram Moolenaar <Bram@vim.org> | 2023-05-15 16:22:38 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2023-05-15 16:22:38 +0100 |
commit | 2ba51236fb7c27fe16dad4c44242f00e17c19726 (patch) | |
tree | c394d7f9f7ca93244bbb169a4c42c6e1870f385e | |
parent | a2c0028fdf8dcf0408e27be730ac0e691ef9559b (diff) |
patch 9.0.1559: function argument types not always checkedv9.0.1559
Problem: Function argument types not always checked and using v:none may
cause an error.
Solution: Check argument types once the function type is known. Do not give
an error for using v:none as an argument. (closes #12200)
-rw-r--r-- | src/testdir/test_vim9_func.vim | 32 | ||||
-rw-r--r-- | src/userfunc.c | 62 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9type.c | 5 |
4 files changed, 89 insertions, 12 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index ffb8de48dd..ecdbd5eaca 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -778,6 +778,38 @@ def Test_using_vnone_default() END v9.CheckScriptSuccess(lines) + lines =<< trim END + vim9script + + export def Floats(x: float, y = 2.0, z = 5.0) + g:result = printf("%.2f %.2f %.2f", x, y, z) + enddef + END + writefile(lines, 'Xlib.vim', 'D') + + # test using a function reference in script-local variable + lines =<< trim END + vim9script + + import './Xlib.vim' + const Floatfunc = Xlib.Floats + Floatfunc(1.0, v:none, 3.0) + END + v9.CheckScriptSuccess(lines) + assert_equal('1.00 2.00 3.00', g:result) + unlet g:result + + # test calling the imported function + lines =<< trim END + vim9script + + import './Xlib.vim' + Xlib.Floats(1.0, v:none, 3.0) + END + v9.CheckScriptSuccess(lines) + assert_equal('1.00 2.00 3.00', g:result) + unlet g:result + # TODO: this should give an error for using a missing argument # lines =<< trim END # vim9script diff --git a/src/userfunc.c b/src/userfunc.c index e63caf9fed..6f0d59dfd3 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3596,6 +3596,34 @@ user_func_error(funcerror_T error, char_u *name, int found_var) } /* + * Check the argument types "argvars[argcount]" for "name" using the + * information in "funcexe". When "base_included" then "funcexe->fe_basetv" + * is already included in "argvars[]". + * Will do nothing if "funcexe->fe_check_type" is NULL or + * "funcexe->fe_evaluate" is FALSE; + * Returns an FCERR_ value. + */ + static funcerror_T +may_check_argument_types( + funcexe_T *funcexe, + typval_T *argvars, + int argcount, + int base_included, + char_u *name) +{ + if (funcexe->fe_check_type != NULL && funcexe->fe_evaluate) + { + // Check that the argument types are OK for the types of the funcref. + if (check_argument_types(funcexe->fe_check_type, + argvars, argcount, + base_included ? NULL : funcexe->fe_basetv, + name) == FAIL) + return FCERR_OTHER; + } + return FCERR_NONE; +} + +/* * Call a function with its resolved parameters * * Return FAIL when the function can't be called, OK otherwise. @@ -3691,15 +3719,10 @@ call_func( } } - if (error == FCERR_NONE && funcexe->fe_check_type != NULL - && funcexe->fe_evaluate) - { - // Check that the argument types are OK for the types of the funcref. - if (check_argument_types(funcexe->fe_check_type, - argvars, argcount, funcexe->fe_basetv, - (name != NULL) ? name : funcname) == FAIL) - error = FCERR_OTHER; - } + if (error == FCERR_NONE) + // check the argument types if possible + error = may_check_argument_types(funcexe, argvars, argcount, FALSE, + (name != NULL) ? name : funcname); if (error == FCERR_NONE && funcexe->fe_evaluate) { @@ -3761,10 +3784,20 @@ call_func( error = FCERR_DELETED; else if (fp != NULL) { + int need_arg_check = FALSE; + if (funcexe->fe_check_type == NULL) + { + funcexe->fe_check_type = fp->uf_func_type; + need_arg_check = TRUE; + } + if (funcexe->fe_argv_func != NULL) + { // postponed filling in the arguments, do it now argcount = funcexe->fe_argv_func(argcount, argvars, - argv_clear, fp); + argv_clear, fp); + need_arg_check = TRUE; + } if (funcexe->fe_basetv != NULL) { @@ -3774,9 +3807,16 @@ call_func( argcount++; argvars = argv; argv_base = 1; + need_arg_check = TRUE; } - error = call_user_func_check(fp, argcount, argvars, rettv, + // Check the argument types now that the function type and all + // argument values are known, if not done above. + if (need_arg_check) + error = may_check_argument_types(funcexe, argvars, argcount, + TRUE, (name != NULL) ? name : funcname); + if (error == FCERR_NONE || error == FCERR_UNKNOWN) + error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict); } } diff --git a/src/version.c b/src/version.c index 689b1a2c75..47079f5331 100644 --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1559, +/**/ 1558, /**/ 1557, diff --git a/src/vim9type.c b/src/vim9type.c index 95252dbafe..0dd74ee184 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -970,7 +970,10 @@ check_argument_types( } else expected = type->tt_args[i]; - if (check_typval_arg_type(expected, tv, NULL, i + 1) == FAIL) + + // check the type, unless the value is v:none + if ((tv->v_type != VAR_SPECIAL || tv->vval.v_number != VVAL_NONE) + && check_typval_arg_type(expected, tv, NULL, i + 1) == FAIL) return FAIL; } return OK; |