summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-01-21 12:34:14 +0100
committerBram Moolenaar <Bram@vim.org>2021-01-21 12:34:14 +0100
commitc5f59fab23820454f060562927ddc1397f9d479a (patch)
treeb2f9c295fd9d127e5211ad608966554cf92d7ae6
parenta0f7f73ebb3ab032af08699c24c465403512b7d8 (diff)
patch 8.2.2381: Vim9: divide by zero does not abort expression executionv8.2.2381
Problem: Vim9: divide by zero does not abort expression execution. Solution: Use a "failed" flag. (issue #7704)
-rw-r--r--src/eval.c31
-rw-r--r--src/evalvars.c6
-rw-r--r--src/proto/eval.pro4
-rw-r--r--src/testdir/test_vim9_assign.vim24
-rw-r--r--src/testdir/vim9.vim13
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c13
7 files changed, 77 insertions, 16 deletions
diff --git a/src/eval.c b/src/eval.c
index 40c6feb706..3e316bdb10 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -57,16 +57,21 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *
/*
* Return "n1" divided by "n2", taking care of dividing by zero.
+ * If "failed" is not NULL set it to TRUE when dividing by zero fails.
*/
varnumber_T
-num_divide(varnumber_T n1, varnumber_T n2)
+num_divide(varnumber_T n1, varnumber_T n2, int *failed)
{
varnumber_T result;
if (n2 == 0)
{
if (in_vim9script())
+ {
emsg(_(e_divide_by_zero));
+ if (failed != NULL)
+ *failed = TRUE;
+ }
if (n1 == 0)
result = VARNUM_MIN; // similar to NaN
else if (n1 < 0)
@@ -82,12 +87,17 @@ num_divide(varnumber_T n1, varnumber_T n2)
/*
* Return "n1" modulus "n2", taking care of dividing by zero.
+ * If "failed" is not NULL set it to TRUE when dividing by zero fails.
*/
varnumber_T
-num_modulus(varnumber_T n1, varnumber_T n2)
+num_modulus(varnumber_T n1, varnumber_T n2, int *failed)
{
if (n2 == 0 && in_vim9script())
+ {
emsg(_(e_divide_by_zero));
+ if (failed != NULL)
+ *failed = TRUE;
+ }
return (n2 == 0) ? 0 : (n1 % n2);
}
@@ -1516,6 +1526,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
varnumber_T n;
char_u numbuf[NUMBUFLEN];
char_u *s;
+ int failed = FALSE;
// Can't do anything with a Funcref, Dict, v:true on the right.
if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
@@ -1599,8 +1610,10 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case '+': n += tv_get_number(tv2); break;
case '-': n -= tv_get_number(tv2); break;
case '*': n *= tv_get_number(tv2); break;
- case '/': n = num_divide(n, tv_get_number(tv2)); break;
- case '%': n = num_modulus(n, tv_get_number(tv2)); break;
+ case '/': n = num_divide(n, tv_get_number(tv2),
+ &failed); break;
+ case '%': n = num_modulus(n, tv_get_number(tv2),
+ &failed); break;
}
clear_tv(tv1);
tv1->v_type = VAR_NUMBER;
@@ -1619,7 +1632,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
tv1->v_type = VAR_STRING;
tv1->vval.v_string = s;
}
- return OK;
+ return failed ? FAIL : OK;
case VAR_FLOAT:
#ifdef FEAT_FLOAT
@@ -3196,12 +3209,16 @@ eval6(
else
#endif
{
+ int failed = FALSE;
+
if (op == '*')
n1 = n1 * n2;
else if (op == '/')
- n1 = num_divide(n1, n2);
+ n1 = num_divide(n1, n2, &failed);
else
- n1 = num_modulus(n1, n2);
+ n1 = num_modulus(n1, n2, &failed);
+ if (failed)
+ return FAIL;
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = n1;
diff --git a/src/evalvars.c b/src/evalvars.c
index 200fb8f12c..1799531786 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -1410,8 +1410,10 @@ ex_let_one(
case '+': n = numval + n; break;
case '-': n = numval - n; break;
case '*': n = numval * n; break;
- case '/': n = (long)num_divide(numval, n); break;
- case '%': n = (long)num_modulus(numval, n); break;
+ case '/': n = (long)num_divide(numval, n,
+ &failed); break;
+ case '%': n = (long)num_modulus(numval, n,
+ &failed); break;
}
}
else if (opt_type == gov_string
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index fbae530306..07b11b25a5 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -1,6 +1,6 @@
/* eval.c */
-varnumber_T num_divide(varnumber_T n1, varnumber_T n2);
-varnumber_T num_modulus(varnumber_T n1, varnumber_T n2);
+varnumber_T num_divide(varnumber_T n1, varnumber_T n2, int *failed);
+varnumber_T num_modulus(varnumber_T n1, varnumber_T n2, int *failed);
void eval_init(void);
void eval_clear(void);
void fill_evalarg_from_eap(evalarg_T *evalarg, exarg_T *eap, int skip);
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 20bdb91380..992eeaa655 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -1489,6 +1489,30 @@ def Test_unlet()
assert_equal('', $ENVVAR)
enddef
+def Test_expr_error_no_assign()
+ var lines =<< trim END
+ vim9script
+ var x = invalid
+ echo x
+ END
+ CheckScriptFailureList(lines, ['E121:', 'E121:'])
+
+ lines =<< trim END
+ vim9script
+ var x = 1 / 0
+ echo x
+ END
+ CheckScriptFailureList(lines, ['E1154:', 'E121:'])
+
+ lines =<< trim END
+ vim9script
+ var x = 1 % 0
+ echo x
+ END
+ CheckScriptFailureList(lines, ['E1154:', 'E121:'])
+enddef
+
+
def Test_assign_command_modifier()
var lines =<< trim END
var verbose = 0
diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim
index bb522df151..cc0510fe91 100644
--- a/src/testdir/vim9.vim
+++ b/src/testdir/vim9.vim
@@ -69,6 +69,19 @@ def CheckScriptFailure(lines: list<string>, error: string, lnum = -3)
endtry
enddef
+def CheckScriptFailureList(lines: list<string>, errors: list<string>, lnum = -3)
+ var cwd = getcwd()
+ var fname = 'XScriptFailure' .. s:sequence
+ s:sequence += 1
+ writefile(lines, fname)
+ try
+ assert_fails('so ' .. fname, errors, lines, lnum)
+ finally
+ chdir(cwd)
+ delete(fname)
+ endtry
+enddef
+
def CheckScriptSuccess(lines: list<string>)
var cwd = getcwd()
var fname = 'XScriptSuccess' .. s:sequence
diff --git a/src/version.c b/src/version.c
index 0e4a97b956..c220a409ec 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2381,
+/**/
2380,
/**/
2379,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 36fd2534c3..e972033f0b 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -4291,9 +4291,10 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
&& ppconst->pp_tv[ppconst_used].v_type == VAR_NUMBER
&& ppconst->pp_tv[ppconst_used + 1].v_type == VAR_NUMBER)
{
- typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
- typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
- varnumber_T res = 0;
+ typval_T *tv1 = &ppconst->pp_tv[ppconst_used];
+ typval_T *tv2 = &ppconst->pp_tv[ppconst_used + 1];
+ varnumber_T res = 0;
+ int failed = FALSE;
// both are numbers: compute the result
switch (*op)
@@ -4301,12 +4302,14 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
case '*': res = tv1->vval.v_number * tv2->vval.v_number;
break;
case '/': res = num_divide(tv1->vval.v_number,
- tv2->vval.v_number);
+ tv2->vval.v_number, &failed);
break;
case '%': res = num_modulus(tv1->vval.v_number,
- tv2->vval.v_number);
+ tv2->vval.v_number, &failed);
break;
}
+ if (failed)
+ return FAIL;
tv1->vval.v_number = res;
--ppconst->pp_used;
}