summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2023-05-15 16:22:38 +0100
committerBram Moolenaar <Bram@vim.org>2023-05-15 16:22:38 +0100
commit2ba51236fb7c27fe16dad4c44242f00e17c19726 (patch)
treec394d7f9f7ca93244bbb169a4c42c6e1870f385e
parenta2c0028fdf8dcf0408e27be730ac0e691ef9559b (diff)
downloadvim-git-2ba51236fb7c27fe16dad4c44242f00e17c19726.tar.gz
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.vim32
-rw-r--r--src/userfunc.c62
-rw-r--r--src/version.c2
-rw-r--r--src/vim9type.c5
4 files changed, 89 insertions, 12 deletions
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index ffb8de48d..ecdbd5eac 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 e63caf9fe..6f0d59dfd 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 689b1a2c7..47079f533 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 95252dbaf..0dd74ee18 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;