diff src/testdir/test_tagfunc.vim @ 26518:13ba00ef7687 v8.2.3788

patch 8.2.3788: lambda for option that is a function may be freed Commit: https://github.com/vim/vim/commit/6ae8fae8696623b527c7fb22567f6a3705b2f0dd Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Dec 12 16:26:44 2021 +0000 patch 8.2.3788: lambda for option that is a function may be freed Problem: Lambda for option that is a function may be garbage collected. Solution: Set a reference in the funcref. (Yegappan Lakshmanan, closes #9330)
author Bram Moolenaar <Bram@vim.org>
date Sun, 12 Dec 2021 17:30:04 +0100
parents 7eaf61a67d18
children 33d680d372aa
line wrap: on
line diff
--- a/src/testdir/test_tagfunc.vim
+++ b/src/testdir/test_tagfunc.vim
@@ -132,55 +132,121 @@ func Test_tagfunc_callback()
     return v:null
   endfunc
 
-  " Test for using a function()
-  set tagfunc=function('MytagFunc1',[10])
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a11', 'E433:')
-  call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
+  let lines =<< trim END
+    #" Test for using a function()
+    set tagfunc=function('g:MytagFunc1',\ [10])
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a11', 'E433:')
+    call assert_equal([10, 'a11', '', {}], g:MytagFunc1_args)
+
+    #" Using a funcref variable to set 'tagfunc'
+    VAR Fn = function('g:MytagFunc1', [11])
+    LET &tagfunc = Fn
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a12', 'E433:')
+    call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
+
+    #" Using a string(funcref_variable) to set 'tagfunc'
+    LET Fn = function('g:MytagFunc1', [12])
+    LET &tagfunc = string(Fn)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a12', 'E433:')
+    call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
+
+    #" Test for using a funcref()
+    set tagfunc=funcref('g:MytagFunc1',\ [13])
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a13', 'E433:')
+    call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
+
+    #" Using a funcref variable to set 'tagfunc'
+    LET Fn = funcref('g:MytagFunc1', [14])
+    LET &tagfunc = Fn
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a14', 'E433:')
+    call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
+
+    #" Using a string(funcref_variable) to set 'tagfunc'
+    LET Fn = funcref('g:MytagFunc1', [15])
+    LET &tagfunc = string(Fn)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a14', 'E433:')
+    call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
+
+    #" Test for using a lambda function
+    VAR optval = "LSTART a, b, c LMIDDLE MytagFunc1(16, a, b, c) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set tagfunc=" .. optval
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a17', 'E433:')
+    call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
 
-  " Using a funcref variable to set 'tagfunc'
-  let Fn = function('MytagFunc1', [11])
-  let &tagfunc = Fn
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a12', 'E433:')
-  call assert_equal([11, 'a12', '', {}], g:MytagFunc1_args)
+    #" Set 'tagfunc' to a lambda expression
+    LET &tagfunc = LSTART a, b, c LMIDDLE MytagFunc1(17, a, b, c) LEND
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a18', 'E433:')
+    call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
+
+    #" Set 'tagfunc' to a string(lambda expression)
+    LET &tagfunc = 'LSTART a, b, c LMIDDLE MytagFunc1(18, a, b, c) LEND'
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails('tag a18', 'E433:')
+    call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
+
+    #" Set 'tagfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a, b, c LMIDDLE MytagFunc1(19, a, b, c) LEND
+    LET &tagfunc = Lambda
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails("tag a19", "E433:")
+    call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
 
-  " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = function('MytagFunc1', [12])
-  let &tagfunc = string(Fn)
+    #" Set 'tagfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a, b, c LMIDDLE MytagFunc1(20, a, b, c) LEND
+    LET &tagfunc = string(Lambda)
+    new | only
+    LET g:MytagFunc1_args = []
+    call assert_fails("tag a19", "E433:")
+    call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
+
+    #" Test for using a lambda function with incorrect return value
+    LET Lambda = LSTART a, b, c LMIDDLE strlen(a) LEND
+    LET &tagfunc = string(Lambda)
+    new | only
+    call assert_fails("tag a20", "E987:")
+
+    #" Test for clearing the 'tagfunc' option
+    set tagfunc=''
+    set tagfunc&
+    call assert_fails("set tagfunc=function('abc')", "E700:")
+    call assert_fails("set tagfunc=funcref('abc')", "E700:")
+
+    #" set 'tagfunc' to a non-existing function
+    LET &tagfunc = function('g:MytagFunc1', [21])
+    call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &tagfunc = function('NonExistingFunc')", 'E700:')
+    call assert_fails("tag axb123", 'E426:')
+  END
+  call CheckLegacyAndVim9Success(lines)
+
+  let &tagfunc = "{a -> 'abc'}"
+  call assert_fails("echo taglist('a')", "E987:")
+
+  " Using Vim9 lambda expression in legacy context should fail
+  set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
   new | only
   let g:MytagFunc1_args = []
-  call assert_fails('tag a12', 'E433:')
-  call assert_equal([12, 'a12', '', {}], g:MytagFunc1_args)
-
-  " Test for using a funcref()
-  func MytagFunc2(pat, flags, info)
-    let g:MytagFunc2_args = [a:pat, a:flags, a:info]
-    return v:null
-  endfunc
-  set tagfunc=funcref('MytagFunc1',[13])
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a13', 'E433:')
-  call assert_equal([13, 'a13', '', {}], g:MytagFunc1_args)
-
-  " Using a funcref variable to set 'tagfunc'
-  let Fn = funcref('MytagFunc1', [14])
-  let &tagfunc = Fn
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a14', 'E433:')
-  call assert_equal([14, 'a14', '', {}], g:MytagFunc1_args)
-
-  " Using a string(funcref_variable) to set 'tagfunc'
-  let Fn = funcref('MytagFunc1', [15])
-  let &tagfunc = string(Fn)
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a14', 'E433:')
-  call assert_equal([15, 'a14', '', {}], g:MytagFunc1_args)
+  call assert_fails("tag a17", "E117:")
+  call assert_equal([], g:MytagFunc1_args)
 
   " Test for using a script local function
   set tagfunc=<SID>ScriptLocalTagFunc
@@ -205,70 +271,25 @@ func Test_tagfunc_callback()
   call assert_fails('tag a16', 'E433:')
   call assert_equal(['a16', '', {}], g:ScriptLocalFuncArgs)
 
-  " Test for using a lambda function
-  set tagfunc={a,\ b,\ c\ ->\ MytagFunc1(16,\ a,\ b,\ c)}
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a17', 'E433:')
-  call assert_equal([16, 'a17', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a lambda expression
-  let &tagfunc = {a, b, c -> MytagFunc1(17, a, b, c)}
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a18', 'E433:')
-  call assert_equal([17, 'a18', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a string(lambda expression)
-  let &tagfunc = '{a, b, c -> MytagFunc1(18, a, b, c)}'
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails('tag a18', 'E433:')
-  call assert_equal([18, 'a18', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a variable with a lambda expression
-  let Lambda = {a, b, c -> MytagFunc1(19, a, b, c)}
-  let &tagfunc = Lambda
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails("tag a19", "E433:")
-  call assert_equal([19, 'a19', '', {}], g:MytagFunc1_args)
-
-  " Set 'tagfunc' to a string(variable with a lambda expression)
-  let Lambda = {a, b, c -> MytagFunc1(20, a, b, c)}
-  let &tagfunc = string(Lambda)
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails("tag a19", "E433:")
-  call assert_equal([20, 'a19', '', {}], g:MytagFunc1_args)
-
-  " Test for using a lambda function with incorrect return value
-  let Lambda = {s -> strlen(s)}
-  let &tagfunc = string(Lambda)
-  new | only
-  call assert_fails("tag a20", "E987:")
-
-  " Test for clearing the 'tagfunc' option
-  set tagfunc=''
-  set tagfunc&
-
-  call assert_fails("set tagfunc=function('abc')", "E700:")
-  call assert_fails("set tagfunc=funcref('abc')", "E700:")
-  let &tagfunc = "{a -> 'abc'}"
-  call assert_fails("echo taglist('a')", "E987:")
-
-  " Using Vim9 lambda expression in legacy context should fail
-  set tagfunc=(a,\ b,\ c)\ =>\ g:MytagFunc1(21,\ a,\ b,\ c)
-  new | only
-  let g:MytagFunc1_args = []
-  call assert_fails("tag a17", "E117:")
-  call assert_equal([], g:MytagFunc1_args)
-
-  " set 'tagfunc' to a non-existing function
-  call assert_fails("set tagfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &tagfunc = function('NonExistingFunc')", 'E700:')
-  call assert_fails("tag axb123", 'E426:')
-  bw!
+  " set 'tagfunc' to a partial with dict. This used to cause a crash.
+  func SetTagFunc()
+    let params = {'tagfn': function('g:DictTagFunc')}
+    let &tagfunc = params.tagfn
+  endfunc
+  func g:DictTagFunc(_) dict
+  endfunc
+  call SetTagFunc()
+  new
+  call SetTagFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set tagfunc=
+  wincmd w
+  set tagfunc=
+  :%bw!
+  delfunc g:DictTagFunc
+  delfunc SetTagFunc
 
   " Vim9 tests
   let lines =<< trim END
@@ -284,42 +305,11 @@ func Test_tagfunc_callback()
     g:Vim9tagFuncArgs = []
     assert_fails('tag a10', 'E433:')
     assert_equal([60, 'a10', '', {}], g:Vim9tagFuncArgs)
-
-    # Test for using a lambda
-    &tagfunc = (a, b, c) => MytagFunc1(61, a, b, c)
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a20', 'E433:')
-    assert_equal([61, 'a20', '', {}], g:MytagFunc1_args)
-
-    # Test for using a string(lambda)
-    &tagfunc = '(a, b, c) => MytagFunc1(62, a, b, c)'
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a20', 'E433:')
-    assert_equal([62, 'a20', '', {}], g:MytagFunc1_args)
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a, b, c) => MytagFunc1(63, a, b, c)
-    &tagfunc = Fn
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a30', 'E433:')
-    assert_equal([63, 'a30', '', {}], g:MytagFunc1_args)
-
-    # Test for using a variable with a lambda expression
-    Fn = (a, b, c) => MytagFunc1(64, a, b, c)
-    &tagfunc = string(Fn)
-    new | only
-    g:MytagFunc1_args = []
-    assert_fails('tag a30', 'E433:')
-    assert_equal([64, 'a30', '', {}], g:MytagFunc1_args)
   END
   call CheckScriptSuccess(lines)
 
   " cleanup
   delfunc MytagFunc1
-  delfunc MytagFunc2
   set tagfunc&
   %bw!
 endfunc