summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-01-07 22:03:02 +0100
committerBram Moolenaar <Bram@vim.org>2021-01-07 22:03:02 +0100
commit17126b13969c3b91516a8e9ff80fb6a1f6924d40 (patch)
treefd8b5ca5599d92409fc98d778b7bf32d1bee1eed
parent43b69b39acb85a2aab2310cba5a2dbac338a4eb9 (diff)
downloadvim-git-17126b13969c3b91516a8e9ff80fb6a1f6924d40.tar.gz
patch 8.2.2311: Vim9: cannot assign to variable that shadows command modifierv8.2.2311
Problem: Vim9: cannot assign to a variable that shadows a command modifier. Solution: Check for assignment after possible command modifier. (closes #7632)
-rw-r--r--src/ex_docmd.c19
-rw-r--r--src/testdir/test_vim9_assign.vim21
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c139
4 files changed, 120 insertions, 61 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 5a3a37abc..9659c20b1 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -2738,6 +2738,25 @@ parse_command_modifiers(
}
p = skip_range(eap->cmd, TRUE, NULL);
+
+ // In Vim9 script a variable can shadow a command modifier:
+ // verbose = 123
+ // verbose += 123
+ // silent! verbose = func()
+ // verbose.member = 2
+ // verbose[expr] = 2
+ if (in_vim9script())
+ {
+ char_u *s;
+
+ for (s = p; ASCII_ISALPHA(*s); ++s)
+ ;
+ s = skipwhite(s);
+ if (vim_strchr((char_u *)".[=", *s) != NULL
+ || (*s != NUL && s[1] == '='))
+ break;
+ }
+
switch (*p)
{
// When adding an entry, also modify cmd_exists().
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 2d137a328..55b25ea17 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -1464,5 +1464,26 @@ def Test_unlet()
assert_equal('', $ENVVAR)
enddef
+def Test_assign_command_modifier()
+ var lines =<< trim END
+ var verbose = 0
+ verbose = 1
+ assert_equal(1, verbose)
+ silent verbose = 2
+ assert_equal(2, verbose)
+ silent verbose += 2
+ assert_equal(4, verbose)
+ silent verbose -= 1
+ assert_equal(3, verbose)
+
+ var topleft = {one: 1}
+ sandbox topleft.one = 3
+ assert_equal({one: 3}, topleft)
+ leftabove topleft[' '] = 4
+ assert_equal({one: 3, ' ': 4}, topleft)
+ END
+ CheckDefAndScriptSuccess(lines)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 0d9816bc9..c79b21970 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 */
/**/
+ 2311,
+/**/
2310,
/**/
2309,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index f66b27ff5..7497e9cc0 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6217,6 +6217,77 @@ theend:
}
/*
+ * Check for an assignment at "eap->cmd", compile it if found.
+ * Return NOTDONE if there is none, FAIL for failure, OK if done.
+ */
+ static int
+may_compile_assignment(exarg_T *eap, char_u **line, cctx_T *cctx)
+{
+ char_u *pskip;
+ char_u *p;
+
+ // Assuming the command starts with a variable or function name,
+ // find what follows.
+ // Skip over "var.member", "var[idx]" and the like.
+ // Also "&opt = val", "$ENV = val" and "@r = val".
+ pskip = (*eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@')
+ ? eap->cmd + 1 : eap->cmd;
+ p = to_name_end(pskip, TRUE);
+ if (p > eap->cmd && *p != NUL)
+ {
+ char_u *var_end;
+ int oplen;
+ int heredoc;
+
+ if (eap->cmd[0] == '@')
+ var_end = eap->cmd + 2;
+ else
+ var_end = find_name_end(pskip, NULL, NULL,
+ FNE_CHECK_START | FNE_INCL_BR);
+ oplen = assignment_len(skipwhite(var_end), &heredoc);
+ if (oplen > 0)
+ {
+ size_t len = p - eap->cmd;
+
+ // Recognize an assignment if we recognize the variable
+ // name:
+ // "g:var = expr"
+ // "local = expr" where "local" is a local var.
+ // "script = expr" where "script" is a script-local var.
+ // "import = expr" where "import" is an imported var
+ // "&opt = expr"
+ // "$ENV = expr"
+ // "@r = expr"
+ if (*eap->cmd == '&'
+ || *eap->cmd == '$'
+ || *eap->cmd == '@'
+ || ((len) > 2 && eap->cmd[1] == ':')
+ || lookup_local(eap->cmd, len, NULL, cctx) == OK
+ || arg_exists(eap->cmd, len, NULL, NULL, NULL, cctx) == OK
+ || script_var_exists(eap->cmd, len, FALSE, cctx) == OK
+ || find_imported(eap->cmd, len, cctx) != NULL)
+ {
+ *line = compile_assignment(eap->cmd, eap, CMD_SIZE, cctx);
+ if (*line == NULL || *line == eap->cmd)
+ return FAIL;
+ return OK;
+ }
+ }
+ }
+
+ if (*eap->cmd == '[')
+ {
+ // [var, var] = expr
+ *line = compile_assignment(eap->cmd, eap, CMD_SIZE, cctx);
+ if (*line == NULL)
+ return FAIL;
+ if (*line != eap->cmd)
+ return OK;
+ }
+ return NOTDONE;
+}
+
+/*
* Check if "name" can be "unlet".
*/
int
@@ -7838,68 +7909,14 @@ compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx)
if (!starts_with_colon)
{
- char_u *pskip;
+ int assign;
- // Assuming the command starts with a variable or function name,
- // find what follows.
- // Skip over "var.member", "var[idx]" and the like.
- // Also "&opt = val", "$ENV = val" and "@r = val".
- pskip = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
- ? ea.cmd + 1 : ea.cmd;
- p = to_name_end(pskip, TRUE);
- if (p > ea.cmd && *p != NUL)
- {
- char_u *var_end;
- int oplen;
- int heredoc;
-
- if (ea.cmd[0] == '@')
- var_end = ea.cmd + 2;
- else
- var_end = find_name_end(pskip, NULL, NULL,
- FNE_CHECK_START | FNE_INCL_BR);
- oplen = assignment_len(skipwhite(var_end), &heredoc);
- if (oplen > 0)
- {
- size_t len = p - ea.cmd;
-
- // Recognize an assignment if we recognize the variable
- // name:
- // "g:var = expr"
- // "local = expr" where "local" is a local var.
- // "script = expr" where "script" is a script-local var.
- // "import = expr" where "import" is an imported var
- // "&opt = expr"
- // "$ENV = expr"
- // "@r = expr"
- if (*ea.cmd == '&'
- || *ea.cmd == '$'
- || *ea.cmd == '@'
- || ((len) > 2 && ea.cmd[1] == ':')
- || lookup_local(ea.cmd, len, NULL, &cctx) == OK
- || arg_exists(ea.cmd, len, NULL, NULL,
- NULL, &cctx) == OK
- || script_var_exists(ea.cmd, len,
- FALSE, &cctx) == OK
- || find_imported(ea.cmd, len, &cctx) != NULL)
- {
- line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
- if (line == NULL || line == ea.cmd)
- goto erret;
- goto nextline;
- }
- }
- }
-
- if (*ea.cmd == '[')
- {
- // [var, var] = expr
- line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
- if (line == NULL)
- goto erret;
- if (line != ea.cmd)
- goto nextline;
- }
+ // Check for assignment after command modifiers.
+ assign = may_compile_assignment(&ea, &line, &cctx);
+ if (assign == OK)
+ goto nextline;
+ if (assign == FAIL)
+ goto erret;
}
/*