1
0
forked from aniani/vim

patch 8.2.0753: Vim9: expressions are evaluated in the discovery phase

Problem:    Vim9: expressions are evaluated in the discovery phase.
Solution:   Bail out if an expression is not a constant.  Require a type for
            declared constants.
This commit is contained in:
Bram Moolenaar 2020-05-14 22:41:15 +02:00
parent e06a28f5e3
commit 32e351179e
12 changed files with 214 additions and 131 deletions

View File

@ -791,8 +791,9 @@ get_literal_key(char_u **arg, typval_T *tv)
* Return OK or FAIL. Returns NOTDONE for {expr}.
*/
int
eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
eval_dict(char_u **arg, typval_T *rettv, int flags, int literal)
{
int evaluate = flags & EVAL_EVALUATE;
dict_T *d = NULL;
typval_T tvkey;
typval_T tv;
@ -800,6 +801,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
dictitem_T *item;
char_u *start = skipwhite(*arg + 1);
char_u buf[NUMBUFLEN];
int vim9script = current_sctx.sc_version == SCRIPT_VERSION_VIM9;
/*
* First check if it's not a curly-braces thing: {expr}.
@ -808,9 +810,9 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
* first item.
* But {} is an empty Dictionary.
*/
if (*start != '}')
if (!vim9script && *start != '}')
{
if (eval1(&start, &tv, FALSE) == FAIL) // recursive!
if (eval1(&start, &tv, 0) == FAIL) // recursive!
return FAIL;
if (*start == '}')
return NOTDONE;
@ -830,7 +832,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
{
if ((literal
? get_literal_key(arg, &tvkey)
: eval1(arg, &tvkey, evaluate)) == FAIL) // recursive!
: eval1(arg, &tvkey, flags)) == FAIL) // recursive!
goto failret;
if (**arg != ':')
@ -852,7 +854,7 @@ eval_dict(char_u **arg, typval_T *rettv, int evaluate, int literal)
}
*arg = skipwhite(*arg + 1);
if (eval1(arg, &tv, evaluate) == FAIL) // recursive!
if (eval1(arg, &tv, flags) == FAIL) // recursive!
{
if (evaluate)
clear_tv(&tvkey);

View File

@ -48,12 +48,12 @@ typedef struct
} forinfo_T;
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
static int eval2(char_u **arg, typval_T *rettv, int evaluate);
static int eval3(char_u **arg, typval_T *rettv, int evaluate);
static int eval4(char_u **arg, typval_T *rettv, int evaluate);
static int eval5(char_u **arg, typval_T *rettv, int evaluate);
static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string);
static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string);
static int eval2(char_u **arg, typval_T *rettv, int flags);
static int eval3(char_u **arg, typval_T *rettv, int flags);
static int eval4(char_u **arg, typval_T *rettv, int flags);
static int eval5(char_u **arg, typval_T *rettv, int flags);
static int eval6(char_u **arg, typval_T *rettv, int flags, int want_string);
static int eval7(char_u **arg, typval_T *rettv, int flags, int want_string);
static int eval7_leader(typval_T *rettv, char_u *start_leader, char_u **end_leaderp);
static int free_unref_items(int copyID);
@ -173,7 +173,7 @@ eval_to_bool(
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, !skip) == FAIL)
if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL)
*error = TRUE;
else
{
@ -201,7 +201,7 @@ eval1_emsg(char_u **arg, typval_T *rettv, int evaluate)
int did_emsg_before = did_emsg;
int called_emsg_before = called_emsg;
ret = eval1(arg, rettv, evaluate);
ret = eval1(arg, rettv, evaluate ? EVAL_EVALUATE : 0);
if (ret == FAIL)
{
// Report the invalid expression unless the expression evaluation has
@ -315,7 +315,7 @@ eval_to_string_skip(
if (skip)
++emsg_skip;
if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip)
if (eval0(arg, &tv, nextcmd, skip ? 0 : EVAL_EVALUATE) == FAIL || skip)
retval = NULL;
else
{
@ -338,7 +338,7 @@ skip_expr(char_u **pp)
typval_T rettv;
*pp = skipwhite(*pp);
return eval1(pp, &rettv, FALSE);
return eval1(pp, &rettv, 0);
}
/*
@ -360,7 +360,7 @@ eval_to_string(
char_u numbuf[NUMBUFLEN];
#endif
if (eval0(arg, &tv, nextcmd, TRUE) == FAIL)
if (eval0(arg, &tv, nextcmd, EVAL_EVALUATE) == FAIL)
retval = NULL;
else
{
@ -430,7 +430,7 @@ eval_to_number(char_u *expr)
++emsg_off;
if (eval1(&p, &rettv, TRUE) == FAIL)
if (eval1(&p, &rettv, EVAL_EVALUATE) == FAIL)
retval = -1;
else
{
@ -453,7 +453,7 @@ eval_expr(char_u *arg, char_u **nextcmd)
typval_T *tv;
tv = ALLOC_ONE(typval_T);
if (tv != NULL && eval0(arg, tv, nextcmd, TRUE) == FAIL)
if (tv != NULL && eval0(arg, tv, nextcmd, EVAL_EVALUATE) == FAIL)
VIM_CLEAR(tv);
return tv;
@ -578,7 +578,7 @@ eval_foldexpr(char_u *arg, int *cp)
++sandbox;
++textwinlock;
*cp = NUL;
if (eval0(arg, &tv, NULL, TRUE) == FAIL)
if (eval0(arg, &tv, NULL, EVAL_EVALUATE) == FAIL)
retval = 0;
else
{
@ -766,7 +766,7 @@ get_lval(
else
{
empty1 = FALSE;
if (eval1(&p, &var1, TRUE) == FAIL) // recursive!
if (eval1(&p, &var1, EVAL_EVALUATE) == FAIL) // recursive!
return NULL;
if (tv_get_string_chk(&var1) == NULL)
{
@ -803,7 +803,8 @@ get_lval(
else
{
lp->ll_empty2 = FALSE;
if (eval1(&p, &var2, TRUE) == FAIL) // recursive!
// recursive!
if (eval1(&p, &var2, EVAL_EVALUATE) == FAIL)
{
clear_tv(&var1);
return NULL;
@ -1433,7 +1434,8 @@ eval_for_line(
if (skip)
++emsg_skip;
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK)
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, skip ? 0 : EVAL_EVALUATE)
== OK)
{
*errp = FALSE;
if (!skip)
@ -1694,9 +1696,10 @@ eval_func(
char_u *name,
int name_len,
typval_T *rettv,
int evaluate,
int flags,
typval_T *basetv) // "expr" for "expr->name(arg)"
{
int evaluate = flags & EVAL_EVALUATE;
char_u *s = name;
int len = name_len;
partial_T *partial;
@ -1712,7 +1715,7 @@ eval_func(
// Need to make a copy, in case evaluating the arguments makes
// the name invalid.
s = vim_strsave(s);
if (s == NULL)
if (s == NULL || (flags & EVAL_CONSTANT))
ret = FAIL;
else
{
@ -1761,6 +1764,7 @@ eval_func(
* This calls eval1() and handles error message and nextcmd.
* Put the result in "rettv" when returning OK and "evaluate" is TRUE.
* Note: "rettv.v_lock" is not set.
* "flags" has EVAL_EVALUATE and similar flags.
* Return OK or FAIL.
*/
int
@ -1768,7 +1772,7 @@ eval0(
char_u *arg,
typval_T *rettv,
char_u **nextcmd,
int evaluate)
int flags)
{
int ret;
char_u *p;
@ -1776,7 +1780,7 @@ eval0(
int called_emsg_before = called_emsg;
p = skipwhite(arg);
ret = eval1(&p, rettv, evaluate);
ret = eval1(&p, rettv, flags);
if (ret == FAIL || !ends_excmd2(arg, p))
{
if (ret != FAIL)
@ -1787,8 +1791,10 @@ eval0(
* exception, or we already gave a more specific error.
* Also check called_emsg for when using assert_fails().
*/
if (!aborting() && did_emsg == did_emsg_before
&& called_emsg == called_emsg_before)
if (!aborting()
&& did_emsg == did_emsg_before
&& called_emsg == called_emsg_before
&& (flags & EVAL_CONSTANT) == 0)
semsg(_(e_invexpr2), arg);
ret = FAIL;
}
@ -1810,7 +1816,7 @@ eval0(
* Return OK or FAIL.
*/
int
eval1(char_u **arg, typval_T *rettv, int evaluate)
eval1(char_u **arg, typval_T *rettv, int flags)
{
int result;
typval_T var2;
@ -1818,13 +1824,15 @@ eval1(char_u **arg, typval_T *rettv, int evaluate)
/*
* Get the first variable.
*/
if (eval2(arg, rettv, evaluate) == FAIL)
if (eval2(arg, rettv, flags) == FAIL)
return FAIL;
if ((*arg)[0] == '?')
{
int evaluate = flags & EVAL_EVALUATE;
result = FALSE;
if (evaluate)
if (flags & EVAL_EVALUATE)
{
int error = FALSE;
@ -1836,10 +1844,10 @@ eval1(char_u **arg, typval_T *rettv, int evaluate)
}
/*
* Get the second variable.
* Get the second variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, rettv, evaluate && result) == FAIL) // recursive!
if (eval1(arg, rettv, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
return FAIL;
/*
@ -1854,10 +1862,10 @@ eval1(char_u **arg, typval_T *rettv, int evaluate)
}
/*
* Get the third variable.
* Get the third variable. Recursive!
*/
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2, evaluate && !result) == FAIL) // recursive!
if (eval1(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
{
if (evaluate && result)
clear_tv(rettv);
@ -1880,7 +1888,7 @@ eval1(char_u **arg, typval_T *rettv, int evaluate)
* Return OK or FAIL.
*/
static int
eval2(char_u **arg, typval_T *rettv, int evaluate)
eval2(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
long result;
@ -1890,7 +1898,7 @@ eval2(char_u **arg, typval_T *rettv, int evaluate)
/*
* Get the first variable.
*/
if (eval3(arg, rettv, evaluate) == FAIL)
if (eval3(arg, rettv, flags) == FAIL)
return FAIL;
/*
@ -1900,6 +1908,8 @@ eval2(char_u **arg, typval_T *rettv, int evaluate)
result = FALSE;
while ((*arg)[0] == '|' && (*arg)[1] == '|')
{
int evaluate = flags & EVAL_EVALUATE;
if (evaluate && first)
{
if (tv_get_number_chk(rettv, &error) != 0)
@ -1914,7 +1924,8 @@ eval2(char_u **arg, typval_T *rettv, int evaluate)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval3(arg, &var2, evaluate && !result) == FAIL)
if (eval3(arg, &var2, !result ? flags : flags & ~EVAL_EVALUATE)
== FAIL)
return FAIL;
/*
@ -1948,7 +1959,7 @@ eval2(char_u **arg, typval_T *rettv, int evaluate)
* Return OK or FAIL.
*/
static int
eval3(char_u **arg, typval_T *rettv, int evaluate)
eval3(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
long result;
@ -1958,7 +1969,7 @@ eval3(char_u **arg, typval_T *rettv, int evaluate)
/*
* Get the first variable.
*/
if (eval4(arg, rettv, evaluate) == FAIL)
if (eval4(arg, rettv, flags) == FAIL)
return FAIL;
/*
@ -1968,6 +1979,8 @@ eval3(char_u **arg, typval_T *rettv, int evaluate)
result = TRUE;
while ((*arg)[0] == '&' && (*arg)[1] == '&')
{
int evaluate = flags & EVAL_EVALUATE;
if (evaluate && first)
{
if (tv_get_number_chk(rettv, &error) == 0)
@ -1982,7 +1995,7 @@ eval3(char_u **arg, typval_T *rettv, int evaluate)
* Get the second variable.
*/
*arg = skipwhite(*arg + 2);
if (eval4(arg, &var2, evaluate && result) == FAIL)
if (eval4(arg, &var2, result ? flags : flags & ~EVAL_EVALUATE) == FAIL)
return FAIL;
/*
@ -2025,7 +2038,7 @@ eval3(char_u **arg, typval_T *rettv, int evaluate)
* Return OK or FAIL.
*/
static int
eval4(char_u **arg, typval_T *rettv, int evaluate)
eval4(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
char_u *p;
@ -2037,7 +2050,7 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
/*
* Get the first variable.
*/
if (eval5(arg, rettv, evaluate) == FAIL)
if (eval5(arg, rettv, flags) == FAIL)
return FAIL;
p = *arg;
@ -2105,12 +2118,12 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
* Get the second variable.
*/
*arg = skipwhite(p + len);
if (eval5(arg, &var2, evaluate) == FAIL)
if (eval5(arg, &var2, flags) == FAIL)
{
clear_tv(rettv);
return FAIL;
}
if (evaluate)
if (flags & EVAL_EVALUATE)
{
int ret = typval_compare(rettv, &var2, type, ic);
@ -2172,7 +2185,7 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
* Return OK or FAIL.
*/
static int
eval5(char_u **arg, typval_T *rettv, int evaluate)
eval5(char_u **arg, typval_T *rettv, int flags)
{
typval_T var2;
int op;
@ -2188,7 +2201,7 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
/*
* Get the first variable.
*/
if (eval6(arg, rettv, evaluate, FALSE) == FAIL)
if (eval6(arg, rettv, flags, FALSE) == FAIL)
return FAIL;
/*
@ -2217,7 +2230,7 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
// we know that the first operand needs to be a string or number
// without evaluating the 2nd operand. So check before to avoid
// side effects after an error.
if (evaluate && tv_get_string_chk(rettv) == NULL)
if ((flags & EVAL_EVALUATE) && tv_get_string_chk(rettv) == NULL)
{
clear_tv(rettv);
return FAIL;
@ -2230,13 +2243,13 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
if (op == '.' && *(*arg + 1) == '.') // .. string concatenation
++*arg;
*arg = skipwhite(*arg + 1);
if (eval6(arg, &var2, evaluate, op == '.') == FAIL)
if (eval6(arg, &var2, flags, op == '.') == FAIL)
{
clear_tv(rettv);
return FAIL;
}
if (evaluate)
if (flags & EVAL_EVALUATE)
{
/*
* Compute the result.
@ -2358,7 +2371,7 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
eval6(
char_u **arg,
typval_T *rettv,
int evaluate,
int flags,
int want_string) // after "." operator
{
typval_T var2;
@ -2373,7 +2386,7 @@ eval6(
/*
* Get the first variable.
*/
if (eval7(arg, rettv, evaluate, want_string) == FAIL)
if (eval7(arg, rettv, flags, want_string) == FAIL)
return FAIL;
/*
@ -2385,7 +2398,7 @@ eval6(
if (op != '*' && op != '/' && op != '%')
break;
if (evaluate)
if (flags & EVAL_EVALUATE)
{
#ifdef FEAT_FLOAT
if (rettv->v_type == VAR_FLOAT)
@ -2408,10 +2421,10 @@ eval6(
* Get the second variable.
*/
*arg = skipwhite(*arg + 1);
if (eval7(arg, &var2, evaluate, FALSE) == FAIL)
if (eval7(arg, &var2, flags, FALSE) == FAIL)
return FAIL;
if (evaluate)
if (flags & EVAL_EVALUATE)
{
#ifdef FEAT_FLOAT
if (var2.v_type == VAR_FLOAT)
@ -2528,9 +2541,10 @@ eval6(
eval7(
char_u **arg,
typval_T *rettv,
int evaluate,
int flags,
int want_string) // after "." operator
{
int evaluate = flags & EVAL_EVALUATE;
int len;
char_u *s;
char_u *start_leader, *end_leader;
@ -2595,7 +2609,7 @@ eval7(
/*
* List: [expr, expr]
*/
case '[': ret = get_list_tv(arg, rettv, evaluate, TRUE);
case '[': ret = get_list_tv(arg, rettv, flags, TRUE);
break;
/*
@ -2604,7 +2618,7 @@ eval7(
case '#': if ((*arg)[1] == '{')
{
++*arg;
ret = eval_dict(arg, rettv, evaluate, TRUE);
ret = eval_dict(arg, rettv, flags, TRUE);
}
else
ret = NOTDONE;
@ -2616,7 +2630,7 @@ eval7(
*/
case '{': ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE)
ret = eval_dict(arg, rettv, evaluate, FALSE);
ret = eval_dict(arg, rettv, flags, FALSE);
break;
/*
@ -2649,7 +2663,7 @@ eval7(
* nested expression: (expression).
*/
case '(': *arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, evaluate); // recursive!
ret = eval1(arg, rettv, flags); // recursive!
if (**arg == ')')
++*arg;
else if (ret == OK)
@ -2680,7 +2694,7 @@ eval7(
else
{
if (**arg == '(') // recursive!
ret = eval_func(arg, s, len, rettv, evaluate, NULL);
ret = eval_func(arg, s, len, rettv, flags, NULL);
else if (evaluate)
ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE);
else
@ -2697,7 +2711,7 @@ eval7(
// Handle following '[', '(' and '.' for expr[expr], expr.name,
// expr(expr), expr->name(expr)
if (ret == OK)
ret = handle_subscript(arg, rettv, evaluate, TRUE,
ret = handle_subscript(arg, rettv, flags, TRUE,
start_leader, &end_leader);
/*
@ -2919,7 +2933,8 @@ eval_method(
ret = FAIL;
}
else
ret = eval_func(arg, name, len, rettv, evaluate, &base);
ret = eval_func(arg, name, len, rettv,
evaluate ? EVAL_EVALUATE : 0, &base);
}
// Clear the funcref afterwards, so that deleting it while
@ -2939,9 +2954,10 @@ eval_method(
eval_index(
char_u **arg,
typval_T *rettv,
int evaluate,
int flags,
int verbose) // give error messages
{
int evaluate = flags & EVAL_EVALUATE;
int empty1 = FALSE, empty2 = FALSE;
typval_T var1, var2;
long i;
@ -3010,7 +3026,7 @@ eval_index(
*arg = skipwhite(*arg + 1);
if (**arg == ':')
empty1 = TRUE;
else if (eval1(arg, &var1, evaluate) == FAIL) // recursive!
else if (eval1(arg, &var1, flags) == FAIL) // recursive!
return FAIL;
else if (evaluate && tv_get_string_chk(&var1) == NULL)
{
@ -3028,7 +3044,7 @@ eval_index(
*arg = skipwhite(*arg + 1);
if (**arg == ']')
empty2 = TRUE;
else if (eval1(arg, &var2, evaluate) == FAIL) // recursive!
else if (eval1(arg, &var2, flags) == FAIL) // recursive!
{
if (!empty1)
clear_tv(&var1);
@ -5310,11 +5326,12 @@ eval_isnamec1(int c)
handle_subscript(
char_u **arg,
typval_T *rettv,
int evaluate, // do more than finding the end
int flags, // do more than finding the end
int verbose, // give error messages
char_u *start_leader, // start of '!' and '-' prefixes
char_u **end_leaderp) // end of '!' and '-' prefixes
{
int evaluate = flags & EVAL_EVALUATE;
int ret = OK;
dict_T *selfdict = NULL;
@ -5374,7 +5391,7 @@ handle_subscript(
}
else
selfdict = NULL;
if (eval_index(arg, rettv, evaluate, verbose) == FAIL)
if (eval_index(arg, rettv, flags, verbose) == FAIL)
{
clear_tv(rettv);
ret = FAIL;
@ -6108,7 +6125,7 @@ ex_echo(exarg_T *eap)
need_clr_eos = needclr;
p = arg;
if (eval1(&arg, &rettv, !eap->skip) == FAIL)
if (eval1(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE) == FAIL)
{
/*
* Report the invalid expression unless the expression evaluation

View File

@ -2132,7 +2132,7 @@ f_eval(typval_T *argvars, typval_T *rettv)
s = skipwhite(s);
p = s;
if (s == NULL || eval1(&s, rettv, TRUE) == FAIL)
if (s == NULL || eval1(&s, rettv, EVAL_EVALUATE) == FAIL)
{
if (p != NULL && !aborting())
semsg(_(e_invexpr2), p);

View File

@ -433,7 +433,7 @@ eval_spell_expr(char_u *badword, char_u *expr)
if (p_verbose == 0)
++emsg_off;
if (eval1(&p, &rettv, TRUE) == OK)
if (eval1(&p, &rettv, EVAL_EVALUATE) == OK)
{
if (rettv.v_type != VAR_LIST)
clear_tv(&rettv);
@ -701,11 +701,14 @@ ex_const(exarg_T *eap)
}
/*
* When "redefine" is TRUE the command will be executed again, redefining the
* variable is OK then.
* When "discovery" is TRUE the ":let" or ":const" is encountered during the
* discovery phase of vim9script:
* - The command will be executed again, redefining the variable is OK then.
* - The expresion argument must be a constant.
* - If no constant expression a type must be specified.
*/
void
ex_let_const(exarg_T *eap, int redefine)
ex_let_const(exarg_T *eap, int discovery)
{
char_u *arg = eap->arg;
char_u *expr = NULL;
@ -717,13 +720,14 @@ ex_let_const(exarg_T *eap, int redefine)
char_u *argend;
int first = TRUE;
int concat;
int has_assign;
int flags = eap->cmdidx == CMD_const ? LET_IS_CONST : 0;
// detect Vim9 assignment without ":let" or ":const"
if (eap->arg == eap->cmd)
flags |= LET_NO_COMMAND;
if (redefine)
flags |= LET_REDEFINE;
if (discovery)
flags |= LET_DISCOVERY;
argend = skip_var_list(arg, TRUE, &var_count, &semicolon);
if (argend == NULL)
@ -734,8 +738,9 @@ ex_let_const(exarg_T *eap, int redefine)
concat = expr[0] == '.'
&& ((expr[1] == '=' && current_sctx.sc_version < 2)
|| (expr[1] == '.' && expr[2] == '='));
if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%", *expr) != NULL
&& expr[1] == '=') || concat))
has_assign = *expr == '=' || (vim_strchr((char_u *)"+-*/%", *expr) != NULL
&& expr[1] == '=');
if (!has_assign && !concat && !discovery)
{
// ":let" without "=": list variables
if (*arg == '[')
@ -779,32 +784,45 @@ ex_let_const(exarg_T *eap, int redefine)
}
else
{
op[0] = '=';
op[1] = NUL;
if (*expr != '=')
{
if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
{
op[0] = *expr; // +=, -=, *=, /=, %= or .=
if (expr[0] == '.' && expr[1] == '.') // ..=
++expr;
}
expr = skipwhite(expr + 2);
}
else
expr = skipwhite(expr + 1);
int eval_flags;
int save_called_emsg = called_emsg;
if (eap->skip)
++emsg_skip;
i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip);
rettv.v_type = VAR_UNKNOWN;
i = FAIL;
if (has_assign || concat)
{
op[0] = '=';
op[1] = NUL;
if (*expr != '=')
{
if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
{
op[0] = *expr; // +=, -=, *=, /=, %= or .=
if (expr[0] == '.' && expr[1] == '.') // ..=
++expr;
}
expr = skipwhite(expr + 2);
}
else
expr = skipwhite(expr + 1);
if (eap->skip)
++emsg_skip;
eval_flags = eap->skip ? 0 : EVAL_EVALUATE;
if (discovery)
eval_flags |= EVAL_CONSTANT;
i = eval0(expr, &rettv, &eap->nextcmd, eval_flags);
}
if (eap->skip)
{
if (i != FAIL)
clear_tv(&rettv);
--emsg_skip;
}
else if (i != FAIL)
else if (i != FAIL || (discovery && save_called_emsg == called_emsg))
{
// In Vim9 script discovery "let v: bool = Func()" fails but is
// still a valid declaration.
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
flags, op);
clear_tv(&rettv);
@ -1112,7 +1130,7 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first)
{
// handle d.key, l[idx], f(expr)
arg_subsc = arg;
if (handle_subscript(&arg, &tv, TRUE, TRUE,
if (handle_subscript(&arg, &tv, EVAL_EVALUATE, TRUE,
name, &name) == FAIL)
error = TRUE;
else
@ -1353,7 +1371,12 @@ ex_let_one(
lval_T lv;
p = get_lval(arg, tv, &lv, FALSE, FALSE, 0, FNE_CHECK_START);
if (p != NULL && lv.ll_name != NULL)
if ((flags & LET_DISCOVERY) && tv->v_type == VAR_UNKNOWN
&& lv.ll_type == NULL)
{
semsg(_("E1091: type missing for %s"), arg);
}
else if (p != NULL && lv.ll_name != NULL)
{
if (endchars != NULL && vim_strchr(endchars,
*skipwhite(lv.ll_name_end)) == NULL)
@ -2981,7 +3004,7 @@ set_var_const(
if (flags & LET_IS_CONST)
di->di_tv.v_lock |= VAR_LOCKED;
if (flags & LET_REDEFINE)
if (flags & LET_DISCOVERY)
di->di_flags |= DI_FLAGS_RELOAD;
}
@ -3288,7 +3311,8 @@ var_exists(char_u *var)
if (n)
{
// handle d.key, l[idx], f(expr)
n = (handle_subscript(&var, &tv, TRUE, FALSE, name, &name) == OK);
n = (handle_subscript(&var, &tv, EVAL_EVALUATE,
FALSE, name, &name) == OK);
if (n)
clear_tv(&tv);
}

View File

@ -879,7 +879,8 @@ ex_eval(exarg_T *eap)
{
typval_T tv;
if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK)
if (eval0(eap->arg, &tv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE)
== OK)
clear_tv(&tv);
}

View File

@ -1046,8 +1046,9 @@ f_join(typval_T *argvars, typval_T *rettv)
* Return OK or FAIL.
*/
int
get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error)
get_list_tv(char_u **arg, typval_T *rettv, int flags, int do_error)
{
int evaluate = flags & EVAL_EVALUATE;
list_T *l = NULL;
typval_T tv;
listitem_T *item;
@ -1062,7 +1063,7 @@ get_list_tv(char_u **arg, typval_T *rettv, int evaluate, int do_error)
*arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL)
{
if (eval1(arg, &tv, evaluate) == FAIL) // recursive!
if (eval1(arg, &tv, flags) == FAIL) // recursive!
goto failret;
if (evaluate)
{

View File

@ -1054,7 +1054,7 @@ def Test_vim9script_forward_func()
def FuncTwo(): string
return 'two'
enddef
let g:res_FuncOne = execute('disass FuncOne')
let g:res_FuncOne: string = execute('disass FuncOne')
END
writefile(lines, 'Xdisassemble')
source Xdisassemble

View File

@ -494,7 +494,7 @@ let s:export_script_lines =<< trim END
def Concat(arg: string): string
return name .. arg
enddef
let g:result = Concat('bie')
let g:result: string = Concat('bie')
let g:localname = name
export const CONST = 1234
@ -1633,7 +1633,7 @@ def Test_vim9_comment_not_compiled()
CheckScriptFailure([
'vim9script',
'let g:var = 123',
'unlet g:var# comment',
'unlet g:var# comment1',
], 'E108:')
CheckScriptFailure([
@ -1643,7 +1643,7 @@ def Test_vim9_comment_not_compiled()
CheckScriptSuccess([
'vim9script',
'if 1 # comment',
'if 1 # comment2',
' echo "yes"',
'elseif 2 #comment',
' echo "no"',
@ -1652,14 +1652,14 @@ def Test_vim9_comment_not_compiled()
CheckScriptFailure([
'vim9script',
'if 1# comment',
'if 1# comment3',
' echo "yes"',
'endif',
], 'E15:')
CheckScriptFailure([
'vim9script',
'if 0 # comment',
'if 0 # comment4',
' echo "yes"',
'elseif 2#comment',
' echo "no"',
@ -1668,23 +1668,18 @@ def Test_vim9_comment_not_compiled()
CheckScriptSuccess([
'vim9script',
'let # comment',
'let v = 1 # comment5',
])
CheckScriptFailure([
'vim9script',
'let# comment',
], 'E121:')
CheckScriptSuccess([
'vim9script',
'let v:version # comment',
])
'let v = 1# comment6',
], 'E15:')
CheckScriptFailure([
'vim9script',
'let v:version# comment',
], 'E121:')
'let v:version',
], 'E1091:')
CheckScriptSuccess([
'vim9script',
@ -1722,6 +1717,41 @@ def Test_finish()
delete('Xfinished')
enddef
def Test_let_func_call()
let lines =<< trim END
vim9script
func GetValue()
if exists('g:count')
let g:count += 1
else
let g:count = 1
endif
return 'this'
endfunc
let val: string = GetValue()
END
writefile(lines, 'Xfinished')
source Xfinished
assert_equal(1, g:count)
unlet g:count
delete('Xfinished')
enddef
def Test_let_missing_type()
let lines =<< trim END
vim9script
func GetValue()
return 'this'
endfunc
let val = GetValue()
END
writefile(lines, 'Xfinished')
assert_fails('source Xfinished', 'E1091:')
delete('Xfinished')
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new

View File

@ -239,7 +239,7 @@ get_function_args(
whitep = p;
p = skipwhite(p);
expr = p;
if (eval1(&p, &rettv, FALSE) != FAIL)
if (eval1(&p, &rettv, 0) != FAIL)
{
if (ga_grow(default_args, 1) == FAIL)
goto err_ret;
@ -572,7 +572,8 @@ get_func_tv(
argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL)
break;
if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL)
if (eval1(&argp, &argvars[argcount],
funcexe->evaluate ? EVAL_EVALUATE : 0) == FAIL)
{
ret = FAIL;
break;
@ -1223,7 +1224,7 @@ call_user_func(
default_expr = ((char_u **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
if (eval1(&default_expr, &def_rettv, TRUE) == FAIL)
if (eval1(&default_expr, &def_rettv, EVAL_EVALUATE) == FAIL)
{
default_arg_err = 1;
break;
@ -1368,7 +1369,7 @@ call_user_func(
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
++ex_nesting_level;
(void)eval1(&p, rettv, TRUE);
(void)eval1(&p, rettv, EVAL_EVALUATE);
--ex_nesting_level;
}
else
@ -3623,7 +3624,8 @@ ex_return(exarg_T *eap)
eap->nextcmd = NULL;
if ((*arg != NUL && *arg != '|' && *arg != '\n')
&& eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL)
&& eval0(arg, &rettv, &eap->nextcmd, eap->skip ? 0 : EVAL_EVALUATE)
!= FAIL)
{
if (!eap->skip)
returning = do_return(eap, FALSE, TRUE, &rettv);
@ -3680,7 +3682,7 @@ ex_call(exarg_T *eap)
// instead to skip to any following command, e.g. for:
// :if 0 | call dict.foo().bar() | endif
++emsg_skip;
if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL)
if (eval0(eap->arg, &rettv, &eap->nextcmd, 0) != FAIL)
clear_tv(&rettv);
--emsg_skip;
return;
@ -3768,8 +3770,8 @@ ex_call(exarg_T *eap)
dbg_check_breakpoint(eap);
// Handle a function returning a Funcref, Dictionary or List.
if (handle_subscript(&arg, &rettv, !eap->skip, TRUE,
name, &name) == FAIL)
if (handle_subscript(&arg, &rettv, eap->skip ? 0 : EVAL_EVALUATE,
TRUE, name, &name) == FAIL)
{
failed = TRUE;
break;

View File

@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
753,
/**/
752,
/**/

View File

@ -2133,7 +2133,7 @@ typedef enum {
// Flags for assignment functions.
#define LET_IS_CONST 1 // ":const"
#define LET_NO_COMMAND 2 // "var = expr" without ":let" or ":const"
#define LET_REDEFINE 4 // variable can be redefined later
#define LET_DISCOVERY 4 // discovery phase: variable can be redefined later
#include "ex_cmds.h" // Ex command defines
#include "spell.h" // spell checking stuff
@ -2662,4 +2662,8 @@ long elapsed(DWORD start_tick);
#define REPTERM_SPECIAL 4
#define REPTERM_NO_SIMPLIFY 8
// Flags for expression evaluation.
#define EVAL_EVALUATE 1 // when missing don't actually evaluate
#define EVAL_CONSTANT 2 // when not a constant return FAIL
#endif // VIM__H

View File

@ -2869,14 +2869,14 @@ to_name_const_end(char_u *arg)
{
// Can be "[1, 2, 3]->Func()".
if (get_list_tv(&p, &rettv, FALSE, FALSE) == FAIL)
if (get_list_tv(&p, &rettv, 0, FALSE) == FAIL)
p = arg;
}
else if (p == arg && *arg == '#' && arg[1] == '{')
{
// Can be "#{a: 1}->Func()".
++p;
if (eval_dict(&p, &rettv, FALSE, TRUE) == FAIL)
if (eval_dict(&p, &rettv, 0, TRUE) == FAIL)
p = arg;
}
else if (p == arg && *arg == '{')
@ -2886,7 +2886,7 @@ to_name_const_end(char_u *arg)
// Can be "{x -> ret}()".
// Can be "{'a': 1}->Func()".
if (ret == NOTDONE)
ret = eval_dict(&p, &rettv, FALSE, FALSE);
ret = eval_dict(&p, &rettv, 0, FALSE);
if (ret != OK)
p = arg;
}