]> granicus.if.org Git - vim/commitdiff
patch 8.2.1313: Vim9 script: cannot assign to environment variable v8.2.1313
authorBram Moolenaar <Bram@vim.org>
Tue, 28 Jul 2020 20:38:37 +0000 (22:38 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 28 Jul 2020 20:38:37 +0000 (22:38 +0200)
Problem:    Vim9 script: cannot assign to environment variable.
Solution:   Recognize environment variable assignment. (closes #6548)
            Also options and registers.

src/ex_docmd.c
src/testdir/test_vim9_script.vim
src/version.c

index 6a2e91c9fea7caf00ecc3048a63276ece44bab6f..536850edc9f65d544f8b20b0c064720f99dd5904 100644 (file)
@@ -1710,7 +1710,7 @@ do_one_cmd(
     char_u     *cmd;
     int                starts_with_colon = FALSE;
 #ifdef FEAT_EVAL
-    int                starts_with_quote;
+    int                may_have_range;
     int                vim9script = in_vim9script();
 #endif
 
@@ -1773,8 +1773,9 @@ do_one_cmd(
  */
     cmd = ea.cmd;
 #ifdef FEAT_EVAL
-    starts_with_quote = vim9script && !starts_with_colon && *ea.cmd == '\'';
-    if (!starts_with_quote)
+    // In Vim9 script a colon is required before the range.
+    may_have_range = !vim9script || starts_with_colon;
+    if (may_have_range)
 #endif
        ea.cmd = skip_range(ea.cmd, NULL);
     if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
@@ -1783,7 +1784,10 @@ do_one_cmd(
 #ifdef FEAT_EVAL
     if (vim9script && !starts_with_colon)
     {
-       if (ea.cmd > cmd)
+       if (ea.cmd == cmd + 1 && *cmd == '$')
+           // should be "$VAR = val"
+           --ea.cmd;
+       else if (ea.cmd > cmd)
        {
            emsg(_(e_colon_required));
            goto doend;
@@ -1876,7 +1880,7 @@ do_one_cmd(
 
     ea.cmd = cmd;
 #ifdef FEAT_EVAL
-    if (!starts_with_quote)
+    if (may_have_range)
 #endif
        if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
            goto doend;
@@ -3267,80 +3271,90 @@ find_ex_command(
      * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
      */
     p = eap->cmd;
-    if (lookup != NULL && (vim_strchr((char_u *)"{('[", *p) != NULL
-              || ((p = to_name_const_end(eap->cmd)) > eap->cmd
-                  && *p != NUL)))
+    if (lookup != NULL)
     {
-       int oplen;
-       int heredoc;
+       // Skip over first char for "&opt = val", "$ENV = val" and "@r = val".
+       char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$'
+                               || *eap->cmd == '@') ? eap->cmd + 1 : eap->cmd;
 
-       if (
-           // "(..." is an expression.
-           // "funcname(" is always a function call.
-           *p == '('
-               || (p == eap->cmd
-                   ? (
-                       // "{..." is an dict expression.
-                       *eap->cmd == '{'
-                       // "'string'->func()" is an expression.
-                    || *eap->cmd == '\''
-                       // "g:varname" is an expression.
-                    || eap->cmd[1] == ':'
-                       )
-                   : (
-                       // "varname[]" is an expression.
-                       *p == '['
-                       // "varname->func()" is an expression.
-                    || (*p == '-' && p[1] == '>')
-                       // "varname.expr" is an expression.
-                    || (*p == '.' && ASCII_ISALPHA(p[1]))
-                    )))
-       {
-           eap->cmdidx = CMD_eval;
-           return eap->cmd;
-       }
-
-       // "[...]->Method()" is a list expression, but "[a, b] = Func()" is
-       // an assignment.
-       // If there is no line break inside the "[...]" then "p" is advanced to
-       // after the "]" by to_name_const_end(): check if a "=" follows.
-       // If "[...]" has a line break "p" still points at the "[" and it can't
-       // be an assignment.
-       if (*eap->cmd == '[')
-       {
-           p = to_name_const_end(eap->cmd);
-           if (p == eap->cmd || *skipwhite(p) != '=')
+       if (vim_strchr((char_u *)"{('[", *p) != NULL
+              || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
+       {
+           int oplen;
+           int heredoc;
+
+           if (
+               // "(..." is an expression.
+               // "funcname(" is always a function call.
+               *p == '('
+                   || (p == eap->cmd
+                       ? (
+                           // "{..." is an dict expression.
+                           *eap->cmd == '{'
+                           // "'string'->func()" is an expression.
+                        || *eap->cmd == '\''
+                           // "g:varname" is an expression.
+                        || eap->cmd[1] == ':'
+                           )
+                       : (
+                           // "varname[]" is an expression.
+                           *p == '['
+                           // "varname->func()" is an expression.
+                        || (*p == '-' && p[1] == '>')
+                           // "varname.expr" is an expression.
+                        || (*p == '.' && ASCII_ISALPHA(p[1]))
+                        )))
            {
                eap->cmdidx = CMD_eval;
                return eap->cmd;
            }
-           if (p > eap->cmd && *skipwhite(p) == '=')
+
+           // "[...]->Method()" is a list expression, but "[a, b] = Func()" is
+           // an assignment.
+           // If there is no line break inside the "[...]" then "p" is
+           // advanced to after the "]" by to_name_const_end(): check if a "="
+           // follows.
+           // If "[...]" has a line break "p" still points at the "[" and it
+           // can't be an assignment.
+           if (*eap->cmd == '[')
            {
-               eap->cmdidx = CMD_let;
-               return eap->cmd;
+               p = to_name_const_end(eap->cmd);
+               if (p == eap->cmd || *skipwhite(p) != '=')
+               {
+                   eap->cmdidx = CMD_eval;
+                   return eap->cmd;
+               }
+               if (p > eap->cmd && *skipwhite(p) == '=')
+               {
+                   eap->cmdidx = CMD_let;
+                   return eap->cmd;
+               }
            }
-       }
 
-       // Recognize an assignment if we recognize the variable name:
-       // "g:var = expr"
-       // "var = expr"  where "var" is a local var name.
-       oplen = assignment_len(skipwhite(p), &heredoc);
-       if (oplen > 0)
-       {
-           if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
-                   || lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
+           // Recognize an assignment if we recognize the variable name:
+           // "g:var = expr"
+           // "var = expr"  where "var" is a local var name.
+           oplen = assignment_len(skipwhite(p), &heredoc);
+           if (oplen > 0)
            {
-               eap->cmdidx = CMD_let;
-               return eap->cmd;
+               if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
+                       || *eap->cmd == '&'
+                       || *eap->cmd == '$'
+                       || *eap->cmd == '@'
+                       || lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
+               {
+                   eap->cmdidx = CMD_let;
+                   return eap->cmd;
+               }
            }
-       }
 
-       // Recognize using a type for a w:, b:, t: or g: variable:
-       // "w:varname: number = 123".
-       if (eap->cmd[1] == ':' && *p == ':')
-       {
-           eap->cmdidx = CMD_eval;
-           return eap->cmd;
+           // Recognize using a type for a w:, b:, t: or g: variable:
+           // "w:varname: number = 123".
+           if (eap->cmd[1] == ':' && *p == ':')
+           {
+               eap->cmdidx = CMD_eval;
+               return eap->cmd;
+           }
        }
     }
 #endif
index 379245f397f229e3fa77c34d69b7cdf7094ffa85..427e91238e1bcf8c3a46d956fd6031de3358568b 100644 (file)
@@ -61,6 +61,14 @@ def Test_assignment()
   assert_equal('foobar', $ENVVAR)
   $ENVVAR = ''
 
+  let lines =<< trim END
+    vim9script
+    $ENVVAR = 'barfoo'
+    assert_equal('barfoo', $ENVVAR)
+    $ENVVAR = ''
+  END
+  call CheckScriptSuccess(lines)
+
   s:appendToMe ..= 'yyy'
   assert_equal('xxxyyy', s:appendToMe)
   s:addToMe += 222
@@ -80,6 +88,15 @@ def Test_assignment()
   set ts=10
   &ts %= 4
   assert_equal(2, &ts)
+
+  lines =<< trim END
+    vim9script
+    &ts = 6
+    &ts += 3
+    assert_equal(9, &ts)
+  END
+  call CheckScriptSuccess(lines)
+
   call CheckDefFailure(['&notex += 3'], 'E113:')
   call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
   call CheckDefFailure(['&ts = [7]'], 'E1013:')
@@ -106,6 +123,14 @@ def Test_assignment()
   call CheckDefFailure(['@a += "more"'], 'E1013:')
   call CheckDefFailure(['@a += 123'], 'E1013:')
 
+  lines =<< trim END
+    vim9script
+    @c = 'areg'
+    @c ..= 'add'
+    assert_equal('aregadd', @c)
+  END
+  call CheckScriptSuccess(lines)
+
   v:errmsg = 'none'
   v:errmsg ..= 'again'
   assert_equal('noneagain', v:errmsg)
index 26bd7ac6518a8b3500686ed96790be5ebd8d8cf6..fce921888c6095983302410ccde475026e58b932 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1313,
 /**/
     1312,
 /**/