To: vim_dev@googlegroups.com Subject: Patch 8.2.2311 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2311 Problem: Vim9: cannot assign to a variable that shadows a command modifier. Solution: Check for assignment after possible command modifier. (closes #7632) Files: src/vim9compile.c, src/ex_docmd.c, src/testdir/test_vim9_assign.vim *** ../vim-8.2.2310/src/vim9compile.c 2021-01-07 19:23:04.966566919 +0100 --- src/vim9compile.c 2021-01-07 22:02:33.588205264 +0100 *************** *** 6217,6222 **** --- 6217,6293 ---- } /* + * 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,7905 **** if (!starts_with_colon) { ! char_u *pskip; ! // 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; ! } } /* --- 7909,7922 ---- if (!starts_with_colon) { ! int assign; ! // Check for assignment after command modifiers. ! assign = may_compile_assignment(&ea, &line, &cctx); ! if (assign == OK) ! goto nextline; ! if (assign == FAIL) ! goto erret; } /* *** ../vim-8.2.2310/src/ex_docmd.c 2021-01-04 16:15:55.066084896 +0100 --- src/ex_docmd.c 2021-01-07 21:56:15.173041913 +0100 *************** *** 2738,2743 **** --- 2738,2762 ---- } 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(). *** ../vim-8.2.2310/src/testdir/test_vim9_assign.vim 2021-01-07 19:23:04.966566919 +0100 --- src/testdir/test_vim9_assign.vim 2021-01-07 21:56:35.244998244 +0100 *************** *** 1464,1468 **** --- 1464,1489 ---- 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 *** ../vim-8.2.2310/src/version.c 2021-01-07 20:23:29.846515289 +0100 --- src/version.c 2021-01-07 21:38:38.395022245 +0100 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2311, /**/ -- hundred-and-one symptoms of being an internet addict: 104. When people ask about the Presidential Election you ask "Which country?" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///