mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.2311: Vim9: cannot assign to variable that shadows command modifier
Problem: Vim9: cannot assign to a variable that shadows a command modifier. Solution: Check for assignment after possible command modifier. (closes #7632)
This commit is contained in:
@@ -2738,6 +2738,25 @@ parse_command_modifiers(
|
|||||||
}
|
}
|
||||||
|
|
||||||
p = skip_range(eap->cmd, TRUE, NULL);
|
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)
|
switch (*p)
|
||||||
{
|
{
|
||||||
// When adding an entry, also modify cmd_exists().
|
// When adding an entry, also modify cmd_exists().
|
||||||
|
@@ -1464,5 +1464,26 @@ def Test_unlet()
|
|||||||
assert_equal('', $ENVVAR)
|
assert_equal('', $ENVVAR)
|
||||||
enddef
|
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
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2311,
|
||||||
/**/
|
/**/
|
||||||
2310,
|
2310,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -6216,6 +6216,77 @@ theend:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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".
|
* Check if "name" can be "unlet".
|
||||||
*/
|
*/
|
||||||
@@ -7838,68 +7909,14 @@ compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx)
|
|||||||
|
|
||||||
if (!starts_with_colon)
|
if (!starts_with_colon)
|
||||||
{
|
{
|
||||||
char_u *pskip;
|
int assign;
|
||||||
|
|
||||||
// Assuming the command starts with a variable or function name,
|
// Check for assignment after command modifiers.
|
||||||
// find what follows.
|
assign = may_compile_assignment(&ea, &line, &cctx);
|
||||||
// Skip over "var.member", "var[idx]" and the like.
|
if (assign == OK)
|
||||||
// Also "&opt = val", "$ENV = val" and "@r = val".
|
goto nextline;
|
||||||
pskip = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@')
|
if (assign == FAIL)
|
||||||
? ea.cmd + 1 : ea.cmd;
|
goto erret;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user