Merge pull request #23081 from zeertzjq/vim-8.2.1062

vim-patch:8.2.{1062,1063,1064,1065,1068,1069,1070,1071,1073,1074,1075,1076,1079,1080,1098,1099,1100,1125,1161,1162,1163,1203,3216}
This commit is contained in:
zeertzjq
2023-04-14 16:33:37 +08:00
committed by GitHub
13 changed files with 286 additions and 189 deletions

View File

@@ -173,6 +173,7 @@ Object nvim_eval(String expr, Error *err)
TRY_WRAP(err, { TRY_WRAP(err, {
ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE); ok = eval0(expr.data, &rettv, NULL, &EVALARG_EVALUATE);
clear_evalarg(&EVALARG_EVALUATE, NULL);
}); });
if (!ERROR_SET(err)) { if (!ERROR_SET(err)) {
@@ -294,6 +295,7 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Error *err)
api_set_error(err, kErrorTypeException, api_set_error(err, kErrorTypeException,
"Failed to evaluate dict expression"); "Failed to evaluate dict expression");
} }
clear_evalarg(&EVALARG_EVALUATE, NULL);
if (try_end(err)) { if (try_end(err)) {
return rv; return rv;
} }

View File

@@ -494,7 +494,7 @@ static typval_T *eval_expr_no_emsg(struct debuggy *const bp)
{ {
// Disable error messages, a bad expression would make Vim unusable. // Disable error messages, a bad expression would make Vim unusable.
emsg_off++; emsg_off++;
typval_T *const tv = eval_expr(bp->dbg_name); typval_T *const tv = eval_expr(bp->dbg_name, NULL);
emsg_off--; emsg_off--;
return tv; return tv;
} }

View File

@@ -692,6 +692,17 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
set_vim_var_string(VV_FNAME_OUT, NULL, -1); set_vim_var_string(VV_FNAME_OUT, NULL, -1);
} }
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, bool skip)
{
*evalarg = (evalarg_T){ .eval_flags = skip ? 0 : EVAL_EVALUATE };
if (eap != NULL) {
if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
evalarg->eval_getline = eap->getline;
evalarg->eval_cookie = eap->cookie;
}
}
}
/// Top level evaluation function, returning a boolean. /// Top level evaluation function, returning a boolean.
/// Sets "error" to true if there was an error. /// Sets "error" to true if there was an error.
/// ///
@@ -702,11 +713,14 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
{ {
typval_T tv; typval_T tv;
bool retval = false; bool retval = false;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) { if (skip) {
emsg_skip++; emsg_skip++;
} }
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL) { if (eval0(arg, &tv, eap, &evalarg) == FAIL) {
*error = true; *error = true;
} else { } else {
*error = false; *error = false;
@@ -718,19 +732,23 @@ int eval_to_bool(char *arg, bool *error, exarg_T *eap, int skip)
if (skip) { if (skip) {
emsg_skip--; emsg_skip--;
} }
clear_evalarg(&evalarg, eap);
return retval; return retval;
} }
/// Call eval1() and give an error message if not done at a lower level. /// Call eval1() and give an error message if not done at a lower level.
static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate) static int eval1_emsg(char **arg, typval_T *rettv, exarg_T *eap)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_NONNULL_ARG(1, 2)
{ {
const char *const start = *arg; const char *const start = *arg;
const int did_emsg_before = did_emsg; const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg; const int called_emsg_before = called_emsg;
evalarg_T evalarg;
const int ret = eval1(arg, rettv, evaluate ? &EVALARG_EVALUATE : NULL); fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
const int ret = eval1(arg, rettv, &evalarg);
if (ret == FAIL) { if (ret == FAIL) {
// Report the invalid expression unless the expression evaluation has // Report the invalid expression unless the expression evaluation has
// been cancelled due to an aborting error, an interrupt, or an // been cancelled due to an aborting error, an interrupt, or an
@@ -742,6 +760,7 @@ static int eval1_emsg(char **arg, typval_T *rettv, bool evaluate)
semsg(_(e_invexpr2), start); semsg(_(e_invexpr2), start);
} }
} }
clear_evalarg(&evalarg, eap);
return ret; return ret;
} }
@@ -786,7 +805,7 @@ int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *r
return FAIL; return FAIL;
} }
s = skipwhite(s); s = skipwhite(s);
if (eval1_emsg(&s, rettv, true) == FAIL) { if (eval1_emsg(&s, rettv, NULL) == FAIL) {
return FAIL; return FAIL;
} }
if (*skipwhite(s) != NUL) { // check for trailing chars after expr if (*skipwhite(s) != NUL) { // check for trailing chars after expr
@@ -827,11 +846,13 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
{ {
typval_T tv; typval_T tv;
char *retval; char *retval;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, skip);
if (skip) { if (skip) {
emsg_skip++; emsg_skip++;
} }
if (eval0(arg, &tv, eap, skip ? NULL : &EVALARG_EVALUATE) == FAIL || skip) { if (eval0(arg, &tv, eap, &evalarg) == FAIL || skip) {
retval = NULL; retval = NULL;
} else { } else {
retval = xstrdup(tv_get_string(&tv)); retval = xstrdup(tv_get_string(&tv));
@@ -840,6 +861,7 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
if (skip) { if (skip) {
emsg_skip--; emsg_skip--;
} }
clear_evalarg(&evalarg, eap);
return retval; return retval;
} }
@@ -847,12 +869,24 @@ char *eval_to_string_skip(char *arg, exarg_T *eap, const bool skip)
/// Skip over an expression at "*pp". /// Skip over an expression at "*pp".
/// ///
/// @return FAIL for an error, OK otherwise. /// @return FAIL for an error, OK otherwise.
int skip_expr(char **pp) int skip_expr(char **pp, evalarg_T *const evalarg)
{ {
typval_T rettv; const int save_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
// Don't evaluate the expression.
if (evalarg != NULL) {
evalarg->eval_flags &= ~EVAL_EVALUATE;
}
*pp = skipwhite(*pp); *pp = skipwhite(*pp);
return eval1(pp, &rettv, NULL); typval_T rettv;
int res = eval1(pp, &rettv, NULL);
if (evalarg != NULL) {
evalarg->eval_flags = save_flags;
}
return res;
} }
/// Top level evaluation function, returning a string. /// Top level evaluation function, returning a string.
@@ -889,6 +923,7 @@ char *eval_to_string(char *arg, bool convert)
} }
tv_clear(&tv); tv_clear(&tv);
} }
clear_evalarg(&EVALARG_EVALUATE, NULL);
return retval; return retval;
} }
@@ -943,12 +978,18 @@ varnumber_T eval_to_number(char *expr)
/// ///
/// @return an allocated typval_T with the result or /// @return an allocated typval_T with the result or
/// NULL when there is an error. /// NULL when there is an error.
typval_T *eval_expr(char *arg) typval_T *eval_expr(char *arg, exarg_T *eap)
{ {
typval_T *tv = xmalloc(sizeof(*tv)); typval_T *tv = xmalloc(sizeof(*tv));
if (eval0(arg, tv, NULL, &EVALARG_EVALUATE) == FAIL) { evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
if (eval0(arg, tv, eap, &evalarg) == FAIL) {
XFREE_CLEAR(tv); XFREE_CLEAR(tv);
} }
clear_evalarg(&evalarg, eap);
return tv; return tv;
} }
@@ -1194,6 +1235,7 @@ int eval_foldexpr(char *arg, int *cp)
sandbox--; sandbox--;
} }
textlock--; textlock--;
clear_evalarg(&EVALARG_EVALUATE, NULL);
return (int)retval; return (int)retval;
} }
@@ -1776,14 +1818,14 @@ notify:
/// @param[out] *errp set to true for an error, false otherwise; /// @param[out] *errp set to true for an error, false otherwise;
/// ///
/// @return a pointer that holds the info. Null when there is an error. /// @return a pointer that holds the info. Null when there is an error.
void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip) void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, evalarg_T *const evalarg)
{ {
forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); forinfo_T *fi = xcalloc(1, sizeof(forinfo_T));
const char *expr; const char *expr;
typval_T tv; typval_T tv;
list_T *l; list_T *l;
const bool skip = !(evalarg->eval_flags & EVAL_EVALUATE);
evalarg_T evalarg = { .eval_flags = skip ? 0 : EVAL_EVALUATE };
*errp = true; // Default: there is an error. *errp = true; // Default: there is an error.
expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon);
@@ -1792,7 +1834,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
} }
expr = skipwhite(expr); expr = skipwhite(expr);
if (expr[0] != 'i' || expr[1] != 'n' || !ascii_iswhite(expr[2])) { if (expr[0] != 'i' || expr[1] != 'n'
|| !(expr[2] == NUL || ascii_iswhite(expr[2]))) {
emsg(_("E690: Missing \"in\" after :for")); emsg(_("E690: Missing \"in\" after :for"));
return fi; return fi;
} }
@@ -1800,7 +1843,8 @@ void *eval_for_line(const char *arg, bool *errp, exarg_T *eap, int skip)
if (skip) { if (skip) {
emsg_skip++; emsg_skip++;
} }
if (eval0(skipwhite(expr + 2), &tv, eap, &evalarg) == OK) { expr = skipwhite(expr + 2);
if (eval0((char *)expr, &tv, eap, evalarg) == OK) {
*errp = false; *errp = false;
if (!skip) { if (!skip) {
if (tv.v_type == VAR_LIST) { if (tv.v_type == VAR_LIST) {
@@ -2164,9 +2208,10 @@ int pattern_match(const char *pat, const char *text, bool ic)
/// @param basetv "expr" for "expr->name(arg)" /// @param basetv "expr" for "expr->name(arg)"
/// ///
/// @return OK or FAIL. /// @return OK or FAIL.
static int eval_func(char **const arg, char *const name, const int name_len, typval_T *const rettv, static int eval_func(char **const arg, evalarg_T *const evalarg, char *const name,
const int flags, typval_T *const basetv) const int name_len, typval_T *const rettv, const int flags,
FUNC_ATTR_NONNULL_ARG(1, 2, 4) typval_T *const basetv)
FUNC_ATTR_NONNULL_ARG(1, 3, 5)
{ {
const bool evaluate = flags & EVAL_EVALUATE; const bool evaluate = flags & EVAL_EVALUATE;
char *s = name; char *s = name;
@@ -2192,7 +2237,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
funcexe.fe_evaluate = evaluate; funcexe.fe_evaluate = evaluate;
funcexe.fe_partial = partial; funcexe.fe_partial = partial;
funcexe.fe_basetv = basetv; funcexe.fe_basetv = basetv;
int ret = get_func_tv(s, len, rettv, arg, &funcexe); int ret = get_func_tv(s, len, rettv, arg, evalarg, &funcexe);
xfree(s); xfree(s);
@@ -2216,6 +2261,26 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
return ret; return ret;
} }
/// After using "evalarg" filled from "eap": free the memory.
void clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
{
if (evalarg != NULL) {
if (evalarg->eval_tofree != NULL) {
if (eap != NULL) {
// We may need to keep the original command line, e.g. for
// ":let" it has the variable names. But we may also need the
// new one, "nextcmd" points into it. Keep both.
xfree(eap->cmdline_tofree);
eap->cmdline_tofree = *eap->cmdlinep;
*eap->cmdlinep = evalarg->eval_tofree;
} else {
xfree(evalarg->eval_tofree);
}
evalarg->eval_tofree = NULL;
}
}
}
/// The "evaluate" argument: When false, the argument is only parsed but not /// The "evaluate" argument: When false, the argument is only parsed but not
/// executed. The function may return OK, but the rettv will be of type /// executed. The function may return OK, but the rettv will be of type
/// VAR_UNKNOWN. The function still returns FAIL for a syntax error. /// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
@@ -2236,9 +2301,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
const int called_emsg_before = called_emsg; const int called_emsg_before = called_emsg;
bool end_error = false; bool end_error = false;
if (evalarg != NULL) {
evalarg->eval_tofree = NULL;
}
p = skipwhite(arg); p = skipwhite(arg);
ret = eval1(&p, rettv, evalarg); ret = eval1(&p, rettv, evalarg);
@@ -2269,21 +2331,6 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
eap->nextcmd = check_nextcmd(p); eap->nextcmd = check_nextcmd(p);
} }
if (evalarg != NULL) {
if (eap != NULL) {
if (evalarg->eval_tofree != NULL) {
// We may need to keep the original command line, e.g. for
// ":let" it has the variable names. But we may also need the
// new one, "nextcmd" points into it. Keep both.
xfree(eap->cmdline_tofree);
eap->cmdline_tofree = *eap->cmdlinep;
*eap->cmdlinep = evalarg->eval_tofree;
}
} else {
xfree(evalarg->eval_tofree);
}
}
return ret; return ret;
} }
@@ -2303,7 +2350,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
return FAIL; return FAIL;
} }
if ((*arg)[0] == '?') { char *p = *arg;
if (*p == '?') {
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE; const bool evaluate = nested_evalarg.eval_flags & EVAL_EVALUATE;
@@ -2329,7 +2377,8 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
} }
// Check for the ":". // Check for the ":".
if ((*arg)[0] != ':') { p = *arg;
if (*p != ':') {
emsg(_("E109: Missing ':' after '?'")); emsg(_("E109: Missing ':' after '?'"));
if (evaluate && result) { if (evaluate && result) {
tv_clear(rettv); tv_clear(rettv);
@@ -2375,7 +2424,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
// Repeat until there is no following "||". // Repeat until there is no following "||".
bool first = true; bool first = true;
bool result = false; bool result = false;
while ((*arg)[0] == '|' && (*arg)[1] == '|') { char *p = *arg;
while (p[0] == '|' && p[1] == '|') {
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
const bool evaluate = orig_flags & EVAL_EVALUATE; const bool evaluate = orig_flags & EVAL_EVALUATE;
@@ -2412,6 +2462,8 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = result; rettv->vval.v_number = result;
} }
p = *arg;
} }
return OK; return OK;
@@ -2437,7 +2489,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
// Repeat until there is no following "&&". // Repeat until there is no following "&&".
bool first = true; bool first = true;
bool result = true; bool result = true;
while ((*arg)[0] == '&' && (*arg)[1] == '&') { char *p = *arg;
while (p[0] == '&' && p[1] == '&') {
evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg; evalarg_T nested_evalarg = evalarg == NULL ? (evalarg_T){ 0 } : *evalarg;
const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags; const int orig_flags = evalarg == NULL ? 0 : evalarg->eval_flags;
const bool evaluate = orig_flags & EVAL_EVALUATE; const bool evaluate = orig_flags & EVAL_EVALUATE;
@@ -2474,6 +2527,8 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = result; rettv->vval.v_number = result;
} }
p = *arg;
} }
return OK; return OK;
@@ -2498,7 +2553,6 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg) static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{ {
typval_T var2; typval_T var2;
char *p;
exprtype_T type = EXPR_UNKNOWN; exprtype_T type = EXPR_UNKNOWN;
int len = 2; int len = 2;
@@ -2507,7 +2561,7 @@ static int eval4(char **arg, typval_T *rettv, evalarg_T *const evalarg)
return FAIL; return FAIL;
} }
p = *arg; char *p = *arg;
switch (p[0]) { switch (p[0]) {
case '=': case '=':
if (p[1] == '=') { if (p[1] == '=') {
@@ -2627,8 +2681,6 @@ static int eval_addlist(typval_T *tv1, typval_T *tv2)
/// @return OK or FAIL. /// @return OK or FAIL.
static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg) static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{ {
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
// Get the first variable. // Get the first variable.
if (eval6(arg, rettv, evalarg, false) == FAIL) { if (eval6(arg, rettv, evalarg, false) == FAIL) {
return FAIL; return FAIL;
@@ -2642,6 +2694,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
break; break;
} }
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB)) if ((op != '+' || (rettv->v_type != VAR_LIST && rettv->v_type != VAR_BLOB))
&& (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) { && (op == '.' || rettv->v_type != VAR_FLOAT) && evaluate) {
// For "list + ...", an illegal use of the first operand as // For "list + ...", an illegal use of the first operand as
@@ -2770,12 +2823,7 @@ static int eval5(char **arg, typval_T *rettv, evalarg_T *const evalarg)
static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
FUNC_ATTR_NO_SANITIZE_UNDEFINED FUNC_ATTR_NO_SANITIZE_UNDEFINED
{ {
typval_T var2;
int op;
varnumber_T n1, n2;
bool use_float = false; bool use_float = false;
float_T f1 = 0, f2 = 0;
bool error = false;
// Get the first variable. // Get the first variable.
if (eval7(arg, rettv, evalarg, want_string) == FAIL) { if (eval7(arg, rettv, evalarg, want_string) == FAIL) {
@@ -2784,12 +2832,15 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// Repeat computing, until no '*', '/' or '%' is following. // Repeat computing, until no '*', '/' or '%' is following.
for (;;) { for (;;) {
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); int op = (uint8_t)(**arg);
op = (uint8_t)(**arg);
if (op != '*' && op != '/' && op != '%') { if (op != '*' && op != '/' && op != '%') {
break; break;
} }
varnumber_T n1, n2;
float_T f1 = 0, f2 = 0;
bool error = false;
const bool evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE);
if (evaluate) { if (evaluate) {
if (rettv->v_type == VAR_FLOAT) { if (rettv->v_type == VAR_FLOAT) {
f1 = rettv->vval.v_float; f1 = rettv->vval.v_float;
@@ -2808,6 +2859,7 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// Get the second variable. // Get the second variable.
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
typval_T var2;
if (eval7(arg, &var2, evalarg, false) == FAIL) { if (eval7(arg, &var2, evalarg, false) == FAIL) {
return FAIL; return FAIL;
} }
@@ -2901,7 +2953,6 @@ static int eval6(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
/// @return OK or FAIL. /// @return OK or FAIL.
static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string) static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool want_string)
{ {
const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK; int ret = OK;
static int recurse = 0; static int recurse = 0;
@@ -2964,14 +3015,14 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// List: [expr, expr] // List: [expr, expr]
case '[': case '[':
ret = get_list_tv(arg, rettv, flags); ret = get_list_tv(arg, rettv, evalarg);
break; break;
// Dictionary: #{key: val, key: val} // Dictionary: #{key: val, key: val}
case '#': case '#':
if ((*arg)[1] == '{') { if ((*arg)[1] == '{') {
(*arg)++; (*arg)++;
ret = eval_dict(arg, rettv, flags, true); ret = eval_dict(arg, rettv, evalarg, true);
} else { } else {
ret = NOTDONE; ret = NOTDONE;
} }
@@ -2980,9 +3031,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// Lambda: {arg, arg -> expr} // Lambda: {arg, arg -> expr}
// Dictionary: {'key': val, 'key': val} // Dictionary: {'key': val, 'key': val}
case '{': case '{':
ret = get_lambda_tv(arg, rettv, evaluate); ret = get_lambda_tv(arg, rettv, evalarg);
if (ret == NOTDONE) { if (ret == NOTDONE) {
ret = eval_dict(arg, rettv, flags, false); ret = eval_dict(arg, rettv, evalarg, false);
} }
break; break;
@@ -3010,6 +3061,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// nested expression: (expression). // nested expression: (expression).
case '(': case '(':
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, evalarg); // recursive! ret = eval1(arg, rettv, evalarg); // recursive!
if (**arg == ')') { if (**arg == ')') {
(*arg)++; (*arg)++;
@@ -3038,8 +3090,9 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
if (len <= 0) { if (len <= 0) {
ret = FAIL; ret = FAIL;
} else { } else {
const int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
if (**arg == '(') { // recursive! if (**arg == '(') { // recursive!
ret = eval_func(arg, s, len, rettv, flags, NULL); ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
} else if (evaluate) { } else if (evaluate) {
ret = get_var_tv(s, len, rettv, NULL, true, false); ret = get_var_tv(s, len, rettv, NULL, true, false);
} else { } else {
@@ -3055,7 +3108,7 @@ static int eval7(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool wan
// Handle following '[', '(' and '.' for expr[expr], expr.name, // Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr) // expr(expr), expr->name(expr)
if (ret == OK) { if (ret == OK) {
ret = handle_subscript((const char **)arg, rettv, flags, true); ret = handle_subscript((const char **)arg, rettv, evalarg, true);
} }
// Apply logical NOT and unary '-', from right to left, ignore '+'. // Apply logical NOT and unary '-', from right to left, ignore '+'.
@@ -3131,10 +3184,10 @@ static int eval7_leader(typval_T *const rettv, const bool numeric_only,
/// to the name of the Lua function to call (after the /// to the name of the Lua function to call (after the
/// "v:lua." prefix). /// "v:lua." prefix).
/// @return OK on success, FAIL on failure. /// @return OK on success, FAIL on failure.
static int call_func_rettv(char **const arg, typval_T *const rettv, const bool evaluate, static int call_func_rettv(char **const arg, evalarg_T *const evalarg, typval_T *const rettv,
dict_T *const selfdict, typval_T *const basetv, const bool evaluate, dict_T *const selfdict, typval_T *const basetv,
const char *const lua_funcname) const char *const lua_funcname)
FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_NONNULL_ARG(1, 3)
{ {
partial_T *pt = NULL; partial_T *pt = NULL;
typval_T functv; typval_T functv;
@@ -3166,7 +3219,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
funcexe.fe_selfdict = selfdict; funcexe.fe_selfdict = selfdict;
funcexe.fe_basetv = basetv; funcexe.fe_basetv = basetv;
const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv, const int ret = get_func_tv(funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
arg, &funcexe); arg, evalarg, &funcexe);
// Clear the funcref afterwards, so that deleting it while // Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55). // evaluating the arguments is possible (see test55).
@@ -3185,16 +3238,17 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
/// @return FAIL or OK. /// @return FAIL or OK.
/// ///
/// @note "*arg" is advanced to after the ')'. /// @note "*arg" is advanced to after the ')'.
static int eval_lambda(char **const arg, typval_T *const rettv, const bool evaluate, static int eval_lambda(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose) const bool verbose)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ARG(1, 2)
{ {
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
// Skip over the ->. // Skip over the ->.
*arg += 2; *arg += 2;
typval_T base = *rettv; typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN; rettv->v_type = VAR_UNKNOWN;
int ret = get_lambda_tv(arg, rettv, evaluate); int ret = get_lambda_tv(arg, rettv, evalarg);
if (ret != OK) { if (ret != OK) {
return FAIL; return FAIL;
} else if (**arg != '(') { } else if (**arg != '(') {
@@ -3208,7 +3262,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
tv_clear(rettv); tv_clear(rettv);
ret = FAIL; ret = FAIL;
} else { } else {
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, NULL); ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, NULL);
} }
// Clear the funcref afterwards, so that deleting it while // Clear the funcref afterwards, so that deleting it while
@@ -3225,10 +3279,12 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
/// @param *arg points to the '-'. /// @param *arg points to the '-'.
/// ///
/// @return FAIL or OK. "*arg" is advanced to after the ')'. /// @return FAIL or OK. "*arg" is advanced to after the ')'.
static int eval_method(char **const arg, typval_T *const rettv, const bool evaluate, static int eval_method(char **const arg, typval_T *const rettv, evalarg_T *const evalarg,
const bool verbose) const bool verbose)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ARG(1, 2)
{ {
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
// Skip over the ->. // Skip over the ->.
*arg += 2; *arg += 2;
typval_T base = *rettv; typval_T base = *rettv;
@@ -3278,9 +3334,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
rettv->vval.v_partial = vvlua_partial; rettv->vval.v_partial = vvlua_partial;
rettv->vval.v_partial->pt_refcount++; rettv->vval.v_partial->pt_refcount++;
} }
ret = call_func_rettv(arg, rettv, evaluate, NULL, &base, lua_funcname); ret = call_func_rettv(arg, evalarg, rettv, evaluate, NULL, &base, lua_funcname);
} else { } else {
ret = eval_func(arg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base); ret = eval_func(arg, evalarg, name, len, rettv, evaluate ? EVAL_EVALUATE : 0, &base);
} }
} }
@@ -3299,9 +3355,9 @@ static int eval_method(char **const arg, typval_T *const rettv, const bool evalu
/// @param verbose give error messages /// @param verbose give error messages
/// ///
/// @returns FAIL or OK. "*arg" is advanced to after the ']'. /// @returns FAIL or OK. "*arg" is advanced to after the ']'.
static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose) static int eval_index(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool verbose)
{ {
const bool evaluate = flags & EVAL_EVALUATE; const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
bool empty1 = false; bool empty1 = false;
bool empty2 = false; bool empty2 = false;
ptrdiff_t len = -1; ptrdiff_t len = -1;
@@ -3350,15 +3406,13 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose
} }
*arg = skipwhite(key + len); *arg = skipwhite(key + len);
} else { } else {
evalarg_T evalarg = { .eval_flags = flags };
// something[idx] // something[idx]
// //
// Get the (first) variable from inside the []. // Get the (first) variable from inside the [].
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
if (**arg == ':') { if (**arg == ':') {
empty1 = true; empty1 = true;
} else if (eval1(arg, &var1, &evalarg) == FAIL) { // Recursive! } else if (eval1(arg, &var1, evalarg) == FAIL) { // Recursive!
return FAIL; return FAIL;
} else if (evaluate && !tv_check_str(&var1)) { } else if (evaluate && !tv_check_str(&var1)) {
// Not a number or string. // Not a number or string.
@@ -3372,7 +3426,7 @@ static int eval_index(char **arg, typval_T *rettv, const int flags, bool verbose
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
if (**arg == ']') { if (**arg == ']') {
empty2 = true; empty2 = true;
} else if (eval1(arg, &var2, &evalarg) == FAIL) { // Recursive! } else if (eval1(arg, &var2, evalarg) == FAIL) { // Recursive!
if (!empty1) { if (!empty1) {
tv_clear(&var1); tv_clear(&var1);
} }
@@ -3974,14 +4028,13 @@ void partial_unref(partial_T *pt)
/// Allocate a variable for a List and fill it from "*arg". /// Allocate a variable for a List and fill it from "*arg".
/// ///
/// @param arg "*arg" points to the "[".
/// @return OK or FAIL. /// @return OK or FAIL.
static int get_list_tv(char **arg, typval_T *rettv, const int flags) static int get_list_tv(char **arg, typval_T *rettv, evalarg_T *const evalarg)
{ {
const bool evaluate = flags & EVAL_EVALUATE; const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
list_T *l = NULL; list_T *l = NULL;
evalarg_T evalarg = { .eval_flags = flags };
if (evaluate) { if (evaluate) {
l = tv_list_alloc(kListLenShouldKnow); l = tv_list_alloc(kListLenShouldKnow);
} }
@@ -3989,7 +4042,7 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags)
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) { while (**arg != ']' && **arg != NUL) {
typval_T tv; typval_T tv;
if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
goto failret; goto failret;
} }
if (evaluate) { if (evaluate) {
@@ -3997,14 +4050,20 @@ static int get_list_tv(char **arg, typval_T *rettv, const int flags)
tv_list_append_owned_tv(l, tv); tv_list_append_owned_tv(l, tv);
} }
// the comma must come after the value
bool had_comma = **arg == ',';
if (had_comma) {
*arg = skipwhite(*arg + 1);
}
if (**arg == ']') { if (**arg == ']') {
break; break;
} }
if (**arg != ',') {
if (!had_comma) {
semsg(_("E696: Missing comma in List: %s"), *arg); semsg(_("E696: Missing comma in List: %s"), *arg);
goto failret; goto failret;
} }
*arg = skipwhite(*arg + 1);
} }
if (**arg != ']') { if (**arg != ']') {
@@ -4622,20 +4681,18 @@ static int get_literal_key(char **arg, typval_T *tv)
/// Allocate a variable for a Dictionary and fill it from "*arg". /// Allocate a variable for a Dictionary and fill it from "*arg".
/// ///
/// @param arg "*arg" points to the "{".
/// @param literal true for #{key: val} /// @param literal true for #{key: val}
/// @param flags can have EVAL_EVALUATE and other EVAL_ flags.
/// ///
/// @return OK or FAIL. Returns NOTDONE for {expr}. /// @return OK or FAIL. Returns NOTDONE for {expr}.
static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal) static int eval_dict(char **arg, typval_T *rettv, evalarg_T *const evalarg, bool literal)
{ {
const bool evaluate = flags & EVAL_EVALUATE; const bool evaluate = evalarg == NULL ? false : evalarg->eval_flags & EVAL_EVALUATE;
typval_T tv; typval_T tv;
char *key = NULL; char *key = NULL;
char *curly_expr = skipwhite(*arg + 1); char *curly_expr = skipwhite(*arg + 1);
char buf[NUMBUFLEN]; char buf[NUMBUFLEN];
evalarg_T evalarg = { .eval_flags = flags };
// First check if it's not a curly-braces expression: {expr}. // First check if it's not a curly-braces expression: {expr}.
// Must do this without evaluating, otherwise a function may be called // Must do this without evaluating, otherwise a function may be called
// twice. Unfortunately this means we need to call eval1() twice for the // twice. Unfortunately this means we need to call eval1() twice for the
@@ -4661,7 +4718,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
while (**arg != '}' && **arg != NUL) { while (**arg != '}' && **arg != NUL) {
if ((literal if ((literal
? get_literal_key(arg, &tvkey) ? get_literal_key(arg, &tvkey)
: eval1(arg, &tvkey, &evalarg)) == FAIL) { // recursive! : eval1(arg, &tvkey, evalarg)) == FAIL) { // recursive!
goto failret; goto failret;
} }
if (**arg != ':') { if (**arg != ':') {
@@ -4679,7 +4736,7 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
} }
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
if (eval1(arg, &tv, &evalarg) == FAIL) { // Recursive! if (eval1(arg, &tv, evalarg) == FAIL) { // Recursive!
if (evaluate) { if (evaluate) {
tv_clear(&tvkey); tv_clear(&tvkey);
} }
@@ -4702,14 +4759,19 @@ static int eval_dict(char **arg, typval_T *rettv, const int flags, bool literal)
} }
tv_clear(&tvkey); tv_clear(&tvkey);
// the comma must come after the value
bool had_comma = **arg == ',';
if (had_comma) {
*arg = skipwhite(*arg + 1);
}
if (**arg == '}') { if (**arg == '}') {
break; break;
} }
if (**arg != ',') { if (!had_comma) {
semsg(_("E722: Missing comma in Dictionary: %s"), *arg); semsg(_("E722: Missing comma in Dictionary: %s"), *arg);
goto failret; goto failret;
} }
*arg = skipwhite(*arg + 1);
} }
if (**arg != '}') { if (**arg != '}') {
@@ -6992,9 +7054,10 @@ int check_luafunc_name(const char *const str, const bool paren)
/// @param verbose give error messages /// @param verbose give error messages
/// @param start_leader start of '!' and '-' prefixes /// @param start_leader start of '!' and '-' prefixes
/// @param end_leaderp end of '!' and '-' prefixes /// @param end_leaderp end of '!' and '-' prefixes
int handle_subscript(const char **const arg, typval_T *rettv, const int flags, bool verbose) int handle_subscript(const char **const arg, typval_T *rettv, evalarg_T *const evalarg,
bool verbose)
{ {
const bool evaluate = flags & EVAL_EVALUATE; const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
int ret = OK; int ret = OK;
dict_T *selfdict = NULL; dict_T *selfdict = NULL;
const char *lua_funcname = NULL; const char *lua_funcname = NULL;
@@ -7023,7 +7086,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
&& !ascii_iswhite(*(*arg - 1))) && !ascii_iswhite(*(*arg - 1)))
|| (**arg == '-' && (*arg)[1] == '>'))) { || (**arg == '-' && (*arg)[1] == '>'))) {
if (**arg == '(') { if (**arg == '(') {
ret = call_func_rettv((char **)arg, rettv, evaluate, selfdict, NULL, lua_funcname); ret = call_func_rettv((char **)arg, evalarg, rettv, evaluate, selfdict, NULL, lua_funcname);
// Stop the expression evaluation when immediately aborting on // Stop the expression evaluation when immediately aborting on
// error, or when an interrupt occurred or an exception was thrown // error, or when an interrupt occurred or an exception was thrown
@@ -7039,10 +7102,10 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
} else if (**arg == '-') { } else if (**arg == '-') {
if ((*arg)[2] == '{') { if ((*arg)[2] == '{') {
// expr->{lambda}() // expr->{lambda}()
ret = eval_lambda((char **)arg, rettv, evaluate, verbose); ret = eval_lambda((char **)arg, rettv, evalarg, verbose);
} else { } else {
// expr->name() // expr->name()
ret = eval_method((char **)arg, rettv, evaluate, verbose); ret = eval_method((char **)arg, rettv, evalarg, verbose);
} }
} else { // **arg == '[' || **arg == '.' } else { // **arg == '[' || **arg == '.'
tv_dict_unref(selfdict); tv_dict_unref(selfdict);
@@ -7054,7 +7117,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, const int flags, b
} else { } else {
selfdict = NULL; selfdict = NULL;
} }
if (eval_index((char **)arg, rettv, flags, verbose) == FAIL) { if (eval_index((char **)arg, rettv, evalarg, verbose) == FAIL) {
tv_clear(rettv); tv_clear(rettv);
ret = FAIL; ret = FAIL;
} }
@@ -7426,8 +7489,9 @@ void ex_echo(exarg_T *eap)
bool need_clear = true; bool need_clear = true;
const int did_emsg_before = did_emsg; const int did_emsg_before = did_emsg;
const int called_emsg_before = called_emsg; const int called_emsg_before = called_emsg;
evalarg_T evalarg;
evalarg_T evalarg = { .eval_flags = eap->skip ? 0 : EVAL_EVALUATE }; fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) { if (eap->skip) {
emsg_skip++; emsg_skip++;
@@ -7479,6 +7543,7 @@ void ex_echo(exarg_T *eap)
arg = skipwhite(arg); arg = skipwhite(arg);
} }
eap->nextcmd = check_nextcmd(arg); eap->nextcmd = check_nextcmd(arg);
clear_evalarg(&evalarg, eap);
if (eap->skip) { if (eap->skip) {
emsg_skip--; emsg_skip--;
@@ -7517,7 +7582,7 @@ void ex_execute(exarg_T *eap)
emsg_skip++; emsg_skip++;
} }
while (*arg != NUL && *arg != '|' && *arg != '\n') { while (*arg != NUL && *arg != '|' && *arg != '\n') {
ret = eval1_emsg(&arg, &rettv, !eap->skip); ret = eval1_emsg(&arg, &rettv, eap);
if (ret == FAIL) { if (ret == FAIL) {
break; break;
} }

View File

@@ -272,9 +272,10 @@ typedef struct {
int eval_flags; ///< EVAL_ flag values below int eval_flags; ///< EVAL_ flag values below
/// copied from exarg_T when "getline" is "getsourceline". Can be NULL. /// copied from exarg_T when "getline" is "getsourceline". Can be NULL.
void *eval_cookie; // argument for getline() LineGetter eval_getline;
void *eval_cookie; ///< argument for eval_getline()
/// pointer to the line obtained with getsourceline() /// pointer to the last line obtained with getsourceline()
char *eval_tofree; char *eval_tofree;
} evalarg_T; } evalarg_T;
@@ -284,7 +285,7 @@ enum {
}; };
/// Passed to an eval() function to enable evaluation. /// Passed to an eval() function to enable evaluation.
EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL }); EXTERN evalarg_T EVALARG_EVALUATE INIT(= { EVAL_EVALUATE, NULL, NULL, NULL });
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h" # include "eval.h.generated.h"

View File

@@ -252,22 +252,23 @@ static void set_ufunc_name(ufunc_T *fp, char *name)
/// Parse a lambda expression and get a Funcref from "*arg". /// Parse a lambda expression and get a Funcref from "*arg".
/// ///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}. /// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
{ {
const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE);
garray_T newargs = GA_EMPTY_INIT_VALUE; garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs; garray_T *pnewargs;
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
partial_T *pt = NULL; partial_T *pt = NULL;
int varargs; int varargs;
int ret; int ret;
char *start = skipwhite(*arg + 1);
char *s, *e;
bool *old_eval_lavars = eval_lavars_used; bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false; bool eval_lavars = false;
char *tofree = NULL;
// First, check if this is a lambda expression. "->" must exists. // First, check if this is a lambda expression. "->" must exists.
ret = get_function_args(&start, '-', NULL, NULL, NULL, true); char *s = skipwhite(*arg + 1);
if (ret == FAIL || *start != '>') { ret = get_function_args(&s, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *s != '>') {
return NOTDONE; return NOTDONE;
} }
@@ -290,12 +291,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression. // Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1); *arg = skipwhite((*arg) + 1);
s = *arg; char *start = *arg;
ret = skip_expr(arg); ret = skip_expr(arg, evalarg);
char *end = *arg;
if (ret == FAIL) { if (ret == FAIL) {
goto errret; goto errret;
} }
e = *arg; if (evalarg != NULL) {
// avoid that the expression gets freed when another line break follows
tofree = evalarg->eval_tofree;
evalarg->eval_tofree = NULL;
}
*arg = skipwhite(*arg); *arg = skipwhite(*arg);
if (**arg != '}') { if (**arg != '}') {
semsg(_("E451: Expected }: %s"), *arg); semsg(_("E451: Expected }: %s"), *arg);
@@ -317,11 +324,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
ga_grow(&newlines, 1); ga_grow(&newlines, 1);
// Add "return " before the expression. // Add "return " before the expression.
size_t len = (size_t)(7 + e - s + 1); size_t len = (size_t)(7 + end - start + 1);
p = xmalloc(len); p = xmalloc(len);
((char **)(newlines.ga_data))[newlines.ga_len++] = p; ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return "); STRCPY(p, "return ");
xstrlcpy(p + 7, s, (size_t)(e - s) + 1); xstrlcpy(p + 7, start, (size_t)(end - start) + 1);
if (strstr(p + 7, "a:") == NULL) { if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure. // No a: variables are used for sure.
flags |= FC_NOARGS; flags |= FC_NOARGS;
@@ -359,12 +366,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
} }
eval_lavars_used = old_eval_lavars; eval_lavars_used = old_eval_lavars;
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
evalarg->eval_tofree = tofree;
} else {
xfree(tofree);
}
return OK; return OK;
errret: errret:
ga_clear_strings(&newargs); ga_clear_strings(&newargs);
xfree(fp); xfree(fp);
xfree(pt); xfree(pt);
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
evalarg->eval_tofree = tofree;
} else {
xfree(tofree);
}
eval_lavars_used = old_eval_lavars; eval_lavars_used = old_eval_lavars;
return FAIL; return FAIL;
} }
@@ -448,15 +465,14 @@ void emsg_funcname(const char *errmsg, const char *name)
/// @param funcexe various values /// @param funcexe various values
/// ///
/// @return OK or FAIL. /// @return OK or FAIL.
int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe) int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg,
funcexe_T *funcexe)
{ {
char *argp; char *argp;
int ret = OK; int ret = OK;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found int argcount = 0; // number of arguments found
evalarg_T evalarg = { .eval_flags = funcexe->fe_evaluate ? EVAL_EVALUATE : 0 };
// Get the arguments. // Get the arguments.
argp = *arg; argp = *arg;
while (argcount < MAX_FUNC_ARGS while (argcount < MAX_FUNC_ARGS
@@ -465,7 +481,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_
if (*argp == ')' || *argp == ',' || *argp == NUL) { if (*argp == ')' || *argp == ',' || *argp == NUL) {
break; break;
} }
if (eval1(&argp, &argvars[argcount], &evalarg) == FAIL) { if (eval1(&argp, &argvars[argcount], evalarg) == FAIL) {
ret = FAIL; ret = FAIL;
break; break;
} }
@@ -2986,6 +3002,7 @@ void ex_return(exarg_T *eap)
if (eap->skip) { if (eap->skip) {
emsg_skip--; emsg_skip--;
} }
clear_evalarg(&evalarg, eap);
} }
/// ":1,25call func(arg1, arg2)" function call. /// ":1,25call func(arg1, arg2)" function call.
@@ -3002,16 +3019,19 @@ void ex_call(exarg_T *eap)
bool failed = false; bool failed = false;
funcdict_T fudi; funcdict_T fudi;
partial_T *partial = NULL; partial_T *partial = NULL;
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap->skip);
if (eap->skip) { if (eap->skip) {
// trans_function_name() doesn't work well when skipping, use eval0() // trans_function_name() doesn't work well when skipping, use eval0()
// instead to skip to any following command, e.g. for: // instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif. // :if 0 | call dict.foo().bar() | endif.
emsg_skip++; emsg_skip++;
if (eval0(eap->arg, &rettv, eap, NULL) != FAIL) { if (eval0(eap->arg, &rettv, eap, &evalarg) != FAIL) {
tv_clear(&rettv); tv_clear(&rettv);
} }
emsg_skip--; emsg_skip--;
clear_evalarg(&evalarg, eap);
return; return;
} }
@@ -3069,14 +3089,13 @@ void ex_call(exarg_T *eap)
funcexe.fe_evaluate = true; funcexe.fe_evaluate = true;
funcexe.fe_partial = partial; funcexe.fe_partial = partial;
funcexe.fe_selfdict = fudi.fd_dict; funcexe.fe_selfdict = fudi.fd_dict;
if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { if (get_func_tv(name, -1, &rettv, &arg, &evalarg, &funcexe) == FAIL) {
failed = true; failed = true;
break; break;
} }
// Handle a function returning a Funcref, Dictionary or List. // Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript((const char **)&arg, &rettv, EVAL_EVALUATE, true) if (handle_subscript((const char **)&arg, &rettv, &EVALARG_EVALUATE, true) == FAIL) {
== FAIL) {
failed = true; failed = true;
break; break;
} }
@@ -3108,6 +3127,7 @@ void ex_call(exarg_T *eap)
eap->nextcmd = check_nextcmd(arg); eap->nextcmd = check_nextcmd(arg);
} }
} }
clear_evalarg(&evalarg, eap);
end: end:
tv_dict_unref(fudi.fd_dict); tv_dict_unref(fudi.fd_dict);

View File

@@ -4,6 +4,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include "nvim/eval.h"
#include "nvim/eval/typval.h" #include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h" #include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h" #include "nvim/ex_cmds_defs.h"

View File

@@ -259,14 +259,13 @@ void ex_let(exarg_T *eap)
if (eap->skip) { if (eap->skip) {
emsg_skip++; emsg_skip++;
} }
evalarg_T evalarg = { evalarg_T evalarg;
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE, fill_evalarg_from_eap(&evalarg, eap, eap->skip);
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL,
};
int eval_res = eval0(expr, &rettv, eap, &evalarg); int eval_res = eval0(expr, &rettv, eap, &evalarg);
if (eap->skip) { if (eap->skip) {
emsg_skip--; emsg_skip--;
} }
clear_evalarg(&evalarg, eap);
if (!eap->skip && eval_res != FAIL) { if (!eap->skip && eval_res != FAIL) {
(void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op); (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, is_const, op);
@@ -510,7 +509,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first)
} else { } else {
// handle d.key, l[idx], f(expr) // handle d.key, l[idx], f(expr)
const char *const arg_subsc = arg; const char *const arg_subsc = arg;
if (handle_subscript(&arg, &tv, EVAL_EVALUATE, true) == FAIL) { if (handle_subscript(&arg, &tv, &EVALARG_EVALUATE, true) == FAIL) {
error = true; error = true;
} else { } else {
if (arg == arg_subsc && len == 2 && name[1] == ':') { if (arg == arg_subsc && len == 2 && name[1] == ':') {
@@ -1717,7 +1716,7 @@ bool var_exists(const char *var)
n = get_var_tv(name, len, &tv, NULL, false, true) == OK; n = get_var_tv(name, len, &tv, NULL, false, true) == OK;
if (n) { if (n) {
// Handle d.key, l[idx], f(expr). // Handle d.key, l[idx], f(expr).
n = handle_subscript(&var, &tv, EVAL_EVALUATE, false) == OK; n = handle_subscript(&var, &tv, &EVALARG_EVALUATE, false) == OK;
if (n) { if (n) {
tv_clear(&tv); tv_clear(&tv);
} }

View File

@@ -484,24 +484,6 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
} }
} }
if (cstack.cs_looplevel > 0) {
// Inside a while/for loop we need to store the lines and use them
// again. Pass a different "fgetline" function to do_one_cmd()
// below, so that it stores lines in or reads them from
// "lines_ga". Makes it possible to define a function inside a
// while/for loop.
cmd_getline = get_loop_line;
cmd_cookie = (void *)&cmd_loop_cookie;
cmd_loop_cookie.lines_gap = &lines_ga;
cmd_loop_cookie.current_line = current_line;
cmd_loop_cookie.getline = fgetline;
cmd_loop_cookie.cookie = cookie;
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
} else {
cmd_getline = fgetline;
cmd_cookie = cookie;
}
// 2. If no line given, get an allocated line with fgetline(). // 2. If no line given, get an allocated line with fgetline().
if (next_cmdline == NULL) { if (next_cmdline == NULL) {
// Need to set msg_didout for the first line after an ":if", // Need to set msg_didout for the first line after an ":if",
@@ -540,15 +522,37 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
} }
cmdline_copy = next_cmdline; cmdline_copy = next_cmdline;
// Save the current line when inside a ":while" or ":for", and when int current_line_before = 0;
// the command looks like a ":while" or ":for", because we may need it // Inside a while/for loop, and when the command looks like a ":while"
// later. When there is a '|' and another command, it is stored // or ":for", the line is stored, because we may need it later when
// separately, because we need to be able to jump back to it from an // looping.
//
// When there is a '|' and another command, it is stored separately,
// because we need to be able to jump back to it from an
// :endwhile/:endfor. // :endwhile/:endfor.
if (current_line == lines_ga.ga_len //
&& (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) { // Pass a different "fgetline" function to do_one_cmd() below,
// that it stores lines in or reads them from "lines_ga". Makes it
// possible to define a function inside a while/for loop.
if ((cstack.cs_looplevel > 0 || has_loop_cmd(next_cmdline))) {
cmd_getline = get_loop_line;
cmd_cookie = (void *)&cmd_loop_cookie;
cmd_loop_cookie.lines_gap = &lines_ga;
cmd_loop_cookie.current_line = current_line;
cmd_loop_cookie.getline = fgetline;
cmd_loop_cookie.cookie = cookie;
cmd_loop_cookie.repeating = (current_line < lines_ga.ga_len);
// Save the current line when encountering it the first time.
if (current_line == lines_ga.ga_len) {
store_loop_line(&lines_ga, next_cmdline); store_loop_line(&lines_ga, next_cmdline);
} }
current_line_before = current_line;
} else {
cmd_getline = fgetline;
cmd_cookie = cookie;
}
did_endif = false; did_endif = false;
if (count++ == 0) { if (count++ == 0) {
@@ -651,7 +655,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
} else if (cstack.cs_lflags & CSL_HAD_LOOP) { } else if (cstack.cs_lflags & CSL_HAD_LOOP) {
// For a ":while" or ":for" we need to remember the line number. // For a ":while" or ":for" we need to remember the line number.
cstack.cs_lflags &= ~CSL_HAD_LOOP; cstack.cs_lflags &= ~CSL_HAD_LOOP;
cstack.cs_line[cstack.cs_idx] = current_line - 1; cstack.cs_line[cstack.cs_idx] = current_line_before;
} }
} }
@@ -3751,7 +3755,7 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
// Skip over `=expr`, wildcards in it are not expanded. // Skip over `=expr`, wildcards in it are not expanded.
if (p[0] == '`' && p[1] == '=') { if (p[0] == '`' && p[1] == '=') {
p += 2; p += 2;
(void)skip_expr(&p); (void)skip_expr(&p, NULL);
if (*p == '`') { if (*p == '`') {
p++; p++;
} }
@@ -3970,7 +3974,7 @@ void separate_nextcmd(exarg_T *eap)
} else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) { } else if (p[0] == '`' && p[1] == '=' && (eap->argt & EX_XFILE)) {
// Skip over `=expr` when wildcards are expanded. // Skip over `=expr` when wildcards are expanded.
p += 2; p += 2;
(void)skip_expr(&p); (void)skip_expr(&p, NULL);
if (*p == NUL) { // stop at NUL after CTRL-V if (*p == NUL) { // stop at NUL after CTRL-V
break; break;
} }

View File

@@ -792,13 +792,15 @@ void report_discard_pending(int pending, void *value)
void ex_eval(exarg_T *eap) void ex_eval(exarg_T *eap)
{ {
typval_T tv; typval_T tv;
evalarg_T evalarg = { evalarg_T evalarg;
.eval_flags = eap->skip ? 0 : EVAL_EVALUATE,
.eval_cookie = eap->getline == getsourceline ? eap->cookie : NULL, fill_evalarg_from_eap(&evalarg, eap, eap->skip);
};
if (eval0(eap->arg, &tv, eap, &evalarg) == OK) { if (eval0(eap->arg, &tv, eap, &evalarg) == OK) {
tv_clear(&tv); tv_clear(&tv);
} }
clear_evalarg(&evalarg, eap);
} }
/// Handle ":if". /// Handle ":if".
@@ -955,13 +957,12 @@ void ex_while(exarg_T *eap)
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
int skip = CHECK_SKIP; int skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) { if (eap->cmdidx == CMD_while) { // ":while bool-expr"
// ":while bool-expr"
result = eval_to_bool(eap->arg, &error, eap, skip); result = eval_to_bool(eap->arg, &error, eap, skip);
} else { } else { // ":for var in list-expr"
evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, skip);
void *fi; void *fi;
// ":for var in list-expr"
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) { if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) {
// Jumping here from a ":continue" or ":endfor": use the // Jumping here from a ":continue" or ":endfor": use the
// previously evaluated list. // previously evaluated list.
@@ -969,7 +970,7 @@ void ex_while(exarg_T *eap)
error = false; error = false;
} else { } else {
// Evaluate the argument and get the info in a structure. // Evaluate the argument and get the info in a structure.
fi = eval_for_line(eap->arg, &error, eap, skip); fi = eval_for_line(eap->arg, &error, eap, &evalarg);
cstack->cs_forinfo[cstack->cs_idx] = fi; cstack->cs_forinfo[cstack->cs_idx] = fi;
} }
@@ -984,6 +985,7 @@ void ex_while(exarg_T *eap)
free_for_info(fi); free_for_info(fi);
cstack->cs_forinfo[cstack->cs_idx] = NULL; cstack->cs_forinfo[cstack->cs_idx] = NULL;
} }
clear_evalarg(&evalarg, eap);
} }
// If this cstack entry was just initialised and is active, set the // If this cstack entry was just initialised and is active, set the

View File

@@ -5266,7 +5266,7 @@ int option_set_callback_func(char *optval, Callback *optcb)
|| (strncmp(optval, "function(", 9) == 0) || (strncmp(optval, "function(", 9) == 0)
|| (strncmp(optval, "funcref(", 8) == 0)) { || (strncmp(optval, "funcref(", 8) == 0)) {
// Lambda expression or a funcref // Lambda expression or a funcref
tv = eval_expr(optval); tv = eval_expr(optval, NULL);
if (tv == NULL) { if (tv == NULL) {
return FAIL; return FAIL;
} }

View File

@@ -603,7 +603,7 @@ void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool es
if (src[0] == '`' && src[1] == '=') { if (src[0] == '`' && src[1] == '=') {
var = src; var = src;
src += 2; src += 2;
(void)skip_expr(&src); (void)skip_expr(&src, NULL);
if (*src == '`') { if (*src == '`') {
src++; src++;
} }

View File

@@ -6957,15 +6957,15 @@ void ex_cexpr(exarg_T *eap)
// Evaluate the expression. When the result is a string or a list we can // Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist. // use it to fill the errorlist.
typval_T tv; typval_T *tv = eval_expr(eap->arg, eap);
if (eval0(eap->arg, &tv, eap, &EVALARG_EVALUATE) == FAIL) { if (tv == NULL) {
return; return;
} }
if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
|| tv.v_type == VAR_LIST) { || tv->v_type == VAR_LIST) {
incr_quickfix_busy(); incr_quickfix_busy();
int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, &tv, p_efm, int res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
(eap->cmdidx != CMD_caddexpr (eap->cmdidx != CMD_caddexpr
&& eap->cmdidx != CMD_laddexpr), && eap->cmdidx != CMD_laddexpr),
(linenr_T)0, (linenr_T)0, (linenr_T)0, (linenr_T)0,
@@ -6996,7 +6996,7 @@ void ex_cexpr(exarg_T *eap)
emsg(_("E777: String or List expected")); emsg(_("E777: String or List expected"));
} }
cleanup: cleanup:
tv_clear(&tv); tv_free(tv);
} }
// Get the location list for ":lhelpgrep" // Get the location list for ":lhelpgrep"

View File

@@ -183,6 +183,7 @@ func Test_argument()
let save_columns = &columns let save_columns = &columns
let &columns = 79 let &columns = 79
try
exe 'args ' .. join(range(1, 81)) exe 'args ' .. join(range(1, 81))
call assert_equal(join([ call assert_equal(join([
\ '', \ '',
@@ -198,7 +199,9 @@ func Test_argument()
let long_arg = repeat('X', 81) let long_arg = repeat('X', 81)
exe 'args ' .. long_arg exe 'args ' .. long_arg
call assert_equal("\n[".long_arg.']', execute('args')) call assert_equal("\n[".long_arg.']', execute('args'))
finally
let &columns = save_columns let &columns = save_columns
endtry
" Setting argument list should fail when the current buffer has unsaved " Setting argument list should fail when the current buffer has unsaved
" changes " changes