]> granicus.if.org Git - vim/commitdiff
patch 8.2.3783: confusing error for using a variable as a function v8.2.3783
authorBram Moolenaar <Bram@vim.org>
Sat, 11 Dec 2021 16:14:07 +0000 (16:14 +0000)
committerBram Moolenaar <Bram@vim.org>
Sat, 11 Dec 2021 16:14:07 +0000 (16:14 +0000)
Problem:    Confusing error for using a variable as a function.
Solution:   If a function is not found but there is a variable, give a more
            useful error. (issue #9310)

src/eval.c
src/proto/userfunc.pro
src/structs.h
src/testdir/test_functions.vim
src/testdir/test_vim9_func.vim
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9execute.c

index 821d0c1dc874516c4eeba9d3ceb9a8c04b2d5431..e2aa41b34ff69063475d9fbedeffb17a383d43ba 100644 (file)
@@ -1988,6 +1988,7 @@ eval_func(
     partial_T  *partial;
     int                ret = OK;
     type_T     *type = NULL;
+    int                found_var = FALSE;
 
     if (!evaluate)
        check_vars(s, len);
@@ -1995,7 +1996,7 @@ eval_func(
     // If "s" is the name of a variable of type VAR_FUNC
     // use its contents.
     s = deref_func_name(s, &len, &partial,
-                                   in_vim9script() ? &type : NULL, !evaluate);
+                       in_vim9script() ? &type : NULL, !evaluate, &found_var);
 
     // Need to make a copy, in case evaluating the arguments makes
     // the name invalid.
@@ -2014,6 +2015,7 @@ eval_func(
        funcexe.partial = partial;
        funcexe.basetv = basetv;
        funcexe.check_type = type;
+       funcexe.fe_found_var = found_var;
        ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
     }
     vim_free(s);
index 85144482e64220cb8d8b2498f369053c8c0b7eab..bb38143524c63a0d1a680f10c816e1ee2954685d 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, type_T **type, int no_autoload);
+char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **type, int no_autoload, int *found_var);
 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);
@@ -30,7 +30,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
 int get_callback_depth(void);
 int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars);
 varnumber_T call_callback_retnr(callback_T *callback, int argcount, typval_T *argvars);
-void user_func_error(int error, char_u *name);
+void user_func_error(int error, char_u *name, funcexe_T *funcexe);
 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, type_T **type);
index a5850d4391687a3fa73860204a11cfc08daf3421..be622f7858d8e550d629a5ef081ccd16e8e2ca5f 100644 (file)
@@ -2001,6 +2001,8 @@ typedef struct {
     dict_T     *selfdict;      // Dictionary for "self"
     typval_T   *basetv;        // base for base->method()
     type_T     *check_type;    // type from funcref or NULL
+    int                fe_found_var;   // if the function is not found then give an
+                               // error that a variable is not callable.
 } funcexe_T;
 
 /*
index a215667964dd3d6eeb83f319407b1690f0014c01..1fe56f34e8b89d009baede00c934e618567d3726 100644 (file)
@@ -2234,6 +2234,12 @@ func Test_call()
   call call(test_null_partial(), [])
   call assert_fails('call test_null_function()()', 'E1192:')
   call assert_fails('call test_null_partial()()', 'E117:')
+
+  let lines =<< trim END
+      let Time = 'localtime'
+      call Time()
+  END
+  CheckScriptFailure(lines, 'E1085:')
 endfunc
 
 func Test_char2nr()
index 7365fd8dc7f9e6d6345a9437025194db005a5b4b..2c871d33e5f505ae7a9e42057f8cf08d6eb41df4 100644 (file)
@@ -1459,7 +1459,7 @@ endfunc
 def Test_call_funcref()
   g:SomeFunc('abc')->assert_equal(3)
   assert_fails('NotAFunc()', 'E117:', '', 2, 'Test_call_funcref') # comment after call
-  assert_fails('g:NotAFunc()', 'E117:', '', 3, 'Test_call_funcref')
+  assert_fails('g:NotAFunc()', 'E1085:', '', 3, 'Test_call_funcref')
 
   var lines =<< trim END
     vim9script
index b789b00921efb4c33feae1e6d6dc31e9a08ac8cc..4b05e9cf1023a38a79b22ff9a5969129eb8525ca 100644 (file)
@@ -1922,6 +1922,15 @@ def Test_script_var_shadows_command()
   CheckDefAndScriptFailure(lines, 'E1207:', 2)
 enddef
 
+def Test_vim9script_call_wrong_type()
+  var lines =<< trim END
+      vim9script
+      var Time = 'localtime'
+      Time()
+  END
+  CheckScriptFailure(lines, 'E1085:')
+enddef
+
 def s:RetSome(): string
   return 'some'
 enddef
index 52bacce35e6d30f70b0c0b0b0fa162321345d578..5f35f35eed0d405e06e861e8f5ba5c46d3670847 100644 (file)
@@ -1544,6 +1544,7 @@ errret:
  * "partialp".
  * If "type" is not NULL and a Vim9 script-local variable is found look up the
  * type of the variable.
+ * If "found_var" is not NULL and a variable was found set it to TRUE.
  */
     char_u *
 deref_func_name(
@@ -1551,7 +1552,8 @@ deref_func_name(
        int         *lenp,
        partial_T   **partialp,
        type_T      **type,
-       int         no_autoload)
+       int         no_autoload,
+       int         *found_var)
 {
     dictitem_T *v;
     typval_T   *tv = NULL;
@@ -1609,6 +1611,8 @@ deref_func_name(
 
     if (tv != NULL)
     {
+       if (found_var != NULL)
+           *found_var = TRUE;
        if (tv->v_type == VAR_FUNC)
        {
            if (tv->vval.v_string == NULL)
@@ -3199,12 +3203,15 @@ call_callback_retnr(
  * Nothing if "error" is FCERR_NONE.
  */
     void
-user_func_error(int error, char_u *name)
+user_func_error(int error, char_u *name, funcexe_T *funcexe)
 {
     switch (error)
     {
        case FCERR_UNKNOWN:
-               emsg_funcname(e_unknownfunc, name);
+               if (funcexe->fe_found_var)
+                   semsg(_(e_not_callable_type_str), name);
+               else
+                   emsg_funcname(e_unknownfunc, name);
                break;
        case FCERR_NOTMETHOD:
                emsg_funcname(
@@ -3448,7 +3455,7 @@ theend:
      */
     if (!aborting())
     {
-       user_func_error(error, (name != NULL) ? name : funcname);
+       user_func_error(error, (name != NULL) ? name : funcname, funcexe);
     }
 
     // clear the copies made from the partial
@@ -3677,7 +3684,7 @@ trans_function_name(
     {
        len = (int)STRLEN(lv.ll_exp_name);
        name = deref_func_name(lv.ll_exp_name, &len, partial, type,
-                                                    flags & TFN_NO_AUTOLOAD);
+                                               flags & TFN_NO_AUTOLOAD, NULL);
        if (name == lv.ll_exp_name)
            name = NULL;
     }
@@ -3685,7 +3692,7 @@ trans_function_name(
     {
        len = (int)(end - *pp);
        name = deref_func_name(*pp, &len, partial, type,
-                                                     flags & TFN_NO_AUTOLOAD);
+                                               flags & TFN_NO_AUTOLOAD, NULL);
        if (name == *pp)
            name = NULL;
     }
@@ -5004,6 +5011,7 @@ ex_call(exarg_T *eap)
     partial_T  *partial = NULL;
     evalarg_T  evalarg;
     type_T     *type = NULL;
+    int                found_var = FALSE;
 
     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
     if (eap->skip)
@@ -5040,7 +5048,7 @@ ex_call(exarg_T *eap)
     // from trans_function_name().
     len = (int)STRLEN(tofree);
     name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial,
-                       in_vim9script() && type == NULL ? &type : NULL, FALSE);
+           in_vim9script() && type == NULL ? &type : NULL, FALSE, &found_var);
 
     // Skip white space to allow ":call func ()".  Not good, but required for
     // backward compatibility.
@@ -5096,6 +5104,7 @@ ex_call(exarg_T *eap)
        funcexe.partial = partial;
        funcexe.selfdict = fudi.fd_dict;
        funcexe.check_type = type;
+       funcexe.fe_found_var = found_var;
        rettv.v_type = VAR_UNKNOWN;     // clear_tv() uses this
        if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
        {
index 50fac523ab9cd5283384e9f6ac4db839e8ad0dd9..1169d103ce651b452a8a0ceae3bdf3f35d239334 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3783,
 /**/
     3782,
 /**/
index 874e57c58c06f56dc9fe632352ae99ec763cd813..c5d39535038505c3a58defe7fd2c45991ea9ac88 100644 (file)
@@ -890,7 +890,7 @@ call_ufunc(
 
     if (error != FCERR_NONE)
     {
-       user_func_error(error, ufunc->uf_name);
+       user_func_error(error, ufunc->uf_name, &funcexe);
        return FAIL;
     }
     if (did_emsg > did_emsg_before)
@@ -2343,7 +2343,8 @@ exec_instructions(ectx_T *ectx)
                    long        n = 0;
                    char_u      *s = NULL;
                    char        *msg;
-                   callback_T  cb = {NULL, NULL, 0};
+                   char_u      numbuf[NUMBUFLEN];
+                   char_u      *tofree = NULL;
 
                    --ectx->ec_stack.ga_len;
                    tv = STACK_TV_BOT(0);
@@ -2356,28 +2357,29 @@ exec_instructions(ectx_T *ectx)
                    else if (iptr->isn_type == ISN_STOREFUNCOPT)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
-                       cb = get_callback(tv);
-                       if (cb.cb_name == NULL || *cb.cb_name == NUL)
+                       // If the option can be set to a function reference or
+                       // a lambda and the passed value is a function
+                       // reference, then convert it to the name (string) of
+                       // the function reference.
+                       s = tv2string(tv, &tofree, numbuf, 0);
+                       if (s == NULL || *s == NUL)
                        {
                            clear_tv(tv);
-                           free_callback(&cb);
                            goto on_error;
                        }
-                       s = cb.cb_name;
                    }
                    else
                        // must be VAR_NUMBER, CHECKTYPE makes sure
                        n = tv->vval.v_number;
                    msg = set_option_value(opt_name, n, s, opt_flags);
                    clear_tv(tv);
+                   vim_free(tofree);
                    if (msg != NULL)
                    {
                        SOURCING_LNUM = iptr->isn_lnum;
                        emsg(_(msg));
                        goto on_error;
                    }
-                   if (cb.cb_name != NULL)
-                       free_callback(&cb);
                }
                break;