changeset 32262:854aeaac48b7 v9.0.1462

patch 9.0.1462: recursively calling :defer function if it does :qa Commit: https://github.com/vim/vim/commit/42994bf678f46dc9ca66e49f512261da8864fff6 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Apr 17 19:23:45 2023 +0100 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 https://github.com/vim/vim/issues/12266)
author Bram Moolenaar <Bram@vim.org>
date Mon, 17 Apr 2023 20:30:07 +0200
parents 8f47c6cfe513
children e5aa6508bcc1
files src/testdir/test_user_func.vim src/userfunc.c src/version.c
diffstat 3 files changed, 21 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_user_func.vim
+++ b/src/testdir/test_user_func.vim
@@ -656,6 +656,7 @@ func Test_defer_quitall()
       vim9script
       func DeferLevelTwo()
         call writefile(['text'], 'XQuitallTwo', 'D')
+        call writefile(['quit'], 'XQuitallThree', 'a')
         qa!
       endfunc
 
@@ -671,6 +672,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()
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -6096,20 +6096,27 @@ handle_defer_one(funccall_T *funccal)
 
     for (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;
-	typval_T    rettv;
-	defer_T	    *dr = ((defer_T *)funccal->fc_defer.ga_data) + idx;
-	int	    i;
-
 	CLEAR_FIELD(funcexe);
 	funcexe.fe_evaluate = TRUE;
 
+	typval_T    rettv;
 	rettv.v_type = VAR_UNKNOWN;	// clear_tv() uses this
-	call_func(dr->dr_name, -1, &rettv,
-				    dr->dr_argcount, dr->dr_argvars, &funcexe);
+
+	char_u *name = dr->dr_name;
+	dr->dr_name = NULL;
+
+	call_func(name, -1, &rettv, dr->dr_argcount, dr->dr_argvars, &funcexe);
+
 	clear_tv(&rettv);
-	vim_free(dr->dr_name);
-	for (i = dr->dr_argcount - 1; i >= 0; --i)
+	vim_free(name);
+	for (int i = dr->dr_argcount - 1; i >= 0; --i)
 	    clear_tv(&dr->dr_argvars[i]);
     }
     ga_clear(&funccal->fc_defer);
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1462,
+/**/
     1461,
 /**/
     1460,