aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-04-16 08:54:07 +0800
committerzeertzjq <zeertzjq@outlook.com>2023-04-16 10:15:15 +0800
commit68ca16c376bd8786ffc0c7ce7619b9a0ce5657e8 (patch)
treec3519829e3131749ba72f3a3a567b1cf4f4a307a
parent2e8cec5f2bf2dd1209052e870d9713a932dc7bfa (diff)
downloadrneovim-68ca16c376bd8786ffc0c7ce7619b9a0ce5657e8.tar.gz
rneovim-68ca16c376bd8786ffc0c7ce7619b9a0ce5657e8.tar.bz2
rneovim-68ca16c376bd8786ffc0c7ce7619b9a0ce5657e8.zip
vim-patch:8.2.3783: confusing error for using a variable as a function
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 vim/vim#9310) https://github.com/vim/vim/commit/2ef9156b4284e4a52613c36e3d4667245273a28d Co-authored-by: Bram Moolenaar <Bram@vim.org>
-rw-r--r--runtime/doc/eval.txt2
-rw-r--r--src/nvim/eval.c4
-rw-r--r--src/nvim/eval/userfunc.c45
-rw-r--r--src/nvim/eval/userfunc.h3
-rw-r--r--src/nvim/globals.h1
-rw-r--r--test/functional/lua/luaeval_spec.lua4
-rw-r--r--test/old/testdir/test_functions.vim6
7 files changed, 46 insertions, 19 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index ccb615602f..f80ca5346c 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1236,7 +1236,7 @@ Note that the dot is also used for String concatenation. To avoid confusion
always put spaces around the dot for String concatenation.
-expr8(expr1, ...) |Funcref| function call
+expr8(expr1, ...) |Funcref| function call *E1085*
When expr8 is a |Funcref| type variable, invoke the function it refers to.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 1d9061186a..b8daa78c79 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2220,6 +2220,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam
const bool evaluate = flags & EVAL_EVALUATE;
char *s = name;
int len = name_len;
+ bool found_var = false;
if (!evaluate) {
check_vars(s, (size_t)len);
@@ -2228,7 +2229,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
partial_T *partial;
- s = deref_func_name(s, &len, &partial, !evaluate);
+ s = deref_func_name(s, &len, &partial, !evaluate, &found_var);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
@@ -2241,6 +2242,7 @@ static int eval_func(char **const arg, evalarg_T *const evalarg, char *const nam
funcexe.fe_evaluate = evaluate;
funcexe.fe_partial = partial;
funcexe.fe_basetv = basetv;
+ funcexe.fe_found_var = found_var;
int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
xfree(s);
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 9705bc93db..a348588106 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -65,6 +65,7 @@ static funccall_T *current_funccal = NULL;
// item in it is still being used.
static funccall_T *previous_funccal = NULL;
+static const char *e_unknownfunc = N_("E117: Unknown function: %s");
static const char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
static const char *e_funcdict = N_("E717: Dictionary entry already exists");
static const char *e_funcref = N_("E718: Funcref required");
@@ -401,9 +402,11 @@ errret:
/// is not needed.
/// @param[in] no_autoload If true, do not source autoload scripts if function
/// was not found.
+/// @param[out] found_var If not NULL and a variable was found set it to true.
///
/// @return name of the function.
-char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
+char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload,
+ bool *found_var)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
if (partialp != NULL) {
@@ -411,18 +414,25 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b
}
dictitem_T *const v = find_var(name, (size_t)(*lenp), NULL, no_autoload);
- if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
- if (v->di_tv.vval.v_string == NULL) { // just in case
+ if (v == NULL) {
+ return (char *)name;
+ }
+ typval_T *const tv = &v->di_tv;
+ if (found_var != NULL) {
+ *found_var = true;
+ }
+
+ if (tv->v_type == VAR_FUNC) {
+ if (tv->vval.v_string == NULL) { // just in case
*lenp = 0;
return "";
}
- *lenp = (int)strlen(v->di_tv.vval.v_string);
- return v->di_tv.vval.v_string;
+ *lenp = (int)strlen(tv->vval.v_string);
+ return tv->vval.v_string;
}
- if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
- partial_T *const pt = v->di_tv.vval.v_partial;
-
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *const pt = tv->vval.v_partial;
if (pt == NULL) { // just in case
*lenp = 0;
return "";
@@ -1454,12 +1464,16 @@ varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argv
/// Give an error message for the result of a function.
/// Nothing if "error" is FCERR_NONE.
-static void user_func_error(int error, const char *name)
+static void user_func_error(int error, const char *name, funcexe_T *funcexe)
FUNC_ATTR_NONNULL_ALL
{
switch (error) {
case FCERR_UNKNOWN:
- emsg_funcname(N_("E117: Unknown function: %s"), 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(N_("E276: Cannot use function as a method: %s"), name);
@@ -1654,7 +1668,7 @@ theend:
// Report an error unless the argument evaluation or function call has been
// cancelled due to an aborting error, an interrupt, or an exception.
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
@@ -1846,14 +1860,13 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
// Check if the name is a Funcref. If so, use the value.
if (lv.ll_exp_name != NULL) {
len = (int)strlen(lv.ll_exp_name);
- name = deref_func_name(lv.ll_exp_name, &len, partial,
- flags & TFN_NO_AUTOLOAD);
+ name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD, NULL);
if (name == lv.ll_exp_name) {
name = NULL;
}
} 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, flags & TFN_NO_AUTOLOAD, NULL);
if (name == *pp) {
name = NULL;
}
@@ -3070,7 +3083,8 @@ void 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);
+ bool found_var = false;
+ name = deref_func_name(tofree, &len, partial != NULL ? NULL : &partial, false, &found_var);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
@@ -3104,6 +3118,7 @@ void ex_call(exarg_T *eap)
funcexe.fe_evaluate = true;
funcexe.fe_partial = partial;
funcexe.fe_selfdict = fudi.fd_dict;
+ funcexe.fe_found_var = found_var;
if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) {
failed = true;
break;
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index d0ad53c43d..4a98afb00e 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -75,6 +75,8 @@ typedef struct {
partial_T *fe_partial; ///< for extra arguments
dict_T *fe_selfdict; ///< Dictionary for "self"
typval_T *fe_basetv; ///< base for base->method()
+ bool fe_found_var; ///< if the function is not found then give an
+ ///< error that a variable is not callable.
} funcexe_T;
#define FUNCEXE_INIT (funcexe_T) { \
@@ -86,6 +88,7 @@ typedef struct {
.fe_partial = NULL, \
.fe_selfdict = NULL, \
.fe_basetv = NULL, \
+ .fe_found_var = false, \
}
#define FUNCARG(fp, j) ((char **)(fp->uf_args.ga_data))[j]
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 11888a5df8..3c31d07e33 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1000,6 +1000,7 @@ EXTERN const char e_fnametoolong[] INIT(= N_("E856: Filename too long"));
EXTERN const char e_float_as_string[] INIT(= N_("E806: using Float as a String"));
EXTERN const char e_inval_string[] INIT(= N_("E908: using an invalid value as a String"));
EXTERN const char e_cannot_edit_other_buf[] INIT(= N_("E788: Not allowed to edit another buffer now"));
+EXTERN const char e_not_callable_type_str[] INIT(= N_("E1085: Not a callable type: %s"));
EXTERN const char e_cmdmap_err[] INIT(= N_("E5520: <Cmd> mapping must end with <CR>"));
EXTERN const char e_cmdmap_repeated[]
diff --git a/test/functional/lua/luaeval_spec.lua b/test/functional/lua/luaeval_spec.lua
index 9f313eab9e..32bb894be1 100644
--- a/test/functional/lua/luaeval_spec.lua
+++ b/test/functional/lua/luaeval_spec.lua
@@ -545,7 +545,7 @@ describe('v:lua', function()
eq("Vim:E15: Invalid expression: v:['lua'].foo()", pcall_err(eval, "v:['lua'].foo()"))
eq("Vim(call):E461: Illegal variable name: v:['lua']", pcall_err(command, "call v:['lua'].baar()"))
- eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "v:lua()"))
+ eq("Vim:E1085: Not a callable type: v:lua", pcall_err(eval, "v:lua()"))
eq("Vim(let):E46: Cannot change read-only variable \"v:['lua']\"", pcall_err(command, "let v:['lua'] = 'xx'"))
eq("Vim(let):E46: Cannot change read-only variable \"v:lua\"", pcall_err(command, "let v:lua = 'xx'"))
@@ -553,7 +553,7 @@ describe('v:lua', function()
eq("Vim:E107: Missing parentheses: v:lua.func", pcall_err(eval, "'bad'->v:lua.func"))
eq("Vim:E274: No white space allowed before parenthesis", pcall_err(eval, "'bad'->v:lua.func ()"))
eq("Vim:E107: Missing parentheses: v:lua", pcall_err(eval, "'bad'->v:lua"))
- eq("Vim:E117: Unknown function: v:lua", pcall_err(eval, "'bad'->v:lua()"))
+ eq("Vim:E1085: Not a callable type: v:lua", pcall_err(eval, "'bad'->v:lua()"))
eq("Vim:E15: Invalid expression: v:lua.()", pcall_err(eval, "'bad'->v:lua.()"))
end)
end)
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index abea3b2538..fb53248d5a 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -2019,6 +2019,12 @@ func Test_call()
" call assert_fails('call test_null_function()()', 'E1192:')
" Nvim doesn't have null partials
" 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()