diff options
author | zeertzjq <zeertzjq@outlook.com> | 2023-04-16 10:42:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-16 10:42:11 +0800 |
commit | 54dab9ed9e200f7c5bcac4a8f4901770fa15fa4f (patch) | |
tree | e07099845b2da8889f38a6f742ca8901f439782c | |
parent | 6adfd24a066c207334609a6b149ada19c0f568d4 (diff) | |
parent | d7965293ec18314df284ef53c363b73c2f3c1db8 (diff) | |
download | rneovim-54dab9ed9e200f7c5bcac4a8f4901770fa15fa4f.tar.gz rneovim-54dab9ed9e200f7c5bcac4a8f4901770fa15fa4f.tar.bz2 rneovim-54dab9ed9e200f7c5bcac4a8f4901770fa15fa4f.zip |
Merge pull request #23118 from zeertzjq/vim-8.2.3783
vim-patch:8.2.{1945,2848,2977,2978,3783,3786}
-rw-r--r-- | runtime/doc/eval.txt | 4 | ||||
-rw-r--r-- | src/nvim/eval.c | 28 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 13 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 45 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.h | 3 | ||||
-rw-r--r-- | src/nvim/globals.h | 1 | ||||
-rw-r--r-- | test/functional/lua/luaeval_spec.lua | 4 | ||||
-rw-r--r-- | test/old/testdir/test_functions.vim | 16 | ||||
-rw-r--r-- | test/old/testdir/test_listdict.vim | 6 | ||||
-rw-r--r-- | test/old/testdir/test_nested_function.vim | 11 |
10 files changed, 97 insertions, 34 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1ff6e3c360..f80ca5346c 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -139,7 +139,7 @@ You will not get an error if you try to change the type of a variable. 1.2 Function references ~ - *Funcref* *E695* *E718* + *Funcref* *E695* *E718* *E1192* A Funcref variable is obtained with the |function()| function, the |funcref()| function or created with the lambda expression |expr-lambda|. It can be used in an expression in the place of a function name, before the parenthesis @@ -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 97cf0c6364..b8daa78c79 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -96,6 +96,7 @@ static const char *e_string_list_or_blob_required = N_("E1098: String, List or B static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); +static const char e_empty_function_name[] = N_("E1192: Empty function name"); static char * const namespace_char = "abglstvw"; @@ -2219,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); @@ -2227,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. @@ -2240,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); @@ -3260,6 +3263,7 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T typval_T functv; const char *funcname; bool is_lua = false; + int ret; // need to copy the funcref so that we can clear rettv if (evaluate) { @@ -3273,6 +3277,11 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T funcname = is_lua ? lua_funcname : partial_name(pt); } else { funcname = functv.vval.v_string; + if (funcname == NULL || *funcname == NUL) { + emsg(_(e_empty_function_name)); + ret = FAIL; + goto theend; + } } } else { funcname = ""; @@ -3285,9 +3294,10 @@ static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T funcexe.fe_partial = pt; funcexe.fe_selfdict = selfdict; funcexe.fe_basetv = basetv; - const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, - arg, evalarg, &funcexe); + ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, + arg, evalarg, &funcexe); +theend: // Clear the funcref afterwards, so that deleting it while // evaluating the arguments is possible (see test55). if (evaluate) { @@ -4175,11 +4185,13 @@ int eval_interp_string(char **arg, typval_T *rettv, bool evaluate) char *partial_name(partial_T *pt) FUNC_ATTR_PURE { - if (pt->pt_name != NULL) { - return pt->pt_name; - } - if (pt->pt_func != NULL) { - return pt->pt_func->uf_name; + if (pt != NULL) { + if (pt->pt_name != NULL) { + return pt->pt_name; + } + if (pt->pt_func != NULL) { + return pt->pt_func->uf_name; + } } return ""; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 23d0242ce4..99e511a7a4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -150,6 +150,8 @@ static const char *e_invalwindow = N_("E957: Invalid window number"); static const char *e_reduceempty = N_("E998: Reduce of an empty %s with no initial value"); static const char e_using_number_as_bool_nr[] = N_("E1023: Using a Number as a Bool: %d"); +static const char e_missing_function_argument[] + = N_("E1132: Missing function argument"); /// Dummy va_list for passing to vim_snprintf /// @@ -563,8 +565,8 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) func = (char *)tv_get_string(&argvars[0]); } - if (*func == NUL) { - return; // type error or empty name + if (func == NULL || *func == NUL) { + return; // type error, empty name or null function } dict_T *selfdict = NULL; @@ -1134,7 +1136,7 @@ static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } /// Set the cursor position. -/// If 'charcol' is true, then use the column number as a character offset. +/// If "charcol" is true, then use the column number as a character offset. /// Otherwise use the column number as a byte offset. static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol) { @@ -6244,8 +6246,9 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } else { func_name = tv_get_string(&argvars[1]); } - if (*func_name == NUL) { - return; // type error or empty name + if (func_name == NULL || *func_name == NUL) { + emsg(_(e_missing_function_argument)); + return; } funcexe_T funcexe = FUNCEXE_INIT; 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 ad2b6dc563..7d3d74caad 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -2009,6 +2009,22 @@ func Test_call() eval mydict.len->call([], mydict)->assert_equal(4) call assert_fails("call call('Mylen', [], 0)", 'E715:') call assert_fails('call foo', 'E107:') + + " These once caused a crash. + " Nvim doesn't have null functions + " call call(test_null_function(), []) + " Nvim doesn't have null partials + " call call(test_null_partial(), []) + " Nvim doesn't have null functions + " 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 + call CheckScriptFailure(lines, 'E1085:') endfunc func Test_char2nr() diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index ba95e2ea9a..cbed71bb0a 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -747,6 +747,12 @@ func Test_reduce() call assert_equal(42, reduce(v:_null_list, function('add'), 42)) call assert_equal(42, reduce(v:_null_blob, function('add'), 42)) + + " should not crash + " Nvim doesn't have null functions + " call assert_fails('echo reduce([1], test_null_function())', 'E1132:') + " Nvim doesn't have null partials + " call assert_fails('echo reduce([1], test_null_partial())', 'E1132:') endfunc " splitting a string to a List using split() diff --git a/test/old/testdir/test_nested_function.vim b/test/old/testdir/test_nested_function.vim index afaaea6ceb..5599655461 100644 --- a/test/old/testdir/test_nested_function.vim +++ b/test/old/testdir/test_nested_function.vim @@ -1,5 +1,7 @@ -"Tests for nested functions -" +" Tests for nested functions + +source check.vim + func NestedFunc() func! Func1() let g:text .= 'Func1 ' @@ -48,6 +50,9 @@ func Recurse(count) endfunc func Test_max_nesting() + " TODO: why does this fail on Windows? Runs out of stack perhaps? + CheckNotMSWindows + let call_depth_here = 2 let ex_depth_here = 5 set mfd& @@ -61,3 +66,5 @@ func Test_max_nesting() set mfd& endfunc + +" vim: shiftwidth=2 sts=2 expandtab |