0
0
mirror of https://github.com/vim/vim.git synced 2025-07-04 23:07:33 -04:00

patch 9.1.0159: Crash in WinClosed after BufUnload closes other windows

Problem:  Crash in WinClosed after BufUnload closes other windows
Solution: Don't trigger WinClosed if the buffer is NULL (zeertzjq)

Now win_close_othertab() doesn't trigger any autocommands if the buffer
is NULL, so remove the autocmd blocking above (which was added not long
ago in patch v9.0.0550) for consistency.

Also remove an unreachable close_last_window_tabpage() above:
- It is only reached if only_one_window() returns TRUE and last_window()
  returns FALSE.
- If only_one_window() returns TRUE, there is only one tabpage.
- If there is only one tabpage and last_window() returns FALSE, the
  one_window() in last_window() must return FALSE, and the ONE_WINDOW
  in close_last_window_tabpage() must also be FALSE.
- So close_last_window_tabpage() doesn't do anything and returns FALSE.

Then the curtab != prev_curtab check also doesn't make much sense, and
the only_one_window() can be replaced with a check for popup and a call
to last_window() since this is a stricter check than only_one_window().

closes: #14166

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
zeertzjq 2024-03-09 15:39:27 +01:00 committed by Christian Brabandt
parent 8a01744c56
commit b2ec0da080
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
3 changed files with 39 additions and 12 deletions

View File

@ -819,6 +819,27 @@ func Test_WinClosed_switch_tab()
%bwipe! %bwipe!
endfunc endfunc
" This used to trigger WinClosed twice for the same window, and the window's
" buffer was NULL in the second autocommand.
func Test_WinClosed_BufUnload_close_other()
tabnew
let g:tab = tabpagenr()
let g:buf = bufnr()
new
setlocal bufhidden=wipe
augroup test-WinClosed
autocmd BufUnload * ++once exe g:buf .. 'bwipe!'
autocmd WinClosed * call tabpagebuflist(g:tab)
augroup END
close
unlet g:tab
unlet g:buf
autocmd! test-WinClosed
augroup! test-WinClosed
%bwipe!
endfunc
func s:AddAnAutocmd() func s:AddAnAutocmd()
augroup vimBarTest augroup vimBarTest
au BufReadCmd * echo 'hello' au BufReadCmd * echo 'hello'

View File

@ -704,6 +704,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 */
/**/
159,
/**/ /**/
158, 158,
/**/ /**/

View File

@ -2585,7 +2585,7 @@ one_window(void)
/* /*
* Close the possibly last window in a tab page. * Close the possibly last window in a tab page.
* Returns TRUE when the window was closed already. * Return FALSE if there are other windows and nothing is done, TRUE otherwise.
*/ */
static int static int
close_last_window_tabpage( close_last_window_tabpage(
@ -2796,9 +2796,11 @@ win_close(win_T *win, int free_buf)
win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE); win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, TRUE);
if (only_one_window() && win_valid(win) && win->w_buffer == NULL if (win_valid(win) && win->w_buffer == NULL
&& (last_window() || curtab != prev_curtab #if defined(FEAT_PROP_POPUP)
|| close_last_window_tabpage(win, free_buf, prev_curtab))) && !popup_is_popup(win)
#endif
&& last_window())
{ {
// Autocommands have closed all windows, quit now. Restore // Autocommands have closed all windows, quit now. Restore
// curwin->w_buffer, otherwise writing viminfo may fail. // curwin->w_buffer, otherwise writing viminfo may fail.
@ -2812,10 +2814,7 @@ win_close(win_T *win, int free_buf)
&& win->w_buffer == NULL) && win->w_buffer == NULL)
{ {
// Need to close the window anyway, since the buffer is NULL. // Need to close the window anyway, since the buffer is NULL.
// Don't trigger autocmds with a NULL buffer.
block_autocmds();
win_close_othertab(win, FALSE, prev_curtab); win_close_othertab(win, FALSE, prev_curtab);
unblock_autocmds();
return FAIL; return FAIL;
} }
@ -3352,10 +3351,15 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
return; // window is already being closed return; // window is already being closed
// Trigger WinClosed just before starting to free window-related resources. // Trigger WinClosed just before starting to free window-related resources.
// If the buffer is NULL, it isn't safe to trigger autocommands,
// and win_close() should have already triggered WinClosed.
if (win->w_buffer != NULL)
{
trigger_winclosed(win); trigger_winclosed(win);
// autocmd may have freed the window already. // autocmd may have freed the window already.
if (!win_valid_any_tab(win)) if (!win_valid_any_tab(win))
return; return;
}
if (win->w_buffer != NULL) if (win->w_buffer != NULL)
// Close the link to the buffer. // Close the link to the buffer.