]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.2381: Vim9: divide by zero does not abort expression execution v8.2.2381
authorBram Moolenaar <Bram@vim.org>
Thu, 21 Jan 2021 11:34:14 +0000 (12:34 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 21 Jan 2021 11:34:14 +0000 (12:34 +0100)
Problem:    Vim9: divide by zero does not abort expression execution.
Solution:   Use a "failed" flag. (issue #7704)

src/eval.c
src/evalvars.c
src/proto/eval.pro
src/testdir/test_vim9_assign.vim
src/testdir/vim9.vim
src/version.c
src/vim9compile.c

index 40c6feb70645fc7eaa8bbfdea9c81992cd16cee3..3e316bdb1061a5d568b3a38759be54b6d5e853fd 100644 (file)
@@ -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;
index 200fb8f12c1709ef9c32dad6891f2e71912c77e7..179953178695494b8ca6bb3d3c38043bd4570dfd 100644 (file)
@@ -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
index fbae530306908b9e216f76eba465c22b5677cca2..07b11b25a5478ce003bac64ab6e84b006415b113 100644 (file)
@@ -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);
index 20bdb91380e55be594601c9db0c5ad3836ce5541..992eeaa655c9c83b5923f23ebc1d2b7dc3b998f2 100644 (file)
@@ -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
index bb522df151a326c2e32d33bc5040a698fd6980d7..cc0510fe91112136cbf68d5613683932ee60ecc8 100644 (file)
@@ -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
index 0e4a97b95608ab2b0a90cc0a9e331088841d3651..c220a409ec7b6ac4b296019f91feaaa14a4e9aa1 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2381,
 /**/
     2380,
 /**/
index 36fd2534c3677367c474d15dee6276ccdf9cc99a..e972033f0bad5bb379ceac8a06ac6710a539445f 100644 (file)
@@ -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;
        }