]> granicus.if.org Git - vim/commitdiff
patch 8.2.2306: Vim9: when using function reference type is not checked v8.2.2306
authorBram Moolenaar <Bram@vim.org>
Wed, 6 Jan 2021 20:59:39 +0000 (21:59 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 6 Jan 2021 20:59:39 +0000 (21:59 +0100)
Problem:    Vim9: when using function reference type is not checked.
Solution:   When using a function reference lookup the type and check the
            argument types. (issue #7629)

12 files changed:
src/eval.c
src/evalfunc.c
src/evalvars.c
src/proto/userfunc.pro
src/proto/vim9type.pro
src/structs.h
src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c
src/vim9compile.c
src/vim9execute.c
src/vim9type.c

index 02cfd7d7dd5b91c2896a72765f91260079e8f041..373c02753cd4d0bb617cefa48f610c37b1f4cb80 100644 (file)
@@ -721,8 +721,10 @@ call_func_retlist(
 
 #ifdef FEAT_FOLDING
 /*
- * Evaluate 'foldexpr'.  Returns the foldlevel, and any character preceding
- * it in "*cp".  Doesn't give error messages.
+ * Evaluate "arg", which is 'foldexpr'.
+ * Note: caller must set "curwin" to match "arg".
+ * Returns the foldlevel, and any character preceding it in "*cp".  Doesn't
+ * give error messages.
  */
     int
 eval_foldexpr(char_u *arg, int *cp)
@@ -809,6 +811,7 @@ get_lval(
     int                len;
     hashtab_T  *ht = NULL;
     int                quiet = flags & GLV_QUIET;
+    int                writing;
 
     // Clear everything in "lp".
     CLEAR_POINTER(lp);
@@ -882,10 +885,10 @@ get_lval(
 
     cc = *p;
     *p = NUL;
-    // Only pass &ht when we would write to the variable, it prevents autoload
-    // as well.
-    v = find_var(lp->ll_name, (flags & GLV_READ_ONLY) ? NULL : &ht,
-                                                     flags & GLV_NO_AUTOLOAD);
+    // When we would write to the variable pass &ht and prevent autoload.
+    writing = !(flags & GLV_READ_ONLY);
+    v = find_var(lp->ll_name, writing ? &ht : NULL,
+                                        (flags & GLV_NO_AUTOLOAD) || writing);
     if (v == NULL && !quiet)
        semsg(_(e_undefined_variable_str), lp->ll_name);
     *p = cc;
@@ -1972,13 +1975,15 @@ eval_func(
     int                len = name_len;
     partial_T  *partial;
     int                ret = OK;
+    type_T     *type = NULL;
 
     if (!evaluate)
        check_vars(s, len);
 
     // If "s" is the name of a variable of type VAR_FUNC
     // use its contents.
-    s = deref_func_name(s, &len, &partial, !evaluate);
+    s = deref_func_name(s, &len, &partial,
+                                   in_vim9script() ? &type : NULL, !evaluate);
 
     // Need to make a copy, in case evaluating the arguments makes
     // the name invalid.
@@ -1996,6 +2001,7 @@ eval_func(
        funcexe.evaluate = evaluate;
        funcexe.partial = partial;
        funcexe.basetv = basetv;
+       funcexe.check_type = type;
        ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
     }
     vim_free(s);
index 07f1da9bb6a9b9348685c4fabe2e7019ca85fa2a..ed9f06fed436e93b5cb79cf29c5669f1bb30decb 100644 (file)
@@ -3497,7 +3497,8 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
     {
        name = s;
        trans_name = trans_function_name(&name, &is_global, FALSE,
-            TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
+            TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF,
+                                                            NULL, NULL, NULL);
        if (*name != NUL)
            s = NULL;
     }
index 8aac7667d4cffecdbc9cb9557f71de001ce71ca8..742c5a34f8f82192a18ed665525531d3c7721ce6 100644 (file)
@@ -2639,8 +2639,7 @@ check_vars(char_u *name, int len)
  * Find variable "name" in the list of variables.
  * Return a pointer to it if found, NULL if not found.
  * Careful: "a:0" variables don't have a name.
- * When "htp" is not NULL we are writing to the variable, set "htp" to the
- * hashtab_T used.
+ * When "htp" is not NULL  set "htp" to the hashtab_T used.
  */
     dictitem_T *
 find_var(char_u *name, hashtab_T **htp, int no_autoload)
@@ -2654,12 +2653,12 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
        *htp = ht;
     if (ht == NULL)
        return NULL;
-    ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
+    ret = find_var_in_ht(ht, *name, varname, no_autoload);
     if (ret != NULL)
        return ret;
 
     // Search in parent scope for lambda
-    ret = find_var_in_scoped_ht(name, no_autoload || htp != NULL);
+    ret = find_var_in_scoped_ht(name, no_autoload);
     if (ret != NULL)
        return ret;
 
@@ -2669,8 +2668,7 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload)
        ht = get_script_local_ht();
        if (ht != NULL)
        {
-           ret = find_var_in_ht(ht, *name, varname,
-                                                  no_autoload || htp != NULL);
+           ret = find_var_in_ht(ht, *name, varname, no_autoload);
            if (ret != NULL)
            {
                if (htp != NULL)
index f3bd2ae7313201cdc7055ca50a7c278c3e20fe8a..08dedae0a69c21dd33b4e1b3ed14bf35c1f41e13 100644 (file)
@@ -4,7 +4,7 @@ hashtab_T *func_tbl_get(void);
 char_u *get_lambda_name(void);
 char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state);
 int get_lambda_tv(char_u **arg, typval_T *rettv, int types_optional, evalarg_T *evalarg);
-char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload);
+char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload);
 void emsg_funcname(char *ermsg, char_u *name);
 int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe);
 char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error);
@@ -31,7 +31,7 @@ int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount,
 void user_func_error(int error, char_u *name);
 int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
 char_u *printable_func_name(ufunc_T *fp);
-char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial);
+char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
 char_u *untrans_function_name(char_u *name);
 void list_functions(regmatch_T *regmatch);
 ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
index ae947e699f31a392dd2801a3de53b31556c2f1d6..bc306687f846cfb636b7236d932d13c079fe75d0 100644 (file)
@@ -16,6 +16,7 @@ void type_mismatch(type_T *expected, type_T *actual);
 void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
 int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
 int check_arg_type(type_T *expected, type_T *actual, int argidx);
+int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
 char_u *skip_type(char_u *start, int optional);
 type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
 int equal_type(type_T *type1, type_T *type2);
index c016f199750f79011825292485ffd928c9b33f18..3324b067ae95e12a770a493fe8a9bc4890ee99fe 100644 (file)
@@ -1944,6 +1944,7 @@ typedef struct {
     partial_T  *partial;       // for extra arguments
     dict_T     *selfdict;      // Dictionary for "self"
     typval_T   *basetv;        // base for base->method()
+    type_T     *check_type;    // type from funcref or NULL
 } funcexe_T;
 
 /*
index e1d48534118755fcbb58a3b1c16830b9dc3183f4..c26f2af5bc595a3468495b4428ef5846a4dd3de3 100644 (file)
@@ -579,6 +579,22 @@ def Test_call_funcref_wrong_args()
 
   CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:')
   CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:')
+
+  var lines =<< trim END
+      vim9script
+      var Ref: func(number): any
+      Ref = (j) => !j
+      echo Ref(false)
+  END
+  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
+
+  lines =<< trim END
+      vim9script
+      var Ref: func(number): any
+      Ref = (j) => !j
+      call Ref(false)
+  END
+  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
 enddef
 
 def Test_call_lambda_args()
index 38930a2169206c2bd1cdf9d622c2b1a7abb38413..4c59bb516e7fdc1a98af928b5f6c2bbab6319f50 100644 (file)
@@ -727,47 +727,68 @@ errret:
  * name it contains, otherwise return "name".
  * If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
  * "partialp".
+ * If "type" is not NULL and a Vim9 script-local variable is found look up the
+ * type of the variable.
  */
     char_u *
-deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
+deref_func_name(
+       char_u      *name,
+       int         *lenp,
+       partial_T   **partialp,
+       type_T      **type,
+       int         no_autoload)
 {
     dictitem_T *v;
     int                cc;
-    char_u     *s;
+    char_u     *s = NULL;
+    hashtab_T  *ht;
 
     if (partialp != NULL)
        *partialp = NULL;
 
     cc = name[*lenp];
     name[*lenp] = NUL;
-    v = find_var(name, NULL, no_autoload);
+    v = find_var(name, &ht, no_autoload);
     name[*lenp] = cc;
-    if (v != NULL && v->di_tv.v_type == VAR_FUNC)
+    if (v != NULL)
     {
-       if (v->di_tv.vval.v_string == NULL)
+       if (v->di_tv.v_type == VAR_FUNC)
        {
-           *lenp = 0;
-           return (char_u *)"";        // just in case
+           if (v->di_tv.vval.v_string == NULL)
+           {
+               *lenp = 0;
+               return (char_u *)"";    // just in case
+           }
+           s = v->di_tv.vval.v_string;
+           *lenp = (int)STRLEN(s);
        }
-       s = v->di_tv.vval.v_string;
-       *lenp = (int)STRLEN(s);
-       return s;
-    }
 
-    if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
-    {
-       partial_T *pt = v->di_tv.vval.v_partial;
+       if (v->di_tv.v_type == VAR_PARTIAL)
+       {
+           partial_T *pt = v->di_tv.vval.v_partial;
 
-       if (pt == NULL)
+           if (pt == NULL)
+           {
+               *lenp = 0;
+               return (char_u *)"";    // just in case
+           }
+           if (partialp != NULL)
+               *partialp = pt;
+           s = partial_name(pt);
+           *lenp = (int)STRLEN(s);
+       }
+
+       if (s != NULL)
        {
-           *lenp = 0;
-           return (char_u *)"";        // just in case
+           if (type != NULL && ht == get_script_local_ht())
+           {
+               svar_T  *sv = find_typval_in_script(&v->di_tv);
+
+               if (sv != NULL)
+                   *type = sv->sv_type;
+           }
+           return s;
        }
-       if (partialp != NULL)
-           *partialp = pt;
-       s = partial_name(pt);
-       *lenp = (int)STRLEN(s);
-       return s;
     }
 
     return name;
@@ -2387,6 +2408,14 @@ call_func(
        }
     }
 
+    if (error == FCERR_NONE && funcexe->check_type != NULL && funcexe->evaluate)
+    {
+       // Check that the argument types are OK for the types of the funcref.
+       if (check_argument_types(funcexe->check_type, argvars, argcount,
+                                                                name) == FAIL)
+           error = FCERR_OTHER;
+    }
+
     if (error == FCERR_NONE && funcexe->evaluate)
     {
        char_u *rfname = fname;
@@ -2629,7 +2658,8 @@ trans_function_name(
     int                skip,           // only find the end, don't evaluate
     int                flags,
     funcdict_T *fdp,           // return: info about dictionary used
-    partial_T  **partial)      // return: partial of a FuncRef
+    partial_T  **partial,      // return: partial of a FuncRef
+    type_T     **type)         // return: type of funcref if not NULL
 {
     char_u     *name = NULL;
     char_u     *start;
@@ -2733,7 +2763,7 @@ trans_function_name(
     if (lv.ll_exp_name != NULL)
     {
        len = (int)STRLEN(lv.ll_exp_name);
-       name = deref_func_name(lv.ll_exp_name, &len, partial,
+       name = deref_func_name(lv.ll_exp_name, &len, partial, type,
                                                     flags & TFN_NO_AUTOLOAD);
        if (name == lv.ll_exp_name)
            name = NULL;
@@ -2741,7 +2771,8 @@ trans_function_name(
     else if (!(flags & TFN_NO_DEREF))
     {
        len = (int)(end - *pp);
-       name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
+       name = deref_func_name(*pp, &len, partial, type,
+                                                     flags & TFN_NO_AUTOLOAD);
        if (name == *pp)
            name = NULL;
     }
@@ -3064,7 +3095,7 @@ define_function(exarg_T *eap, char_u *name_arg)
     else
     {
        name = trans_function_name(&p, &is_global, eap->skip,
-                                                TFN_NO_AUTOLOAD, &fudi, NULL);
+                                          TFN_NO_AUTOLOAD, &fudi, NULL, NULL);
        paren = (vim_strchr(p, '(') != NULL);
        if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
        {
@@ -3479,7 +3510,8 @@ define_function(exarg_T *eap, char_u *name_arg)
                if (*p == '!')
                    p = skipwhite(p + 1);
                p += eval_fname_script(p);
-               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL, NULL));
+               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
+                                                                 NULL, NULL));
                if (*skipwhite(p) == '(')
                {
                    if (nesting == MAX_FUNC_NESTING - 1)
@@ -3616,7 +3648,7 @@ define_function(exarg_T *eap, char_u *name_arg)
     {
        hashtab_T       *ht;
 
-       v = find_var(name, &ht, FALSE);
+       v = find_var(name, &ht, TRUE);
        if (v != NULL && v->di_tv.v_type == VAR_FUNC)
        {
            emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
@@ -4007,7 +4039,7 @@ function_exists(char_u *name, int no_deref)
     flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
     if (no_deref)
        flag |= TFN_NO_DEREF;
-    p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL);
+    p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
     nm = skipwhite(nm);
 
     // Only accept "funcname", "funcname ", "funcname (..." and
@@ -4027,7 +4059,7 @@ get_expanded_name(char_u *name, int check)
     int                is_global = FALSE;
 
     p = trans_function_name(&nm, &is_global, FALSE,
-                                               TFN_INT|TFN_QUIET, NULL, NULL);
+                                         TFN_INT|TFN_QUIET, NULL, NULL, NULL);
 
     if (p != NULL && *nm == NUL
                       && (!check || translated_function_exists(p, is_global)))
@@ -4097,7 +4129,8 @@ ex_delfunction(exarg_T *eap)
     int                is_global = FALSE;
 
     p = eap->arg;
-    name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi, NULL);
+    name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
+                                                                  NULL, NULL);
     vim_free(fudi.fd_newkey);
     if (name == NULL)
     {
@@ -4328,6 +4361,7 @@ ex_call(exarg_T *eap)
     funcdict_T fudi;
     partial_T  *partial = NULL;
     evalarg_T  evalarg;
+    type_T     *type = NULL;
 
     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
     if (eap->skip)
@@ -4343,8 +4377,8 @@ ex_call(exarg_T *eap)
        return;
     }
 
-    tofree = trans_function_name(&arg, NULL, eap->skip,
-                                                    TFN_INT, &fudi, &partial);
+    tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
+                             &fudi, &partial, in_vim9script() ? &type : NULL);
     if (fudi.fd_newkey != NULL)
     {
        // Still need to give an error message for missing key.
@@ -4363,8 +4397,8 @@ ex_call(exarg_T *eap)
     // contents.  For VAR_PARTIAL get its partial, unless we already have one
     // from trans_function_name().
     len = (int)STRLEN(tofree);
-    name = deref_func_name(tofree, &len,
-                                   partial != NULL ? NULL : &partial, FALSE);
+    name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial,
+                       in_vim9script() && type == NULL ? &type : NULL, FALSE);
 
     // Skip white space to allow ":call func ()".  Not good, but required for
     // backward compatibility.
@@ -4416,6 +4450,7 @@ ex_call(exarg_T *eap)
        funcexe.evaluate = !eap->skip;
        funcexe.partial = partial;
        funcexe.selfdict = fudi.fd_dict;
+       funcexe.check_type = type;
        if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
        {
            failed = TRUE;
index 0e711ed4c8e71c605ff1e0ea9eda810626c8e0a8..1945a4a30b175d343b631e64d1d652c5cd394a08 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2306,
 /**/
     2305,
 /**/
index 9810f410ad06405deeee9a524e116f1f580607ec..c3aab20d3d389063c66f733aede9113bc1bfda0f 100644 (file)
@@ -1790,9 +1790,9 @@ generate_PCALL(
                                                       stack->ga_len + offset];
                    type_T *expected;
 
-                   if (varargs && i >= type->tt_min_argcount - 1)
+                   if (varargs && i >= type->tt_argcount - 1)
                        expected = type->tt_args[
-                                        type->tt_min_argcount - 1]->tt_member;
+                                            type->tt_argcount - 1]->tt_member;
                    else
                        expected = type->tt_args[i];
                    if (need_type(actual, expected, offset,
index b6297d7ebaa16ad68c9e3dc9172bc6c50b3d04eb..1376148228361dd836231485e22f9d506ab23a74 100644 (file)
@@ -3423,7 +3423,7 @@ ex_disassemble(exarg_T *eap)
     }
     else
        fname = trans_function_name(&arg, &is_global, FALSE,
-                           TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL);
+                     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
     if (fname == NULL)
     {
        semsg(_(e_invarg2), eap->arg);
index 2344874a33f09548463003b98ec150d7111baf4c..c65f747483cb0ead7a7a78c2dc26dc98ae7862a9 100644 (file)
@@ -527,6 +527,46 @@ check_arg_type(type_T *expected, type_T *actual, int argidx)
     return check_type(expected, actual, TRUE, argidx);
 }
 
+/*
+ * Check that the arguments of "type" match "argvars[argcount]".
+ * Return OK/FAIL.
+ */
+    int
+check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name)
+{
+    int            varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0;
+    int            i;
+
+    if (type->tt_type != VAR_FUNC && type->tt_type != VAR_PARTIAL)
+       return OK;  // just in case
+    if (argcount < type->tt_min_argcount - varargs)
+    {
+       semsg(_(e_toofewarg), name);
+       return FAIL;
+    }
+    if (!varargs && type->tt_argcount >= 0 && argcount > type->tt_argcount)
+    {
+       semsg(_(e_toomanyarg), name);
+       return FAIL;
+    }
+    if (type->tt_args == NULL)
+       return OK;  // cannot check
+
+
+    for (i = 0; i < argcount; ++i)
+    {
+       type_T  *expected;
+
+       if (varargs && i >= type->tt_argcount - 1)
+           expected = type->tt_args[type->tt_argcount - 1]->tt_member;
+       else
+           expected = type->tt_args[i];
+       if (check_typval_type(expected, &argvars[i], i + 1) == FAIL)
+           return FAIL;
+    }
+    return OK;
+}
+
 /*
  * Skip over a type definition and return a pointer to just after it.
  * When "optional" is TRUE then a leading "?" is accepted.