]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.0.1468: recursively calling :defer function if it does :qa v9.0.1468
authorzeertzjq <zeertzjq@outlook.com>
Tue, 18 Apr 2023 20:04:53 +0000 (21:04 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 18 Apr 2023 20:04:53 +0000 (21:04 +0100)
Problem:    Recursively calling :defer function if it does :qa in a compiled
            function.
Solution:   Clear the defer entry before calling the function. (closes #12271)

src/testdir/test_user_func.vim
src/version.c
src/vim9execute.c

index 8715a0b78a7c6d77dd67e545af10ce3cde95d61f..51d575433b3112443a120bf358ac0edbaf1d1121 100644 (file)
@@ -651,30 +651,55 @@ func Test_defer_throw()
   call assert_false(filereadable('XDeleteTwo'))
 endfunc
 
-func Test_defer_quitall()
+func Test_defer_quitall_func()
   let lines =<< trim END
-      vim9script
       func DeferLevelTwo()
-        call writefile(['text'], 'XQuitallTwo', 'D')
-        call writefile(['quit'], 'XQuitallThree', 'a')
+        call writefile(['text'], 'XQuitallFuncTwo', 'D')
+        call writefile(['quit'], 'XQuitallFuncThree', 'a')
         qa!
       endfunc
 
+      func DeferLevelOne()
+        call writefile(['text'], 'XQuitalFunclOne', 'D')
+        defer DeferLevelTwo()
+      endfunc
+
+      call DeferLevelOne()
+  END
+  call writefile(lines, 'XdeferQuitallFunc', 'D')
+  call system(GetVimCommand() .. ' -X -S XdeferQuitallFunc')
+  call assert_equal(0, v:shell_error)
+  call assert_false(filereadable('XQuitallFuncOne'))
+  call assert_false(filereadable('XQuitallFuncTwo'))
+  call assert_equal(['quit'], readfile('XQuitallFuncThree'))
+
+  call delete('XQuitallFuncThree')
+endfunc
+
+func Test_defer_quitall_def()
+  let lines =<< trim END
+      vim9script
+      def DeferLevelTwo()
+        call writefile(['text'], 'XQuitallDefTwo', 'D')
+        call writefile(['quit'], 'XQuitallDefThree', 'a')
+        qa!
+      enddef
+
       def DeferLevelOne()
-        call writefile(['text'], 'XQuitallOne', 'D')
-        call DeferLevelTwo()
+        call writefile(['text'], 'XQuitallDefOne', 'D')
+        defer DeferLevelTwo()
       enddef
 
       DeferLevelOne()
   END
-  call writefile(lines, 'XdeferQuitall', 'D')
-  let res = system(GetVimCommand() .. ' -X -S XdeferQuitall')
+  call writefile(lines, 'XdeferQuitallDef', 'D')
+  call system(GetVimCommand() .. ' -X -S XdeferQuitallDef')
   call assert_equal(0, v:shell_error)
-  call assert_false(filereadable('XQuitallOne'))
-  call assert_false(filereadable('XQuitallTwo'))
-  call assert_equal(['quit'], readfile('XQuitallThree'))
+  call assert_false(filereadable('XQuitallDefOne'))
+  call assert_false(filereadable('XQuitallDefTwo'))
+  call assert_equal(['quit'], readfile('XQuitallDefThree'))
 
-  call delete('XQuitallThree')
+  call delete('XQuitallDefThree')
 endfunc
 
 func Test_defer_quitall_in_expr_func()
@@ -693,7 +718,7 @@ func Test_defer_quitall_in_expr_func()
       call Test_defer_in_funcref()
   END
   call writefile(lines, 'XdeferQuitallExpr', 'D')
-  let res = system(GetVimCommand() .. ' -X -S XdeferQuitallExpr')
+  call system(GetVimCommand() .. ' -X -S XdeferQuitallExpr')
   call assert_equal(0, v:shell_error)
   call assert_false(filereadable('Xentry0'))
   call assert_false(filereadable('Xentry1'))
index bff52cc4ef5aaede9a74a5785481e8b190c591f4..6ff6bf6539de6311ad5fd15ae5049018e74a9dac 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1468,
 /**/
     1467,
 /**/
index d558ca0fa23d6b835a60963aab8411b672ff48a9..33a2fc6084b01f77c73ebeb187a5645090f2076d 100644 (file)
@@ -1063,6 +1063,10 @@ invoke_defer_funcs(ectx_T *ectx)
        int         obj_off = functv->v_type == VAR_PARTIAL ? 1 : 0;
        int         argcount = l->lv_len - 1 - obj_off;
 
+       if (functv->vval.v_string == NULL)
+           // already being called, can happen if function does ":qa"
+           continue;
+
        if (obj_off == 1)
            arg_li = arg_li->li_next;  // second list item is the object
        for (i = 0; i < argcount; ++i)
@@ -1082,9 +1086,14 @@ invoke_defer_funcs(ectx_T *ectx)
            if (funcexe.fe_object != NULL)
                ++funcexe.fe_object->obj_refcount;
        }
-       (void)call_func(functv->vval.v_string, -1,
-                                         &rettv, argcount, argvars, &funcexe);
+
+       char_u *name = functv->vval.v_string;
+       functv->vval.v_string = NULL;
+
+       (void)call_func(name, -1, &rettv, argcount, argvars, &funcexe);
+
        clear_tv(&rettv);
+       vim_free(name);
     }
 }