]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1559: function argument types not always checked v9.0.1559
authorBram Moolenaar <Bram@vim.org>
Mon, 15 May 2023 15:22:38 +0000 (16:22 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 15 May 2023 15:22:38 +0000 (16:22 +0100)
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)

src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c
src/vim9type.c

index ffb8de48dd0b0645ce7694bfa1782a1f0cef2020..ecdbd5eacaa9980a75d71ad9c3811e73dc2db8ff 100644 (file)
@@ -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
index e63caf9fed3daf6d2c394a7142f71263f040e211..6f0d59dfd36febd2baa408d9c144f56bae102770 100644 (file)
@@ -3595,6 +3595,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
  *
@@ -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);
            }
        }
index 689b1a2c75b5ce6af53211716ab58e235a8a7626..47079f5331cbb098c90cbf74aede6e65ccda27ce 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1559,
 /**/
     1558,
 /**/
index 95252dbafeda3e68df92440ac5f165db905addc1..0dd74ee184be61fe8491ffd3149a8a605125f804 100644 (file)
@@ -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;