From 8e0ad6e261b81e9e2649bdba103904867b7ff6ef Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 19 Apr 2023 07:52:53 +0800 Subject: [PATCH] vim-patch:9.0.1462: recursively calling :defer function if it does :qa Problem: Recursively calling :defer function if it does :qa. Solution: Clear the defer entry before calling the function. (closes vim/vim#12266) https://github.com/vim/vim/commit/42994bf678f46dc9ca66e49f512261da8864fff6 Co-authored-by: Bram Moolenaar --- src/nvim/eval/userfunc.c | 16 ++++++++++++++-- test/old/testdir/test_user_func.vim | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 63d5f94f11..e8b50d8c94 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -3242,12 +3242,24 @@ static void handle_defer_one(funccall_T *funccal) { for (int idx = funccal->fc_defer.ga_len - 1; idx >= 0; idx--) { defer_T *dr = ((defer_T *)funccal->fc_defer.ga_data) + idx; + + if (dr->dr_name == NULL) { + // already being called, can happen if function does ":qa" + continue; + } + funcexe_T funcexe = { .fe_evaluate = true }; + typval_T rettv; rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - call_func(dr->dr_name, -1, &rettv, dr->dr_argcount, dr->dr_argvars, &funcexe); + + char *name = dr->dr_name; + dr->dr_name = NULL; + + call_func(name, -1, &rettv, dr->dr_argcount, dr->dr_argvars, &funcexe); + tv_clear(&rettv); - xfree(dr->dr_name); + xfree(name); for (int i = dr->dr_argcount - 1; i >= 0; i--) { tv_clear(&dr->dr_argvars[i]); } diff --git a/test/old/testdir/test_user_func.vim b/test/old/testdir/test_user_func.vim index 0f9b40814c..d217ca8c69 100644 --- a/test/old/testdir/test_user_func.vim +++ b/test/old/testdir/test_user_func.vim @@ -614,6 +614,7 @@ func Test_defer_quitall() " vim9script func DeferLevelTwo() call writefile(['text'], 'XQuitallTwo', 'D') + call writefile(['quit'], 'XQuitallThree', 'a') qa! endfunc @@ -632,6 +633,9 @@ func Test_defer_quitall() call assert_equal(0, v:shell_error) call assert_false(filereadable('XQuitallOne')) call assert_false(filereadable('XQuitallTwo')) + call assert_equal(['quit'], readfile('XQuitallThree')) + + call delete('XQuitallThree') endfunc func Test_defer_quitall_in_expr_func()