diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 38766e3301568..bb5635626484e 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -4339,6 +4339,33 @@ def Test_defer() assert_equal('', glob('XdeferFile')) enddef +def Test_invalid_redir() + var lines =<< trim END + def Tone() + if 1 + redi =>@0 + redi END + endif + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E354:') + delfunc g:Tone + + # this was reading past the end of the line + lines =<< trim END + def Ttwo() + if 0 + redi =>@0 + redi END + endif + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E354:') + delfunc g:Ttwo +enddef + " The following messes up syntax highlight, keep near the end. if has('python3') def Test_python3_command() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index a0b8352f3f727..6c8f1f0ce0b0d 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2136,15 +2136,66 @@ enddef def Test_skipped_redir() var lines =<< trim END - def T() + def Tredir() if 0 - redir =>l[0] + redir => l[0] redir END endif enddef defcompile END v9.CheckScriptSuccess(lines) + delfunc g:Tredir + + lines =<< trim END + def Tredir() + if 0 + redir => l[0] + endif + echo 'executed' + if 0 + redir END + endif + enddef + defcompile + END + v9.CheckScriptSuccess(lines) + delfunc g:Tredir + + lines =<< trim END + def Tredir() + var l = [''] + if 1 + redir => l[0] + endif + echo 'executed' + if 0 + redir END + else + redir END + endif + enddef + defcompile + END + v9.CheckScriptSuccess(lines) + delfunc g:Tredir + + lines =<< trim END + let doit = 1 + def Tredir() + var l = [''] + if g:doit + redir => l[0] + endif + echo 'executed' + if g:doit + redir END + endif + enddef + defcompile + END + v9.CheckScriptSuccess(lines) + delfunc g:Tredir enddef def Test_for_loop() diff --git a/src/version.c b/src/version.c index 6570ac52a8a98..467f8e083690b 100644 --- a/src/version.c +++ b/src/version.c @@ -699,6 +699,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 742, /**/ 741, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 73f95b26dfe52..e5abf895e6a73 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -2412,34 +2412,37 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx) { if (STRNCMP(arg, "END", 3) == 0) { - if (lhs->lhs_append) + if (cctx->ctx_skip != SKIP_YES) { - // First load the current variable value. - if (compile_load_lhs_with_index(lhs, lhs->lhs_whole, + if (lhs->lhs_append) + { + // First load the current variable value. + if (compile_load_lhs_with_index(lhs, lhs->lhs_whole, cctx) == FAIL) - return NULL; - } + return NULL; + } - // Gets the redirected text and put it on the stack, then store it - // in the variable. - generate_instr_type(cctx, ISN_REDIREND, &t_string); + // Gets the redirected text and put it on the stack, then store + // it in the variable. + generate_instr_type(cctx, ISN_REDIREND, &t_string); - if (lhs->lhs_append) - generate_CONCAT(cctx, 2); + if (lhs->lhs_append) + generate_CONCAT(cctx, 2); - if (lhs->lhs_has_index) - { - // Use the info in "lhs" to store the value at the index in the - // list or dict. - if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE, + if (lhs->lhs_has_index) + { + // Use the info in "lhs" to store the value at the index in + // the list or dict. + if (compile_assign_unlet(lhs->lhs_whole, lhs, TRUE, &t_string, cctx) == FAIL) + return NULL; + } + else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL) return NULL; - } - else if (generate_store_lhs(cctx, lhs, -1, FALSE) == FAIL) - return NULL; - VIM_CLEAR(lhs->lhs_name); - VIM_CLEAR(lhs->lhs_whole); + VIM_CLEAR(lhs->lhs_name); + VIM_CLEAR(lhs->lhs_whole); + } return arg + 3; } emsg(_(e_cannot_nest_redir)); @@ -2465,13 +2468,20 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx) if (need_type(&t_string, lhs->lhs_member_type, -1, 0, cctx, FALSE, FALSE) == FAIL) return NULL; - generate_instr(cctx, ISN_REDIRSTART); - lhs->lhs_append = append; - if (lhs->lhs_has_index) + if (cctx->ctx_skip == SKIP_YES) { - lhs->lhs_whole = vim_strnsave(arg, lhs->lhs_varlen_total); - if (lhs->lhs_whole == NULL) - return NULL; + VIM_CLEAR(lhs->lhs_name); + } + else + { + generate_instr(cctx, ISN_REDIRSTART); + lhs->lhs_append = append; + if (lhs->lhs_has_index) + { + lhs->lhs_whole = vim_strnsave(arg, lhs->lhs_varlen_total); + if (lhs->lhs_whole == NULL) + return NULL; + } } return arg + lhs->lhs_varlen_total; diff --git a/src/vim9compile.c b/src/vim9compile.c index b3e1b83ea4ef6..73bfa6c6af1e2 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1283,6 +1283,19 @@ vim9_declare_error(char_u *name) semsg(_(e_cannot_declare_a_scope_variable), scope, name); } +/* + * Return TRUE if "name" is a valid register to use. + * Return FALSE and give an error message if not. + */ + static int +valid_dest_reg(int name) +{ + if ((name == '@' || valid_yank_reg(name, FALSE)) && name != '.') + return TRUE; + emsg_invreg(name); + return FAIL; +} + /* * For one assignment figure out the type of destination. Return it in "dest". * When not recognized "dest" is not set. @@ -1364,12 +1377,8 @@ get_var_dest( } else if (*name == '@') { - if (name[1] != '@' - && (!valid_yank_reg(name[1], FALSE) || name[1] == '.')) - { - emsg_invreg(name[1]); + if (!valid_dest_reg(name[1])) return FAIL; - } *dest = dest_reg; *type = name[1] == '#' ? &t_number_or_string : &t_string; } @@ -1445,7 +1454,11 @@ compile_lhs( // "var_end" is the end of the variable/option/etc. name. lhs->lhs_dest_end = skip_var_one(var_start, FALSE); if (*var_start == '@') + { + if (!valid_dest_reg(var_start[1])) + return FAIL; var_end = var_start + 2; + } else { // skip over the leading "&", "&l:", "&g:" and "$"