summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-12-11 16:14:07 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-11 16:14:07 +0000
commit2ef9156b4284e4a52613c36e3d4667245273a28d (patch)
treebb591edc76e6f750ba20acf4c6dd13164e175afe
parent052ff291d72bc9c176f9562f021d7e8e030e74c0 (diff)
patch 8.2.3783: confusing error for using a variable as a functionv8.2.3783
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)
-rw-r--r--src/eval.c4
-rw-r--r--src/proto/userfunc.pro4
-rw-r--r--src/structs.h2
-rw-r--r--src/testdir/test_functions.vim6
-rw-r--r--src/testdir/test_vim9_func.vim2
-rw-r--r--src/testdir/test_vim9_script.vim9
-rw-r--r--src/userfunc.c23
-rw-r--r--src/version.c2
-rw-r--r--src/vim9execute.c18
9 files changed, 51 insertions, 19 deletions
diff --git a/src/eval.c b/src/eval.c
index 821d0c1dc8..e2aa41b34f 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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);
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 85144482e6..bb38143524 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -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);
diff --git a/src/structs.h b/src/structs.h
index a5850d4391..be622f7858 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -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;
/*
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index a215667964..1fe56f34e8 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -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()
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index 7365fd8dc7..2c871d33e5 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -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
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index b789b00921..4b05e9cf10 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -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
diff --git a/src/userfunc.c b/src/userfunc.c
index 52bacce35e..5f35f35eed 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -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)
{
diff --git a/src/version.c b/src/version.c
index 50fac523ab..1169d103ce 100644
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 3783,
+/**/
3782,
/**/
3781,
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 874e57c58c..c5d3953503 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -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;