Patch 8.2.2306
Problem: Vim9: when using function reference type is not checked.
Solution: When using a function reference lookup the type and check the
argument types. (issue #7629)
Files: src/userfunc.c, src/proto/
userfunc.pro, src/eval.c, src/structs.h,
src/vim9type.c, src/proto/
vim9type.pro, src/vim9compile.c,
src/vim9execute.c, src/evalvars.c, src/evalfunc.c,
src/testdir/test_vim9_func.vim
*** ../vim-8.2.2305/src/userfunc.c 2021-01-04 14:09:40.053795023 +0100
--- src/userfunc.c 2021-01-06 21:36:27.686016340 +0100
***************
*** 727,773 ****
* name it contains, otherwise return "name".
* If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
* "partialp".
*/
char_u *
! deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload)
{
dictitem_T *v;
int cc;
! char_u *s;
if (partialp != NULL)
*partialp = NULL;
cc = name[*lenp];
name[*lenp] = NUL;
! v = find_var(name, NULL, no_autoload);
name[*lenp] = cc;
! if (v != NULL && v->di_tv.v_type == VAR_FUNC)
{
! if (v->di_tv.vval.v_string == NULL)
{
! *lenp = 0;
! return (char_u *)""; // just in case
}
- s = v->di_tv.vval.v_string;
- *lenp = (int)STRLEN(s);
- return s;
- }
! if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
! {
! partial_T *pt = v->di_tv.vval.v_partial;
! if (pt == NULL)
{
! *lenp = 0;
! return (char_u *)""; // just in case
}
- if (partialp != NULL)
- *partialp = pt;
- s = partial_name(pt);
- *lenp = (int)STRLEN(s);
- return s;
}
return name;
--- 727,794 ----
* name it contains, otherwise return "name".
* If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
* "partialp".
+ * If "type" is not NULL and a Vim9 script-local variable is found look up the
+ * type of the variable.
*/
char_u *
! deref_func_name(
! char_u *name,
! int *lenp,
! partial_T **partialp,
! type_T **type,
! int no_autoload)
{
dictitem_T *v;
int cc;
! char_u *s = NULL;
! hashtab_T *ht;
if (partialp != NULL)
*partialp = NULL;
cc = name[*lenp];
name[*lenp] = NUL;
! v = find_var(name, &ht, no_autoload);
name[*lenp] = cc;
! if (v != NULL)
{
! if (v->di_tv.v_type == VAR_FUNC)
{
! if (v->di_tv.vval.v_string == NULL)
! {
! *lenp = 0;
! return (char_u *)""; // just in case
! }
! s = v->di_tv.vval.v_string;
! *lenp = (int)STRLEN(s);
}
! if (v->di_tv.v_type == VAR_PARTIAL)
! {
! partial_T *pt = v->di_tv.vval.v_partial;
! if (pt == NULL)
! {
! *lenp = 0;
! return (char_u *)""; // just in case
! }
! if (partialp != NULL)
! *partialp = pt;
! s = partial_name(pt);
! *lenp = (int)STRLEN(s);
! }
!
! if (s != NULL)
{
! if (type != NULL && ht == get_script_local_ht())
! {
! svar_T *sv = find_typval_in_script(&v->di_tv);
!
! if (sv != NULL)
! *type = sv->sv_type;
! }
! return s;
}
}
return name;
***************
*** 2387,2392 ****
--- 2408,2421 ----
}
}
+ if (error == FCERR_NONE && funcexe->check_type != NULL && funcexe->evaluate)
+ {
+ // Check that the argument types are OK for the types of the funcref.
+ if (check_argument_types(funcexe->check_type, argvars, argcount,
+ name) == FAIL)
+ error = FCERR_OTHER;
+ }
+
if (error == FCERR_NONE && funcexe->evaluate)
{
char_u *rfname = fname;
***************
*** 2629,2635 ****
int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
! partial_T **partial) // return: partial of a FuncRef
{
char_u *name = NULL;
char_u *start;
--- 2658,2665 ----
int skip, // only find the end, don't evaluate
int flags,
funcdict_T *fdp, // return: info about dictionary used
! partial_T **partial, // return: partial of a FuncRef
! type_T **type) // return: type of funcref if not NULL
{
char_u *name = NULL;
char_u *start;
***************
*** 2733,2739 ****
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);
if (name == lv.ll_exp_name)
name = NULL;
--- 2763,2769 ----
if (lv.ll_exp_name != NULL)
{
len = (int)STRLEN(lv.ll_exp_name);
! name = deref_func_name(lv.ll_exp_name, &len, partial, type,
flags & TFN_NO_AUTOLOAD);
if (name == lv.ll_exp_name)
name = NULL;
***************
*** 2741,2747 ****
else if (!(flags & TFN_NO_DEREF))
{
len = (int)(end - *pp);
! name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD);
if (name == *pp)
name = NULL;
}
--- 2771,2778 ----
else if (!(flags & TFN_NO_DEREF))
{
len = (int)(end - *pp);
! name = deref_func_name(*pp, &len, partial, type,
! flags & TFN_NO_AUTOLOAD);
if (name == *pp)
name = NULL;
}
***************
*** 3064,3070 ****
else
{
name = trans_function_name(&p, &is_global, eap->skip,
! TFN_NO_AUTOLOAD, &fudi, NULL);
paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
{
--- 3095,3101 ----
else
{
name = trans_function_name(&p, &is_global, eap->skip,
! TFN_NO_AUTOLOAD, &fudi, NULL, NULL);
paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
{
***************
*** 3479,3485 ****
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
! vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
--- 3510,3517 ----
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
! vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
! NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
***************
*** 3616,3622 ****
{
hashtab_T *ht;
! v = find_var(name, &ht, FALSE);
if (v != NULL && v->di_tv.v_type == VAR_FUNC)
{
emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
--- 3648,3654 ----
{
hashtab_T *ht;
! v = find_var(name, &ht, TRUE);
if (v != NULL && v->di_tv.v_type == VAR_FUNC)
{
emsg_funcname(N_("E707: Function name conflicts with variable: %s"),
***************
*** 4007,4013 ****
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
! p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
--- 4039,4045 ----
flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
if (no_deref)
flag |= TFN_NO_DEREF;
! p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
nm = skipwhite(nm);
// Only accept "funcname", "funcname ", "funcname (..." and
***************
*** 4027,4033 ****
int is_global = FALSE;
p = trans_function_name(&nm, &is_global, FALSE,
! TFN_INT|TFN_QUIET, NULL, NULL);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
--- 4059,4065 ----
int is_global = FALSE;
p = trans_function_name(&nm, &is_global, FALSE,
! TFN_INT|TFN_QUIET, NULL, NULL, NULL);
if (p != NULL && *nm == NUL
&& (!check || translated_function_exists(p, is_global)))
***************
*** 4097,4103 ****
int is_global = FALSE;
p = eap->arg;
! name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
--- 4129,4136 ----
int is_global = FALSE;
p = eap->arg;
! name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
! NULL, NULL);
vim_free(fudi.fd_newkey);
if (name == NULL)
{
***************
*** 4328,4333 ****
--- 4361,4367 ----
funcdict_T fudi;
partial_T *partial = NULL;
evalarg_T evalarg;
+ type_T *type = NULL;
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip)
***************
*** 4343,4350 ****
return;
}
! tofree = trans_function_name(&arg, NULL, eap->skip,
! TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.
--- 4377,4384 ----
return;
}
! tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
! &fudi, &partial, in_vim9script() ? &type : NULL);
if (fudi.fd_newkey != NULL)
{
// Still need to give an error message for missing key.
***************
*** 4363,4370 ****
// 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);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
--- 4397,4404 ----
// 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,
! in_vim9script() && type == NULL ? &type : NULL, FALSE);
// Skip white space to allow ":call func ()". Not good, but required for
// backward compatibility.
***************
*** 4416,4421 ****
--- 4450,4456 ----
funcexe.evaluate = !eap->skip;
funcexe.partial = partial;
funcexe.selfdict = fudi.fd_dict;
+ funcexe.check_type = type;
if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL)
{
failed = TRUE;
*** ../vim-8.2.2305/src/proto/
userfunc.pro 2021-01-04 14:09:40.053795023 +0100
--- src/proto/
userfunc.pro 2021-01-06 21:37:44.985807661 +0100
***************
*** 4,10 ****
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, int no_autoload);
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);
--- 4,10 ----
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);
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);
***************
*** 31,37 ****
void user_func_error(int error, char_u *name);
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);
char_u *untrans_function_name(char_u *name);
void list_functions(regmatch_T *regmatch);
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
--- 31,37 ----
void user_func_error(int error, char_u *name);
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);
char_u *untrans_function_name(char_u *name);
void list_functions(regmatch_T *regmatch);
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
*** ../vim-8.2.2305/src/eval.c 2021-01-05 22:08:17.205806639 +0100
--- src/eval.c 2021-01-06 21:25:44.895464060 +0100
***************
*** 721,728 ****
#ifdef FEAT_FOLDING
/*
! * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
! * it in "*cp". Doesn't give error messages.
*/
int
eval_foldexpr(char_u *arg, int *cp)
--- 721,730 ----
#ifdef FEAT_FOLDING
/*
! * Evaluate "arg", which is 'foldexpr'.
! * Note: caller must set "curwin" to match "arg".
! * Returns the foldlevel, and any character preceding it in "*cp". Doesn't
! * give error messages.
*/
int
eval_foldexpr(char_u *arg, int *cp)
***************
*** 809,814 ****
--- 811,817 ----
int len;
hashtab_T *ht = NULL;
int quiet = flags & GLV_QUIET;
+ int writing;
// Clear everything in "lp".
CLEAR_POINTER(lp);
***************
*** 882,891 ****
cc = *p;
*p = NUL;
! // Only pass &ht when we would write to the variable, it prevents autoload
! // as well.
! v = find_var(lp->ll_name, (flags & GLV_READ_ONLY) ? NULL : &ht,
! flags & GLV_NO_AUTOLOAD);
if (v == NULL && !quiet)
semsg(_(e_undefined_variable_str), lp->ll_name);
*p = cc;
--- 885,894 ----
cc = *p;
*p = NUL;
! // When we would write to the variable pass &ht and prevent autoload.
! writing = !(flags & GLV_READ_ONLY);
! v = find_var(lp->ll_name, writing ? &ht : NULL,
! (flags & GLV_NO_AUTOLOAD) || writing);
if (v == NULL && !quiet)
semsg(_(e_undefined_variable_str), lp->ll_name);
*p = cc;
***************
*** 1972,1984 ****
int len = name_len;
partial_T *partial;
int ret = OK;
if (!evaluate)
check_vars(s, len);
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
! s = deref_func_name(s, &len, &partial, !evaluate);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
--- 1975,1989 ----
int len = name_len;
partial_T *partial;
int ret = OK;
+ type_T *type = NULL;
if (!evaluate)
check_vars(s, len);
// 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);
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
***************
*** 1996,2001 ****
--- 2001,2007 ----
funcexe.evaluate = evaluate;
funcexe.partial = partial;
funcexe.basetv = basetv;
+ funcexe.check_type = type;
ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
}
vim_free(s);
*** ../vim-8.2.2305/src/structs.h 2021-01-04 12:41:49.507891351 +0100
--- src/structs.h 2021-01-06 20:23:54.283683093 +0100
***************
*** 1944,1949 ****
--- 1944,1950 ----
partial_T *partial; // for extra arguments
dict_T *selfdict; // Dictionary for "self"
typval_T *basetv; // base for base->method()
+ type_T *check_type; // type from funcref or NULL
} funcexe_T;
/*
*** ../vim-8.2.2305/src/vim9type.c 2021-01-03 13:09:48.226390595 +0100
--- src/vim9type.c 2021-01-06 21:11:59.509421778 +0100
***************
*** 528,533 ****
--- 528,573 ----
}
/*
+ * Check that the arguments of "type" match "argvars[argcount]".
+ * Return OK/FAIL.
+ */
+ int
+ check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name)
+ {
+ int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0;
+ int i;
+
+ if (type->tt_type != VAR_FUNC && type->tt_type != VAR_PARTIAL)
+ return OK; // just in case
+ if (argcount < type->tt_min_argcount - varargs)
+ {
+ semsg(_(e_toofewarg), name);
+ return FAIL;
+ }
+ if (!varargs && type->tt_argcount >= 0 && argcount > type->tt_argcount)
+ {
+ semsg(_(e_toomanyarg), name);
+ return FAIL;
+ }
+ if (type->tt_args == NULL)
+ return OK; // cannot check
+
+
+ for (i = 0; i < argcount; ++i)
+ {
+ type_T *expected;
+
+ if (varargs && i >= type->tt_argcount - 1)
+ expected = type->tt_args[type->tt_argcount - 1]->tt_member;
+ else
+ expected = type->tt_args[i];
+ if (check_typval_type(expected, &argvars[i], i + 1) == FAIL)
+ return FAIL;
+ }
+ return OK;
+ }
+
+ /*
* Skip over a type definition and return a pointer to just after it.
* When "optional" is TRUE then a leading "?" is accepted.
*/
*** ../vim-8.2.2305/src/proto/
vim9type.pro 2020-12-26 20:09:11.282465257 +0100
--- src/proto/
vim9type.pro 2021-01-06 20:48:43.780691288 +0100
***************
*** 16,21 ****
--- 16,22 ----
void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
int check_arg_type(type_T *expected, type_T *actual, int argidx);
+ int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
char_u *skip_type(char_u *start, int optional);
type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
int equal_type(type_T *type1, type_T *type2);
*** ../vim-8.2.2305/src/vim9compile.c 2021-01-05 22:08:17.201806649 +0100
--- src/vim9compile.c 2021-01-06 21:12:38.253324361 +0100
***************
*** 1790,1798 ****
stack->ga_len + offset];
type_T *expected;
! if (varargs && i >= type->tt_min_argcount - 1)
expected = type->tt_args[
! type->tt_min_argcount - 1]->tt_member;
else
expected = type->tt_args[i];
if (need_type(actual, expected, offset,
--- 1790,1798 ----
stack->ga_len + offset];
type_T *expected;
! if (varargs && i >= type->tt_argcount - 1)
expected = type->tt_args[
! type->tt_argcount - 1]->tt_member;
else
expected = type->tt_args[i];
if (need_type(actual, expected, offset,
*** ../vim-8.2.2305/src/vim9execute.c 2021-01-05 20:58:20.851037690 +0100
--- src/vim9execute.c 2021-01-06 21:37:10.413920902 +0100
***************
*** 3423,3429 ****
}
else
fname = trans_function_name(&arg, &is_global, FALSE,
! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL);
if (fname == NULL)
{
semsg(_(e_invarg2), eap->arg);
--- 3423,3429 ----
}
else
fname = trans_function_name(&arg, &is_global, FALSE,
! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
if (fname == NULL)
{
semsg(_(e_invarg2), eap->arg);
*** ../vim-8.2.2305/src/evalvars.c 2021-01-04 13:37:50.251107339 +0100
--- src/evalvars.c 2021-01-06 21:23:01.983837281 +0100
***************
*** 2639,2646 ****
* Find variable "name" in the list of variables.
* Return a pointer to it if found, NULL if not found.
* Careful: "a:0" variables don't have a name.
! * When "htp" is not NULL we are writing to the variable, set "htp" to the
! * hashtab_T used.
*/
dictitem_T *
find_var(char_u *name, hashtab_T **htp, int no_autoload)
--- 2639,2645 ----
* Find variable "name" in the list of variables.
* Return a pointer to it if found, NULL if not found.
* Careful: "a:0" variables don't have a name.
! * When "htp" is not NULL set "htp" to the hashtab_T used.
*/
dictitem_T *
find_var(char_u *name, hashtab_T **htp, int no_autoload)
***************
*** 2654,2665 ****
*htp = ht;
if (ht == NULL)
return NULL;
! ret = find_var_in_ht(ht, *name, varname, no_autoload || htp != NULL);
if (ret != NULL)
return ret;
// Search in parent scope for lambda
! ret = find_var_in_scoped_ht(name, no_autoload || htp != NULL);
if (ret != NULL)
return ret;
--- 2653,2664 ----
*htp = ht;
if (ht == NULL)
return NULL;
! ret = find_var_in_ht(ht, *name, varname, no_autoload);
if (ret != NULL)
return ret;
// Search in parent scope for lambda
! ret = find_var_in_scoped_ht(name, no_autoload);
if (ret != NULL)
return ret;
***************
*** 2669,2676 ****
ht = get_script_local_ht();
if (ht != NULL)
{
! ret = find_var_in_ht(ht, *name, varname,
! no_autoload || htp != NULL);
if (ret != NULL)
{
if (htp != NULL)
--- 2668,2674 ----
ht = get_script_local_ht();
if (ht != NULL)
{
! ret = find_var_in_ht(ht, *name, varname, no_autoload);
if (ret != NULL)
{
if (htp != NULL)
*** ../vim-8.2.2305/src/evalfunc.c 2021-01-03 19:51:01.392063235 +0100
--- src/evalfunc.c 2021-01-06 21:37:01.177941528 +0100
***************
*** 3497,3503 ****
{
name = s;
trans_name = trans_function_name(&name, &is_global, FALSE,
! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
if (*name != NUL)
s = NULL;
}
--- 3497,3504 ----
{
name = s;
trans_name = trans_function_name(&name, &is_global, FALSE,
! TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF,
! NULL, NULL, NULL);
if (*name != NUL)
s = NULL;
}
*** ../vim-8.2.2305/src/testdir/test_vim9_func.vim 2021-01-04 14:09:40.053795023 +0100
--- src/testdir/test_vim9_func.vim 2021-01-06 21:29:46.026917452 +0100
***************
*** 579,584 ****
--- 579,600 ----
CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:')
CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:')
+
+ var lines =<< trim END
+ vim9script
+ var Ref: func(number): any
+ Ref = (j) => !j
+ echo Ref(false)
+ END
+ CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
+
+ lines =<< trim END
+ vim9script
+ var Ref: func(number): any
+ Ref = (j) => !j
+ call Ref(false)
+ END
+ CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
enddef
def Test_call_lambda_args()
*** ../vim-8.2.2305/src/version.c 2021-01-05 22:08:17.205806639 +0100
--- src/version.c 2021-01-06 21:58:11.602231840 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2306,
/**/
--
Common sense is what tells you that the world is flat.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///