mirror of
https://github.com/neovim/neovim.git
synced 2026-01-07 05:43:14 +00:00
Merge pull request #23118 from zeertzjq/vim-8.2.3783
vim-patch:8.2.{1945,2848,2977,2978,3783,3786}
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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[]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user