]> granicus.if.org Git - vim/commitdiff
patch 8.2.3137: Vim9: no error when a line only has a variable name v8.2.3137
authorBram Moolenaar <Bram@vim.org>
Sat, 10 Jul 2021 17:42:03 +0000 (19:42 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 10 Jul 2021 17:42:03 +0000 (19:42 +0200)
Problem:    Vim9: no error when a line only has a variable name.
Solution:   Give an error when an expression is evaluated without an effect.
            (closes #8538)

src/errors.h
src/ex_eval.c
src/testdir/test_vim9_expr.vim
src/testdir/test_vim9_func.vim
src/testdir/test_vim9_script.vim
src/version.c
src/vim9compile.c

index 14c5b8def86800e74150ce85161ea4efe69a7dcc..d4f3179abe0c736b1e9cb385887bb43905fd9482 100644 (file)
@@ -494,3 +494,5 @@ EXTERN char e_no_white_space_allowed_between_option_and[]
        INIT(= N_("E1205: No white space allowed between option and"));
 EXTERN char e_dict_required_for_argument_nr[]
        INIT(= N_("E1206: Dictionary required for argument %d"));
+EXTERN char e_expression_without_effect_str[]
+       INIT(= N_("E1207: Expression without an effect: %s"));
index b504763dfccfe533bf821401e25509945788080e..08ba858876e86969625f022f36414f3caecb97d7 100644 (file)
@@ -208,7 +208,7 @@ cause_errthrow(
      * not skipped.  Errors in those commands may affect what of the subsequent
      * commands are regarded part of catch and finally clauses.  Catching the
      * exception would then cause execution of commands not intended by the
-     * user, who wouldn't even get aware of the problem.  Therefor, discard the
+     * user, who wouldn't even get aware of the problem.  Therefore, discard the
      * exception currently being thrown to prevent it from being caught.  Just
      * execute finally clauses and terminate.
      */
@@ -896,11 +896,28 @@ ex_eval(exarg_T *eap)
 {
     typval_T   tv;
     evalarg_T  evalarg;
+    int                name_only = FALSE;
+    char_u     *p;
+    long       lnum = SOURCING_LNUM;
+
+    if (in_vim9script())
+    {
+       char_u  *alias;
+
+       p = eap->arg;
+       get_name_len(&p, &alias, FALSE, FALSE);
+       name_only = ends_excmd2(eap->arg, skipwhite(p));
+       vim_free(alias);
+    }
 
     fill_evalarg_from_eap(&evalarg, eap, eap->skip);
 
     if (eval0(eap->arg, &tv, eap, &evalarg) == OK)
+    {
        clear_tv(&tv);
+       if (in_vim9script() && name_only && lnum == SOURCING_LNUM)
+           semsg(_(e_expression_without_effect_str), eap->arg);
+    }
 
     clear_evalarg(&evalarg, eap);
 }
@@ -1287,7 +1304,7 @@ ex_continue(exarg_T *eap)
     {
        // Try to find the matching ":while".  This might stop at a try
        // conditional not in its finally clause (which is then to be executed
-       // next).  Therefor, inactivate all conditionals except the ":while"
+       // next).  Therefore, inactivate all conditionals except the ":while"
        // itself (if reached).
        idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE);
        if (idx >= 0 && (cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR)))
index 1e99e9568d48cb949cd456c6e28b0aaae5872606..79a4ea36e54dcd54ea552bdac7b5fe8b0a924b67 100644 (file)
@@ -647,7 +647,7 @@ def Test_expr4_equal()
   CheckDefFailure(["var x = 'a' == "], 'E1097:', 3)
   CheckScriptFailure(['vim9script', "var x = 'a' == "], 'E15:', 2)
 
-  CheckDefExecAndScriptFailure2(['var items: any', 'eval 1', 'eval 2', 'if items == []', 'endif'], 'E691:', 'E1072:', 4)
+  CheckDefExecAndScriptFailure2(['var items: any', 'eval 1 + 1', 'eval 2 + 2', 'if items == []', 'endif'], 'E691:', 'E1072:', 4)
 
   CheckDefExecAndScriptFailure(['var x: any = "a"', 'echo x == true'], 'E1072: Cannot compare string with bool', 2)
   CheckDefExecAndScriptFailure(["var x: any = true", 'echo x == ""'], 'E1072: Cannot compare bool with string', 2)
index 89cdee7437be3aa7bcdf01233bee086e2244ea08..08e68d147c0a373b21be355d1f7f275ffce50c5f 100644 (file)
@@ -2538,7 +2538,7 @@ def Test_restore_modifiers()
       set eventignore=
       autocmd QuickFixCmdPost * copen
       def AutocmdsDisabled()
-        eval 0
+        eval 1 + 2
       enddef
       func Func()
         noautocmd call s:AutocmdsDisabled()
@@ -2551,8 +2551,8 @@ def Test_restore_modifiers()
 enddef
 
 def StackTop()
-  eval 1
-  eval 2
+  eval 1 + 2
+  eval 2 + 3
   # call not on fourth line
   StackBot()
 enddef
index 66b0c740fedb767728e1d80d21072f7733d73912..b21b63cbe0e7fbfedbc186da83ae0d099c795e7a 100644 (file)
@@ -691,7 +691,7 @@ enddef
 def Test_cnext_works_in_catch()
   var lines =<< trim END
       vim9script
-      au BufEnter * eval 0
+      au BufEnter * eval 1 + 2
       writefile(['text'], 'Xfile1')
       writefile(['text'], 'Xfile2')
       var items = [
@@ -1754,6 +1754,21 @@ def Test_script_var_shadows_function()
   CheckScriptFailure(lines, 'E1041:', 5)
 enddef
 
+def Test_script_var_shadows_command()
+  var lines =<< trim END
+      var undo = 1
+      undo = 2
+      assert_equal(2, undo)
+  END
+  CheckDefAndScriptSuccess(lines)
+
+  lines =<< trim END
+      var undo = 1
+      undo
+  END
+  CheckDefAndScriptFailure(lines, 'E1207:', 2)
+enddef
+
 def s:RetSome(): string
   return 'some'
 enddef
@@ -2270,7 +2285,7 @@ def Test_if_const_expr()
   assert_equal(false, res)
 
   # with constant "false" expression may be invalid so long as the syntax is OK
-  if false | eval 0 | endif
+  if false | eval 1 + 2 | endif
   if false | eval burp + 234 | endif
   if false | echo burp 234 'asd' | endif
   if false
index bcbff0a9cb437b0cd8cc8cef42306789b06ef146..762f3f809a6ca76b056d4e447f364fc662ccd87c 100644 (file)
@@ -755,6 +755,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3137,
 /**/
     3136,
 /**/
index c1fc31ed23af5fac5733087652972c8615b79775..c27a1cc34afb58f9e309cf8a02a1401cb05a99f6 100644 (file)
@@ -8563,6 +8563,37 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
     return p;
 }
 
+    static char_u *
+compile_eval(char_u *arg, cctx_T *cctx)
+{
+    char_u     *p = arg;
+    int                name_only;
+    char_u     *alias;
+    long       lnum = SOURCING_LNUM;
+
+    // find_ex_command() will consider a variable name an expression, assuming
+    // that something follows on the next line.  Check that something actually
+    // follows, otherwise it's probably a misplaced command.
+    get_name_len(&p, &alias, FALSE, FALSE);
+    name_only = ends_excmd2(arg, skipwhite(p));
+    vim_free(alias);
+
+    p = arg;
+    if (compile_expr0(&p, cctx) == FAIL)
+       return NULL;
+
+    if (name_only && lnum == SOURCING_LNUM)
+    {
+       semsg(_(e_expression_without_effect_str), arg);
+       return NULL;
+    }
+
+    // drop the result
+    generate_instr_drop(cctx, ISN_DROP, 1);
+
+    return skipwhite(p);
+}
+
 /*
  * compile "echo expr"
  * compile "echomsg expr"
@@ -9630,13 +9661,7 @@ compile_def_function(
                    break;
 
            case CMD_eval:
-                   if (compile_expr0(&p, &cctx) == FAIL)
-                       goto erret;
-
-                   // drop the result
-                   generate_instr_drop(&cctx, ISN_DROP, 1);
-
-                   line = skipwhite(p);
+                   line = compile_eval(p, &cctx);
                    break;
 
            case CMD_echo: