diff options
author | Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> | 2021-05-11 22:17:12 -0400 |
---|---|---|
committer | Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> | 2021-05-13 20:47:26 -0400 |
commit | f98433d82a340008fdc51c2093e74400e71101dc (patch) | |
tree | 7b381a5f7eb4243226c97655af05e36dc09ef97b /src | |
parent | 433807763236427cbe0ff347c1c5b77877aa0aff (diff) | |
download | rneovim-f98433d82a340008fdc51c2093e74400e71101dc.tar.gz rneovim-f98433d82a340008fdc51c2093e74400e71101dc.tar.bz2 rneovim-f98433d82a340008fdc51c2093e74400e71101dc.zip |
vim-patch:8.2.2354: crash with a weird combination of autocommands
Problem: Crash with a weird combination of autocommands.
Solution: Increment b_nwindows when needed. (closes vim/vim#7674)
https://github.com/vim/vim/commit/797e63b9f2baa1853e7063aac478d663cd02f207
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer.c | 25 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 16 | ||||
-rw-r--r-- | src/nvim/testdir/test_autocmd.vim | 21 |
3 files changed, 48 insertions, 14 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index ce4163fccf..5e700dea8a 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -407,7 +407,8 @@ bool buf_valid(buf_T *buf) /// there to be only one window with this buffer. e.g. when /// ":quit" is supposed to close the window but autocommands /// close all other windows. -void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) +/// @returns true when we got to the end and b_nwindows was decremented. +bool close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) { bool unload_buf = (action != 0); bool del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); @@ -444,7 +445,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) // halfway a command that relies on it). Unloading is allowed. if (buf->b_locked > 0 && (del_buf || wipe_buf)) { EMSG(_("E937: Attempt to delete a buffer that is in use")); - return; + return false; } if (win != NULL // Avoid bogus clang warning. @@ -471,13 +472,13 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); - return; + return false; } buf->b_locked--; if (abort_if_last && last_nonfloat(win)) { // Autocommands made this the only window. EMSG(_(e_auabort)); - return; + return false; } // When the buffer becomes hidden, but is not unloaded, trigger @@ -488,17 +489,17 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) buf) && !bufref_valid(&bufref)) { // Autocommands deleted the buffer. EMSG(_(e_auabort)); - return; + return false; } buf->b_locked--; if (abort_if_last && last_nonfloat(win)) { // Autocommands made this the only window. EMSG(_(e_auabort)); - return; + return false; } } if (aborting()) { // autocmds may abort script processing - return; + return false; } } @@ -525,7 +526,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) /* Return when a window is displaying the buffer or when it's not * unloaded. */ if (buf->b_nwindows > 0 || !unload_buf) { - return; + return false; } if (buf->terminal) { @@ -561,11 +562,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) if (!bufref_valid(&bufref)) { // Autocommands may have deleted the buffer. - return; + return false; } if (aborting()) { // Autocmds may abort script processing. - return; + return false; } /* @@ -576,7 +577,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) * deleted buffer. */ if (buf == curbuf && !is_curbuf) { - return; + return false; } if (win != NULL // Avoid bogus clang warning. @@ -636,6 +637,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) buf->b_p_bl = false; } } + // NOTE: at this point "curbuf" may be invalid! + return true; } /// Make buffer not contain a file. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 349c7d6280..18530a7d58 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2462,6 +2462,7 @@ int do_ecmd( auto_buf = true; } else { win_T *the_curwin = curwin; + buf_T *was_curbuf = curbuf; // Set w_closing to avoid that autocommands close the window. // Set b_locked for the same reason. @@ -2475,9 +2476,10 @@ int do_ecmd( // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(false); - close_buffer(oldwin, curbuf, - (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, - false); + const bool did_decrement = close_buffer( + oldwin, curbuf, + (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, + false); // Autocommands may have closed the window. if (win_valid(the_curwin)) { @@ -2497,6 +2499,14 @@ int do_ecmd( goto theend; } if (buf == curbuf) { // already in new buffer + // close_buffer() has decremented the window count, + // increment it again here and restore w_buffer. + if (did_decrement && buf_valid(was_curbuf)) { + was_curbuf->b_nwindows++; + } + if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL) { + oldwin->w_buffer = was_curbuf; + } auto_buf = true; } else { // <VN> We could instead free the synblock diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index fb3aa2b3b0..a2aa1c32c5 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -452,6 +452,27 @@ func Test_autocmd_bufwipe_in_SessLoadPost() endfor endfunc +" Using :blast and :ball for many events caused a crash, because b_nwindows was +" not incremented correctly. +func Test_autocmd_blast_badd() + let content =<< trim [CODE] + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast + edit foo1 + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball + edit foo2 + call writefile(['OK'], 'Xerrors') + qall + [CODE] + + call writefile(content, 'XblastBall') + call system(GetVimCommand() .. ' --clean -S XblastBall') + " call assert_match('OK', readfile('Xerrors')->join()) + call assert_match('OK', join(readfile('Xerrors'))) + + call delete('XblastBall') + call delete('Xerrors') +endfunc + " SEGV occurs in older versions. func Test_autocmd_bufwipe_in_SessLoadPost2() tabnew |