diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index eb5c6e503a..0fadc0d220 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3017,11 +3017,12 @@ void ex_call(exarg_T *eap) } } - // When inside :try we need to check for following "| catch". - if (!failed || eap->cstack->cs_trylevel > 0) { + // When inside :try we need to check for following "| catch" or "| endtry". + // Not when there was an error, but do check if an exception was thrown. + if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) { // Check for trailing illegal characters and a following command. if (!ends_excmd(*arg)) { - if (!failed) { + if (!failed && !aborting()) { emsg_severe = true; emsg(_(e_trailing)); } diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 3c7c635d98..f4aaab5c43 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1083,7 +1083,7 @@ void ex_endwhile(exarg_T *eap) if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = err; } else { - fl = cstack->cs_flags[cstack->cs_idx]; + fl = cstack->cs_flags[cstack->cs_idx]; if (!(fl & csf)) { // If we are in a ":while" or ":for" but used the wrong endloop // command, do not rewind to the next enclosing ":for"/":while". @@ -1575,6 +1575,7 @@ void ex_endtry(exarg_T *eap) if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { eap->errmsg = get_end_emsg(cstack); + // Find the matching ":try" and report what's missing. idx = cstack->cs_idx; do { @@ -1594,6 +1595,9 @@ void ex_endtry(exarg_T *eap) if (current_exception) { discard_current_exception(); } + + // report eap->errmsg, also when there already was an error + did_emsg = false; } else { idx = cstack->cs_idx; @@ -1664,8 +1668,10 @@ void ex_endtry(exarg_T *eap) */ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE); - --cstack->cs_idx; - --cstack->cs_trylevel; + if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { + cstack->cs_idx--; + } + cstack->cs_trylevel--; if (!skip) { report_resume_pending(pending, @@ -1913,7 +1919,7 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) default: if (cstack->cs_flags[idx] & CSF_FINALLY) { - if (cstack->cs_pending[idx] & CSTP_THROW) { + if ((cstack->cs_pending[idx] & CSTP_THROW) && cstack->cs_exception[idx] != NULL) { // Cancel the pending exception. This is in the // finally clause, so that the stack of the // caught exceptions is not involved. @@ -1934,8 +1940,9 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive) */ if (!(cstack->cs_flags[idx] & CSF_FINALLY)) { if ((cstack->cs_flags[idx] & CSF_ACTIVE) - && (cstack->cs_flags[idx] & CSF_CAUGHT)) { + && (cstack->cs_flags[idx] & CSF_CAUGHT) && !(cstack->cs_flags[idx] & CSF_FINISHED)) { finish_exception((except_T *)cstack->cs_exception[idx]); + cstack->cs_flags[idx] |= CSF_FINISHED; } // Stop at this try conditional - except the try block never // got active (because of an inactive surrounding conditional diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index 15da4b2d60..1cd0a47d7a 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -14,9 +14,10 @@ #define CSF_TRY 0x0100 // is a ":try" #define CSF_FINALLY 0x0200 // ":finally" has been passed -#define CSF_THROWN 0x0400 // exception thrown to this try conditional -#define CSF_CAUGHT 0x0800 // exception caught by this try conditional -#define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" +#define CSF_THROWN 0x0800 // exception thrown to this try conditional +#define CSF_CAUGHT 0x1000 // exception caught by this try conditional +#define CSF_FINISHED 0x2000 // CSF_CAUGHT was handled by finish_exception() +#define CSF_SILENT 0x4000 // "emsg_silent" reset by ":try" // Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset // (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 296255ed8c..5a33ebfb37 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5173,19 +5173,19 @@ static void win_redr_status(win_T *wp) *(p + len++) = ' '; } if (bt_help(wp->w_buffer)) { - STRCPY(p + len, _("[Help]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Help]")); len += (int)STRLEN(p + len); } if (wp->w_p_pvw) { - STRCPY(p + len, _("[Preview]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[Preview]")); len += (int)STRLEN(p + len); } if (bufIsChanged(wp->w_buffer)) { - STRCPY(p + len, "[+]"); - len += 3; + snprintf((char *)p + len, MAXPATHL - len, "%s", "[+]"); + len += (int)STRLEN(p + len); } if (wp->w_buffer->b_p_ro) { - STRCPY(p + len, _("[RO]")); + snprintf((char *)p + len, MAXPATHL - len, "%s", _("[RO]")); // len += (int)STRLEN(p + len); // dead assignment } diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index fad13e3340..492d09c645 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -523,4 +523,16 @@ func Test_statusline_mbyte_fillchar() %bw! endfunc +" Used to write beyond allocated memory. This assumes MAXPATHL is 4096 bytes. +func Test_statusline_verylong_filename() + let fname = repeat('x', 4090) + " Nvim's swap file creation fails on Windows (E303) due to fname's length + " exe "new " .. fname + exe "noswapfile new " .. fname + set buftype=help + set previewwindow + redraw + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim index adc1745b39..205ed095ea 100644 --- a/src/nvim/testdir/test_trycatch.vim +++ b/src/nvim/testdir/test_trycatch.vim @@ -1996,5 +1996,97 @@ func Test_reload_in_try_catch() call delete('Xreload') endfunc +" Test for using throw in a called function with following error {{{1 +func Test_user_command_throw_in_function_call() + let lines =<< trim END + function s:get_dict() abort + throw 'my_error' + endfunction + + try + call s:get_dict().foo() + catch /my_error/ + let caught = 'yes' + catch + let caught = v:exception + endtry + call assert_equal('yes', caught) + END + call writefile(lines, 'XtestThrow') + source XtestThrow + + call delete('XtestThrow') + unlet g:caught +endfunc + +" Test for using throw in a called function with following endtry {{{1 +func Test_user_command_function_call_with_endtry() + let lines =<< trim END + funct s:throw(msg) abort + throw a:msg + endfunc + func s:main() abort + try + try + throw 'err1' + catch + call s:throw('err2') | endtry + catch + let s:caught = 'yes' + endtry + endfunc + + call s:main() + call assert_equal('yes', s:caught) + END + call writefile(lines, 'XtestThrow') + source XtestThrow + + call delete('XtestThrow') +endfunc + +func ThisWillFail() + +endfunc + +" This was crashing prior to the fix in 8.2.3478. +func Test_error_in_catch_and_finally() + let lines =<< trim END + try + echo x + catch + for l in [] + finally + END + call writefile(lines, 'XtestCatchAndFinally') + try + source XtestCatchAndFinally + catch /E600:/ + endtry + + call delete('XtestCatchAndFinally') +endfunc + +" This was causing an illegal memory access +func Test_leave_block_in_endtry_not_called() + let lines =<< trim END + " vim9script + " try # + try " + for x in [] + if + endwhile + if + endtry + END + call writefile(lines, 'XtestEndtry') + try + source XtestEndtry + catch /E171:/ + endtry + + call delete('XtestEndtry') +endfunc + " Modeline {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker