changeset 19944:3055cd26e139 v8.2.0528

patch 8.2.0528: Vim9: function arguments insufficiently tested Commit: https://github.com/vim/vim/commit/0b76b42d0a09fb6f1ed79cfc153da4edd6154c89 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Apr 7 22:05:08 2020 +0200 patch 8.2.0528: Vim9: function arguments insufficiently tested Problem: Vim9: function arguments insufficiently tested. Solution: Check types. Add more tests. Fix function with varargs only.
author Bram Moolenaar <Bram@vim.org>
date Tue, 07 Apr 2020 22:15:05 +0200
parents a456f90669a4
children 734ada3fa6de
files src/testdir/test_vim9_func.vim src/userfunc.c src/version.c src/vim9compile.c
diffstat 4 files changed, 58 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -130,6 +130,19 @@ def Test_call_def_varargs()
   assert_equal('one,two,three', MyDefVarargs('one', 'two', 'three'))
 enddef
 
+" Only varargs
+def MyVarargsOnly(...args: list<string>): string
+  return join(args, ',')
+enddef
+
+def Test_call_varargs_only()
+  assert_equal('', MyVarargsOnly())
+  assert_equal('one', MyVarargsOnly('one'))
+  assert_equal('one,two', MyVarargsOnly('one', 'two'))
+  call CheckDefFailure(['MyVarargsOnly(1)'], 'E1013: argument 1: type mismatch, expected string but got number')
+  call CheckDefFailure(['MyVarargsOnly("one", 2)'], 'E1013: argument 2: type mismatch, expected string but got number')
+enddef
+
 def Test_using_var_as_arg()
   call writefile(['def Func(x: number)',  'let x = 234', 'enddef'], 'Xdef')
   call assert_fails('so Xdef', 'E1006:')
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3020,7 +3020,7 @@ ex_function(exarg_T *eap)
 
     if (eap->cmdidx == CMD_def)
     {
-	int lnum_save = SOURCING_LNUM;
+	int	lnum_save = SOURCING_LNUM;
 
 	// error messages are for the first function line
 	SOURCING_LNUM = sourcing_lnum_top;
@@ -3034,7 +3034,8 @@ ex_function(exarg_T *eap)
 	    // and uf_va_type.
 	    int len = argtypes.ga_len - (varargs ? 1 : 0);
 
-	    fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
+	    if (len > 0)
+		fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len);
 	    if (fp->uf_arg_types != NULL)
 	    {
 		int	i;
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    528,
+/**/
     527,
 /**/
     526,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -130,6 +130,8 @@ static int compile_expr1(char_u **arg,  
 static int compile_expr2(char_u **arg,  cctx_T *cctx);
 static int compile_expr3(char_u **arg,  cctx_T *cctx);
 static void delete_def_function_contents(dfunc_T *dfunc);
+static void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
+static int check_type(type_T *expected, type_T *actual, int give_msg);
 
 /*
  * Lookup variable "name" in the local scope and return the index.
@@ -1240,6 +1242,32 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu
 	return FAIL;
     }
 
+    if (ufunc->uf_dfunc_idx >= 0)
+    {
+	int		i;
+
+	for (i = 0; i < argcount; ++i)
+	{
+	    type_T *expected;
+	    type_T *actual;
+
+	    if (i < regular_args)
+	    {
+		if (ufunc->uf_arg_types == NULL)
+		    continue;
+		expected = ufunc->uf_arg_types[i];
+	    }
+	    else
+		expected = ufunc->uf_va_type->tt_member;
+	    actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i];
+	    if (check_type(expected, actual, FALSE) == FAIL)
+	    {
+		arg_type_mismatch(expected, actual, i + 1);
+		return FAIL;
+	    }
+	}
+    }
+
     // Turn varargs into a list.
     if (ufunc->uf_va_name != NULL)
     {
@@ -2403,6 +2431,18 @@ type_mismatch(type_T *expected, type_T *
     vim_free(tofree2);
 }
 
+    static void
+arg_type_mismatch(type_T *expected, type_T *actual, int argidx)
+{
+    char *tofree1, *tofree2;
+
+    semsg(_("E1013: argument %d: type mismatch, expected %s but got %s"),
+	    argidx,
+	    type_name(expected, &tofree1), type_name(actual, &tofree2));
+    vim_free(tofree1);
+    vim_free(tofree2);
+}
+
 /*
  * Check if the expected and actual types match.
  */