blob: af7d466f6bb4be1c352853daa1b5dea5e4605b7b [file] [log] [blame]
Bram Moolenaar53f0c962017-10-22 14:23:59 +02001" Test for user functions.
2" Also test an <expr> mapping calling a function.
3" Also test that a builtin function cannot be replaced.
4" Also test for regression when calling arbitrary expression.
5
Bram Moolenaar476a6132020-04-08 19:48:56 +02006source check.vim
7source shared.vim
Bram Moolenaar16900322022-09-08 19:51:45 +01008import './vim9.vim' as v9
Bram Moolenaar476a6132020-04-08 19:48:56 +02009
Bram Moolenaar53f0c962017-10-22 14:23:59 +020010func Table(title, ...)
11 let ret = a:title
12 let idx = 1
13 while idx <= a:0
14 exe "let ret = ret . a:" . idx
15 let idx = idx + 1
16 endwhile
17 return ret
18endfunc
19
20func Compute(n1, n2, divname)
21 if a:n2 == 0
22 return "fail"
23 endif
24 exe "let g:" . a:divname . " = ". a:n1 / a:n2
25 return "ok"
26endfunc
27
28func Expr1()
29 silent! normal! v
30 return "111"
31endfunc
32
33func Expr2()
34 call search('XX', 'b')
35 return "222"
36endfunc
37
38func ListItem()
39 let g:counter += 1
40 return g:counter . '. '
41endfunc
42
43func ListReset()
44 let g:counter = 0
45 return ''
46endfunc
47
48func FuncWithRef(a)
49 unlet g:FuncRef
50 return a:a
51endfunc
52
53func Test_user_func()
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020054 let g:FuncRef = function("FuncWithRef")
Bram Moolenaar53f0c962017-10-22 14:23:59 +020055 let g:counter = 0
56 inoremap <expr> ( ListItem()
57 inoremap <expr> [ ListReset()
58 imap <expr> + Expr1()
59 imap <expr> * Expr2()
60 let g:retval = "nop"
61
62 call assert_equal('xxx4asdf', Table("xxx", 4, "asdf"))
63 call assert_equal('fail', Compute(45, 0, "retval"))
64 call assert_equal('nop', g:retval)
65 call assert_equal('ok', Compute(45, 5, "retval"))
66 call assert_equal(9, g:retval)
67 call assert_equal(333, g:FuncRef(333))
68
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020069 let g:retval = "nop"
70 call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
71 call assert_equal('fail', 45->Compute(0, "retval"))
72 call assert_equal('nop', g:retval)
73 call assert_equal('ok', 45->Compute(5, "retval"))
74 call assert_equal(9, g:retval)
75 " call assert_equal(333, 333->g:FuncRef())
76
Bram Moolenaar53f0c962017-10-22 14:23:59 +020077 enew
78
79 normal oXX+-XX
80 call assert_equal('XX111-XX', getline('.'))
81 normal o---*---
82 call assert_equal('---222---', getline('.'))
83 normal o(one
84 call assert_equal('1. one', getline('.'))
85 normal o(two
86 call assert_equal('2. two', getline('.'))
87 normal o[(one again
88 call assert_equal('1. one again', getline('.'))
89
Bram Moolenaar476a6132020-04-08 19:48:56 +020090 " Try to overwrite a function in the global (g:) scope
Bram Moolenaar53f0c962017-10-22 14:23:59 +020091 call assert_equal(3, max([1, 2, 3]))
Bram Moolenaare2e40752020-09-04 21:18:46 +020092 call assert_fails("call extend(g:, {'max': function('min')})", 'E704:')
Bram Moolenaar53f0c962017-10-22 14:23:59 +020093 call assert_equal(3, max([1, 2, 3]))
94
Bram Moolenaar8dfcce32020-03-18 19:32:26 +010095 " Try to overwrite an user defined function with a function reference
96 call assert_fails("let Expr1 = function('min')", 'E705:')
97
Bram Moolenaar53f0c962017-10-22 14:23:59 +020098 " Regression: the first line below used to throw ?E110: Missing ')'?
99 " Second is here just to prove that this line is correct when not skipping
100 " rhs of &&.
101 call assert_equal(0, (0 && (function('tr'))(1, 2, 3)))
102 call assert_equal(1, (1 && (function('tr'))(1, 2, 3)))
103
104 delfunc Table
105 delfunc Compute
106 delfunc Expr1
107 delfunc Expr2
108 delfunc ListItem
109 delfunc ListReset
110 unlet g:retval g:counter
111 enew!
112endfunc
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200113
114func Log(val, base = 10)
115 return log(a:val) / log(a:base)
116endfunc
117
118func Args(mandatory, optional = v:null, ...)
119 return deepcopy(a:)
120endfunc
121
122func Args2(a = 1, b = 2, c = 3)
123 return deepcopy(a:)
124endfunc
125
126func MakeBadFunc()
127 func s:fcn(a, b=1, c)
128 endfunc
129endfunc
130
131func Test_default_arg()
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100132 call assert_equal(1.0, Log(10))
133 call assert_equal(log(10), Log(10, exp(1)))
134 call assert_fails("call Log(1,2,3)", 'E118:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200135
136 let res = Args(1)
137 call assert_equal(res.mandatory, 1)
138 call assert_equal(res.optional, v:null)
139 call assert_equal(res['0'], 0)
140
141 let res = Args(1,2)
142 call assert_equal(res.mandatory, 1)
143 call assert_equal(res.optional, 2)
144 call assert_equal(res['0'], 0)
145
146 let res = Args(1,2,3)
147 call assert_equal(res.mandatory, 1)
148 call assert_equal(res.optional, 2)
149 call assert_equal(res['0'], 1)
150
Bram Moolenaare2e40752020-09-04 21:18:46 +0200151 call assert_fails("call MakeBadFunc()", 'E989:')
Bram Moolenaare9b8b782021-04-06 20:18:29 +0200152 call assert_fails("fu F(a=1 ,) | endf", 'E1068:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200153
154 let d = Args2(7, v:none, 9)
155 call assert_equal([7, 2, 9], [d.a, d.b, d.c])
156
157 call assert_equal("\n"
158 \ .. " function Args2(a = 1, b = 2, c = 3)\n"
159 \ .. "1 return deepcopy(a:)\n"
160 \ .. " endfunction",
161 \ execute('func Args2'))
Yegappan Lakshmanan34fcb692021-05-25 20:14:00 +0200162
163 " Error in default argument expression
164 let l =<< trim END
165 func F1(x = y)
166 return a:x * 2
167 endfunc
168 echo F1()
169 END
170 let @a = l->join("\n")
171 call assert_fails("exe @a", 'E121:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200172endfunc
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +0200173
174func s:addFoo(lead)
175 return a:lead .. 'foo'
176endfunc
177
178func Test_user_method()
179 eval 'bar'->s:addFoo()->assert_equal('barfoo')
180endfunc
Bram Moolenaare51bb172020-02-16 19:42:23 +0100181
Bram Moolenaar34820942022-12-19 20:28:38 +0000182func Test_method_with_linebreaks()
183 let lines =<< trim END
184 vim9script
185
186 export def Scan(ll: list<number>): func(func(number))
187 return (Emit: func(number)) => {
188 for v in ll
189 Emit(v)
190 endfor
191 }
192 enddef
193
194 export def Build(Cont: func(func(number))): list<number>
195 var result: list<number> = []
196 Cont((v) => {
197 add(result, v)
198 })
199 return result
200 enddef
201
202 export def Noop(Cont: func(func(number))): func(func(number))
203 return (Emit: func(number)) => {
204 Cont(Emit)
205 }
206 enddef
207 END
208 call writefile(lines, 'Xlib.vim', 'D')
209
210 let lines =<< trim END
211 vim9script
212
213 import "./Xlib.vim" as lib
214
215 const x = [1, 2, 3]
216
217 var result = lib.Scan(x)->lib.Noop()->lib.Build()
218 assert_equal([1, 2, 3], result)
219
220 result = lib.Scan(x)->lib.Noop()
221 ->lib.Build()
222 assert_equal([1, 2, 3], result)
223
224 result = lib.Scan(x)
225 ->lib.Noop()->lib.Build()
226 assert_equal([1, 2, 3], result)
227
228 result = lib.Scan(x)
229 ->lib.Noop()
230 ->lib.Build()
231 assert_equal([1, 2, 3], result)
232 END
233 call v9.CheckScriptSuccess(lines)
234endfunc
235
Bram Moolenaare51bb172020-02-16 19:42:23 +0100236func Test_failed_call_in_try()
237 try | call UnknownFunc() | catch | endtry
238endfunc
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200239
240" Test for listing user-defined functions
241func Test_function_list()
242 call assert_fails("function Xabc", 'E123:')
243endfunc
244
Bram Moolenaar476a6132020-04-08 19:48:56 +0200245" Test for <sfile>, <slnum> in a function
246func Test_sfile_in_function()
247 func Xfunc()
248 call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
249 call assert_equal('2', expand('<slnum>'))
250 endfunc
251 call Xfunc()
252 delfunc Xfunc
253endfunc
254
255" Test trailing text after :endfunction {{{1
256func Test_endfunction_trailing()
257 call assert_false(exists('*Xtest'))
258
259 exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
260 call assert_true(exists('*Xtest'))
261 call assert_equal('yes', done)
262 delfunc Xtest
263 unlet done
264
265 exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
266 call assert_true(exists('*Xtest'))
267 call assert_equal('yes', done)
268 delfunc Xtest
269 unlet done
270
271 " trailing line break
272 exe "func Xtest()\necho 'hello'\nendfunc\n"
273 call assert_true(exists('*Xtest'))
274 delfunc Xtest
275
276 set verbose=1
277 exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
278 call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
279 call assert_true(exists('*Xtest'))
280 delfunc Xtest
281
282 exe "func Xtest()\necho 'hello'\nendfunc garbage"
283 call assert_match('W22:', split(execute('1messages'), "\n")[0])
284 call assert_true(exists('*Xtest'))
285 delfunc Xtest
286 set verbose=0
287
Bram Moolenaara0d072e2020-07-01 20:19:37 +0200288 func Xtest(a1, a2)
289 echo a:a1 .. a:a2
290 endfunc
291 set verbose=15
292 redir @a
293 call Xtest(123, repeat('x', 100))
294 redir END
295 call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a'))
296 delfunc Xtest
297 set verbose=0
298
Bram Moolenaar476a6132020-04-08 19:48:56 +0200299 function Foo()
300 echo 'hello'
301 endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
302 delfunc Foo
303endfunc
304
305func Test_delfunction_force()
306 delfunc! Xtest
307 delfunc! Xtest
308 func Xtest()
309 echo 'nothing'
310 endfunc
311 delfunc! Xtest
312 delfunc! Xtest
313
314 " Try deleting the current function
315 call assert_fails('delfunc Test_delfunction_force', 'E131:')
316endfunc
317
318func Test_function_defined_line()
319 CheckNotGui
320
321 let lines =<< trim [CODE]
322 " F1
323 func F1()
324 " F2
325 func F2()
326 "
327 "
328 "
329 return
330 endfunc
331 " F3
332 execute "func F3()\n\n\n\nreturn\nendfunc"
333 " F4
334 execute "func F4()\n
335 \\n
336 \\n
337 \\n
338 \return\n
339 \endfunc"
340 endfunc
341 " F5
342 execute "func F5()\n\n\n\nreturn\nendfunc"
343 " F6
344 execute "func F6()\n
345 \\n
346 \\n
347 \\n
348 \return\n
349 \endfunc"
350 call F1()
351 verbose func F1
352 verbose func F2
353 verbose func F3
354 verbose func F4
355 verbose func F5
356 verbose func F6
357 qall!
358 [CODE]
359
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100360 call writefile(lines, 'Xtest.vim', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200361 let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
362 call assert_equal(0, v:shell_error)
363
364 let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
365 call assert_match(' line 2$', m)
366
367 let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
368 call assert_match(' line 4$', m)
369
370 let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
371 call assert_match(' line 11$', m)
372
373 let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
374 call assert_match(' line 13$', m)
375
376 let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
377 call assert_match(' line 21$', m)
378
379 let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
380 call assert_match(' line 23$', m)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200381endfunc
382
383" Test for defining a function reference in the global scope
384func Test_add_funcref_to_global_scope()
385 let x = g:
386 let caught_E862 = 0
387 try
388 func x.Xfunc()
389 return 1
390 endfunc
391 catch /E862:/
392 let caught_E862 = 1
393 endtry
394 call assert_equal(1, caught_E862)
395endfunc
396
397func Test_funccall_garbage_collect()
398 func Func(x, ...)
399 call add(a:x, a:000)
400 endfunc
401 call Func([], [])
402 " Must not crash cause by invalid freeing
403 call test_garbagecollect_now()
404 call assert_true(v:true)
405 delfunc Func
406endfunc
407
408" Test for script-local function
409func <SID>DoLast()
410 call append(line('$'), "last line")
411endfunc
412
413func s:DoNothing()
414 call append(line('$'), "nothing line")
415endfunc
416
417func Test_script_local_func()
418 set nocp nomore viminfo+=nviminfo
419 new
420 nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
421
422 normal _x
423 call assert_equal('nothing line', getline(2))
424 call assert_equal('last line', getline(3))
425 close!
426
427 " Try to call a script local function in global scope
428 let lines =<< trim [CODE]
429 :call assert_fails('call s:Xfunc()', 'E81:')
Yegappan Lakshmanan6289f912025-01-14 17:13:36 +0100430 :call assert_fails('let x = call("<SID>Xfunc", [])', ['E81:', 'E117:'])
Bram Moolenaar476a6132020-04-08 19:48:56 +0200431 :call writefile(v:errors, 'Xresult')
432 :qall
433
434 [CODE]
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100435 call writefile(lines, 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200436 if RunVim([], [], '-s Xscript')
437 call assert_equal([], readfile('Xresult'))
438 endif
439 call delete('Xresult')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200440endfunc
441
442" Test for errors in defining new functions
443func Test_func_def_error()
444 call assert_fails('func Xfunc abc ()', 'E124:')
445 call assert_fails('func Xfunc(', 'E125:')
446 call assert_fails('func xfunc()', 'E128:')
447
448 " Try to redefine a function that is in use
449 let caught_E127 = 0
450 try
451 func! Test_func_def_error()
452 endfunc
453 catch /E127:/
454 let caught_E127 = 1
455 endtry
456 call assert_equal(1, caught_E127)
457
458 " Try to define a function in a dict twice
459 let d = {}
460 let lines =<< trim END
461 func d.F1()
462 return 1
463 endfunc
464 END
465 let l = join(lines, "\n") . "\n"
466 exe l
467 call assert_fails('exe l', 'E717:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200468 call assert_fails('call feedkeys(":func d.F1()\<CR>", "xt")', 'E717:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200469
470 " Define an autoload function with an incorrect file name
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100471 call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200472 call assert_fails('source Xscript', 'E746:')
Bram Moolenaar531be472020-09-23 22:38:05 +0200473
474 " Try to list functions using an invalid search pattern
475 call assert_fails('function /\%(/', 'E53:')
zeertzjq04d2a3f2025-02-02 19:03:17 +0100476
477 " Use a script-local function to cover uf_name_exp.
478 func s:TestRedefine(arg1 = 1, arg2 = 10)
479 let caught_E122 = 0
480 try
481 func s:TestRedefine(arg1 = 1, arg2 = 10)
482 endfunc
483 catch /E122:/
484 let caught_E122 = 1
485 endtry
486 call assert_equal(1, caught_E122)
487
488 let caught_E127 = 0
489 try
490 func! s:TestRedefine(arg1 = 1, arg2 = 10)
491 endfunc
492 catch /E127:/
493 let caught_E127 = 1
494 endtry
495 call assert_equal(1, caught_E127)
496
497 " The failures above shouldn't cause heap-use-after-free here.
498 return [a:arg1 + a:arg2, expand('<stack>')]
499 endfunc
500
501 let stacks = []
502 " Call the function twice.
503 " Failing to redefine a function shouldn't clear its argument list.
504 for i in range(2)
505 let [val, stack] = s:TestRedefine(1000)
506 call assert_equal(1010, val)
507 call assert_match(expand('<SID>') .. 'TestRedefine\[20\]$', stack)
508 call add(stacks, stack)
509 endfor
510 call assert_equal(stacks[0], stacks[1])
511
512 delfunc s:TestRedefine
Bram Moolenaar476a6132020-04-08 19:48:56 +0200513endfunc
514
515" Test for deleting a function
516func Test_del_func()
Bram Moolenaarc553a212021-12-26 20:20:34 +0000517 call assert_fails('delfunction Xabc', 'E117:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200518 let d = {'a' : 10}
519 call assert_fails('delfunc d.a', 'E718:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200520 func d.fn()
521 return 1
522 endfunc
Bram Moolenaarddfc0512021-09-06 20:56:56 +0200523
524 " cannot delete the dict function by number
525 let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '')
526 call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:')
527
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200528 delfunc d.fn
529 call assert_equal({'a' : 10}, d)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200530endfunc
531
532" Test for calling return outside of a function
533func Test_return_outside_func()
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100534 call writefile(['return 10'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200535 call assert_fails('source Xscript', 'E133:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200536endfunc
537
538" Test for errors in calling a function
539func Test_func_arg_error()
540 " Too many arguments
541 call assert_fails("call call('min', range(1,20))", 'E118:')
542 call assert_fails("call call('min', range(1,21))", 'E699:')
543 call assert_fails('echo min(0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,0,1)',
544 \ 'E740:')
545
546 " Missing dict argument
547 func Xfunc() dict
548 return 1
549 endfunc
550 call assert_fails('call Xfunc()', 'E725:')
551 delfunc Xfunc
552endfunc
553
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100554func Test_func_dict()
555 let mydict = {'a': 'b'}
556 function mydict.somefunc() dict
557 return len(self)
558 endfunc
559
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200560 call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict))
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100561 call assert_equal(2, mydict.somefunc())
562 call assert_match("^\n function \\d\\\+() dict"
563 \ .. "\n1 return len(self)"
564 \ .. "\n endfunction$", execute('func mydict.somefunc'))
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200565 call assert_fails('call mydict.nonexist()', 'E716:')
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100566endfunc
567
568func Test_func_range()
569 new
570 call setline(1, range(1, 8))
571 func FuncRange() range
572 echo a:firstline
573 echo a:lastline
574 endfunc
575 3
576 call assert_equal("\n3\n3", execute('call FuncRange()'))
577 call assert_equal("\n4\n6", execute('4,6 call FuncRange()'))
578 call assert_equal("\n function FuncRange() range"
579 \ .. "\n1 echo a:firstline"
580 \ .. "\n2 echo a:lastline"
581 \ .. "\n endfunction",
582 \ execute('function FuncRange'))
583
584 bwipe!
585endfunc
586
Yegappan Lakshmanan7c7e19c2022-04-09 11:09:07 +0100587" Test for memory allocation failure when defining a new function
588func Test_funcdef_alloc_failure()
589 new
590 let lines =<< trim END
591 func Xtestfunc()
592 return 321
593 endfunc
594 END
595 call setline(1, lines)
596 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
597 call assert_fails('source', 'E342:')
598 call assert_false(exists('*Xtestfunc'))
599 call assert_fails('delfunc Xtestfunc', 'E117:')
600 %d _
601 let lines =<< trim END
602 def g:Xvim9func(): number
603 return 456
604 enddef
605 END
606 call setline(1, lines)
607 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
608 call assert_fails('source', 'E342:')
609 call assert_false(exists('*Xvim9func'))
610 "call test_alloc_fail(GetAllocId('get_func'), 0, 0)
611 "call assert_fails('source', 'E342:')
612 "call assert_false(exists('*Xtestfunc'))
613 "call assert_fails('delfunc Xtestfunc', 'E117:')
614 bw!
615endfunc
616
Bram Moolenaar86d87252022-09-05 21:21:25 +0100617func AddDefer(arg1, ...)
618 call extend(g:deferred, [a:arg1])
619 if a:0 == 1
620 call extend(g:deferred, [a:1])
621 endif
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100622endfunc
623
624func WithDeferTwo()
625 call extend(g:deferred, ['in Two'])
626 for nr in range(3)
627 defer AddDefer('Two' .. nr)
628 endfor
629 call extend(g:deferred, ['end Two'])
630endfunc
631
632func WithDeferOne()
633 call extend(g:deferred, ['in One'])
634 call writefile(['text'], 'Xfuncdefer')
635 defer delete('Xfuncdefer')
636 defer AddDefer('One')
637 call WithDeferTwo()
638 call extend(g:deferred, ['end One'])
639endfunc
640
Bram Moolenaar86d87252022-09-05 21:21:25 +0100641func WithPartialDefer()
642 call extend(g:deferred, ['in Partial'])
643 let Part = funcref('AddDefer', ['arg1'])
644 defer Part("arg2")
645 call extend(g:deferred, ['end Partial'])
646endfunc
647
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100648func Test_defer()
649 let g:deferred = []
650 call WithDeferOne()
651
652 call assert_equal(['in One', 'in Two', 'end Two', 'Two2', 'Two1', 'Two0', 'end One', 'One'], g:deferred)
653 unlet g:deferred
654
655 call assert_equal('', glob('Xfuncdefer'))
Bram Moolenaar86d87252022-09-05 21:21:25 +0100656
657 call assert_fails('defer delete("Xfuncdefer")->Another()', 'E488:')
658 call assert_fails('defer delete("Xfuncdefer").member', 'E488:')
659
660 let g:deferred = []
661 call WithPartialDefer()
662 call assert_equal(['in Partial', 'end Partial', 'arg1', 'arg2'], g:deferred)
663 unlet g:deferred
664
665 let Part = funcref('AddDefer', ['arg1'], {})
666 call assert_fails('defer Part("arg2")', 'E1300:')
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100667endfunc
668
Bram Moolenaar58779852022-09-06 18:31:14 +0100669func DeferLevelTwo()
670 call writefile(['text'], 'XDeleteTwo', 'D')
671 throw 'someerror'
672endfunc
673
674def DeferLevelOne()
675 call writefile(['text'], 'XDeleteOne', 'D')
676 call g:DeferLevelTwo()
677enddef
678
679func Test_defer_throw()
680 let caught = 'no'
681 try
682 call DeferLevelOne()
683 catch /someerror/
684 let caught = 'yes'
685 endtry
686 call assert_equal('yes', caught)
687 call assert_false(filereadable('XDeleteOne'))
688 call assert_false(filereadable('XDeleteTwo'))
689endfunc
690
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100691func Test_defer_quitall_func()
Bram Moolenaar58779852022-09-06 18:31:14 +0100692 let lines =<< trim END
Bram Moolenaar58779852022-09-06 18:31:14 +0100693 func DeferLevelTwo()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100694 call writefile(['text'], 'XQuitallFuncTwo', 'D')
695 call writefile(['quit'], 'XQuitallFuncThree', 'a')
Bram Moolenaar58779852022-09-06 18:31:14 +0100696 qa!
697 endfunc
698
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100699 func DeferLevelOne()
700 call writefile(['text'], 'XQuitalFunclOne', 'D')
701 defer DeferLevelTwo()
702 endfunc
703
704 call DeferLevelOne()
705 END
706 call writefile(lines, 'XdeferQuitallFunc', 'D')
707 call system(GetVimCommand() .. ' -X -S XdeferQuitallFunc')
708 call assert_equal(0, v:shell_error)
709 call assert_false(filereadable('XQuitallFuncOne'))
710 call assert_false(filereadable('XQuitallFuncTwo'))
711 call assert_equal(['quit'], readfile('XQuitallFuncThree'))
712
713 call delete('XQuitallFuncThree')
714endfunc
715
716func Test_defer_quitall_def()
717 let lines =<< trim END
718 vim9script
719 def DeferLevelTwo()
720 call writefile(['text'], 'XQuitallDefTwo', 'D')
721 call writefile(['quit'], 'XQuitallDefThree', 'a')
722 qa!
723 enddef
724
Bram Moolenaar58779852022-09-06 18:31:14 +0100725 def DeferLevelOne()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100726 call writefile(['text'], 'XQuitallDefOne', 'D')
727 defer DeferLevelTwo()
Bram Moolenaar58779852022-09-06 18:31:14 +0100728 enddef
729
730 DeferLevelOne()
731 END
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100732 call writefile(lines, 'XdeferQuitallDef', 'D')
733 call system(GetVimCommand() .. ' -X -S XdeferQuitallDef')
Bram Moolenaar58779852022-09-06 18:31:14 +0100734 call assert_equal(0, v:shell_error)
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100735 call assert_false(filereadable('XQuitallDefOne'))
736 call assert_false(filereadable('XQuitallDefTwo'))
737 call assert_equal(['quit'], readfile('XQuitallDefThree'))
Bram Moolenaar42994bf2023-04-17 19:23:45 +0100738
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100739 call delete('XQuitallDefThree')
Bram Moolenaar58779852022-09-06 18:31:14 +0100740endfunc
741
zeertzjq960cf912023-04-18 21:52:54 +0100742func Test_defer_quitall_autocmd()
743 let lines =<< trim END
zeertzjq1be4b812023-04-19 14:21:24 +0100744 func DeferLevelFive()
745 defer writefile(['5'], 'XQuitallAutocmd', 'a')
746 qa!
zeertzjq960cf912023-04-18 21:52:54 +0100747 endfunc
748
zeertzjq1be4b812023-04-19 14:21:24 +0100749 autocmd User DeferAutocmdFive call DeferLevelFive()
zeertzjq960cf912023-04-18 21:52:54 +0100750
zeertzjq1be4b812023-04-19 14:21:24 +0100751 def DeferLevelFour()
752 defer writefile(['4'], 'XQuitallAutocmd', 'a')
753 doautocmd User DeferAutocmdFive
zeertzjq960cf912023-04-18 21:52:54 +0100754 enddef
755
zeertzjq1be4b812023-04-19 14:21:24 +0100756 func DeferLevelThree()
757 defer writefile(['3'], 'XQuitallAutocmd', 'a')
758 call DeferLevelFour()
759 endfunc
760
761 autocmd User DeferAutocmdThree ++nested call DeferLevelThree()
762
763 def DeferLevelTwo()
764 defer writefile(['2'], 'XQuitallAutocmd', 'a')
765 doautocmd User DeferAutocmdThree
766 enddef
767
768 func DeferLevelOne()
769 defer writefile(['1'], 'XQuitallAutocmd', 'a')
770 call DeferLevelTwo()
771 endfunc
772
zeertzjq960cf912023-04-18 21:52:54 +0100773 autocmd User DeferAutocmdOne ++nested call DeferLevelOne()
774
775 doautocmd User DeferAutocmdOne
776 END
777 call writefile(lines, 'XdeferQuitallAutocmd', 'D')
zeertzjq1be4b812023-04-19 14:21:24 +0100778 call system(GetVimCommand() .. ' -X -S XdeferQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100779 call assert_equal(0, v:shell_error)
zeertzjq1be4b812023-04-19 14:21:24 +0100780 call assert_equal(['5', '4', '3', '2', '1'], readfile('XQuitallAutocmd'))
781
782 call delete('XQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100783endfunc
784
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100785func Test_defer_quitall_in_expr_func()
786 let lines =<< trim END
787 def DefIndex(idx: number, val: string): bool
788 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
789 if val == 'b'
790 qa!
791 endif
792 return val == 'c'
793 enddef
794
795 def Test_defer_in_funcref()
796 assert_equal(2, indexof(['a', 'b', 'c'], funcref('g:DefIndex')))
797 enddef
798 call Test_defer_in_funcref()
799 END
800 call writefile(lines, 'XdeferQuitallExpr', 'D')
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100801 call system(GetVimCommand() .. ' -X -S XdeferQuitallExpr')
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100802 call assert_equal(0, v:shell_error)
803 call assert_false(filereadable('Xentry0'))
804 call assert_false(filereadable('Xentry1'))
805 call assert_false(filereadable('Xentry2'))
806endfunc
807
Bram Moolenaar98aff652022-09-06 21:02:35 +0100808func FuncIndex(idx, val)
809 call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D')
810 return a:val == 'c'
811endfunc
812
813def DefIndex(idx: number, val: string): bool
814 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
815 return val == 'c'
816enddef
817
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100818def DefIndexXtra(xtra: string, idx: number, val: string): bool
819 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
820 return val == 'c'
821enddef
822
Bram Moolenaar98aff652022-09-06 21:02:35 +0100823def Test_defer_in_funcref()
824 assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex')))
825 assert_false(filereadable('Xentry0'))
826 assert_false(filereadable('Xentry1'))
827 assert_false(filereadable('Xentry2'))
828
829 assert_equal(2, indexof(['a', 'b', 'c'], g:DefIndex))
830 assert_false(filereadable('Xentry0'))
831 assert_false(filereadable('Xentry1'))
832 assert_false(filereadable('Xentry2'))
833
834 assert_equal(2, indexof(['a', 'b', 'c'], function('g:DefIndex')))
835 assert_false(filereadable('Xentry0'))
836 assert_false(filereadable('Xentry1'))
837 assert_false(filereadable('Xentry2'))
838
839 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndex)))
840 assert_false(filereadable('Xentry0'))
841 assert_false(filereadable('Xentry1'))
842 assert_false(filereadable('Xentry2'))
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100843
844 assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra'])))
845 assert_false(filereadable('Xentry0'))
846 assert_false(filereadable('Xentry1'))
847 assert_false(filereadable('Xentry2'))
848
849 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra'])))
850 assert_false(filereadable('Xentry0'))
851 assert_false(filereadable('Xentry1'))
852 assert_false(filereadable('Xentry2'))
Bram Moolenaar98aff652022-09-06 21:02:35 +0100853enddef
854
Bram Moolenaar16900322022-09-08 19:51:45 +0100855func Test_defer_wrong_arguments()
856 call assert_fails('defer delete()', 'E119:')
857 call assert_fails('defer FuncIndex(1)', 'E119:')
858 call assert_fails('defer delete(1, 2, 3)', 'E118:')
859 call assert_fails('defer FuncIndex(1, 2, 3)', 'E118:')
860
861 let lines =<< trim END
862 def DeferFunc0()
863 defer delete()
864 enddef
865 defcompile
866 END
867 call v9.CheckScriptFailure(lines, 'E119:')
868 let lines =<< trim END
869 def DeferFunc3()
870 defer delete(1, 2, 3)
871 enddef
872 defcompile
873 END
874 call v9.CheckScriptFailure(lines, 'E118:')
875 let lines =<< trim END
876 def DeferFunc2()
877 defer delete(1, 2)
878 enddef
879 defcompile
880 END
881 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
882
883 def g:FuncOneArg(arg: string)
884 echo arg
885 enddef
886
887 let lines =<< trim END
888 def DeferUserFunc0()
889 defer g:FuncOneArg()
890 enddef
891 defcompile
892 END
893 call v9.CheckScriptFailure(lines, 'E119:')
894 let lines =<< trim END
895 def DeferUserFunc2()
896 defer g:FuncOneArg(1, 2)
897 enddef
898 defcompile
899 END
900 call v9.CheckScriptFailure(lines, 'E118:')
901 let lines =<< trim END
902 def DeferUserFunc1()
903 defer g:FuncOneArg(1)
904 enddef
905 defcompile
906 END
907 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
908endfunc
909
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200910" Test for calling a deferred function after an exception
911func Test_defer_after_exception()
912 let g:callTrace = []
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200913 func Bar()
914 let g:callTrace += [1]
915 throw 'InnerException'
916 endfunc
917
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200918 func Defer()
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200919 let g:callTrace += [2]
920 let g:callTrace += [3]
921 try
922 call Bar()
923 catch /InnerException/
924 let g:callTrace += [4]
925 endtry
926 let g:callTrace += [5]
927 let g:callTrace += [6]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200928 endfunc
929
930 func Foo()
931 defer Defer()
932 throw "TestException"
933 endfunc
934
935 try
936 call Foo()
937 catch /TestException/
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200938 let g:callTrace += [7]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200939 endtry
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200940 call assert_equal([2, 3, 1, 4, 5, 6, 7], g:callTrace)
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200941
942 delfunc Defer
943 delfunc Foo
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200944 delfunc Bar
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200945 unlet g:callTrace
946endfunc
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100947
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200948" Test for multiple deferred function which throw exceptions.
949" Exceptions thrown by deferred functions should result in error messages but
950" not propagated into the calling functions.
951func Test_multidefer_with_exception()
952 let g:callTrace = []
953 func Except()
954 let g:callTrace += [1]
955 throw 'InnerException'
956 let g:callTrace += [2]
957 endfunc
958
959 func FirstDefer()
960 let g:callTrace += [3]
961 let g:callTrace += [4]
962 endfunc
963
964 func SecondDeferWithExcept()
965 let g:callTrace += [5]
966 call Except()
967 let g:callTrace += [6]
968 endfunc
969
970 func ThirdDefer()
971 let g:callTrace += [7]
972 let g:callTrace += [8]
973 endfunc
974
975 func Foo()
976 let g:callTrace += [9]
977 defer FirstDefer()
978 defer SecondDeferWithExcept()
979 defer ThirdDefer()
980 let g:callTrace += [10]
981 endfunc
982
983 let v:errmsg = ''
984 try
985 let g:callTrace += [11]
986 call Foo()
987 let g:callTrace += [12]
988 catch /TestException/
989 let g:callTrace += [13]
990 catch
991 let g:callTrace += [14]
992 finally
993 let g:callTrace += [15]
994 endtry
995 let g:callTrace += [16]
996
997 call assert_equal('E605: Exception not caught: InnerException', v:errmsg)
998 call assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], g:callTrace)
999
1000 unlet g:callTrace
1001 delfunc Except
1002 delfunc FirstDefer
1003 delfunc SecondDeferWithExcept
1004 delfunc ThirdDefer
1005 delfunc Foo
1006endfunc
1007
zeertzjq6a04bf52024-03-16 09:39:06 +01001008func Test_func_curly_brace_invalid_name()
1009 func Fail()
1010 func Foo{'()'}bar()
1011 endfunc
1012 endfunc
1013
1014 call assert_fails('call Fail()', 'E475: Invalid argument: Foo()bar')
1015
1016 silent! call Fail()
1017 call assert_equal([], getcompletion('Foo', 'function'))
1018
1019 set formatexpr=Fail()
1020 normal! gqq
1021 call assert_equal([], getcompletion('Foo', 'function'))
1022
1023 set formatexpr&
1024 delfunc Fail
1025endfunc
1026
zeertzjq21012302025-02-02 08:55:57 +01001027func Test_func_return_in_try_verbose()
1028 func TryReturnList()
1029 try
1030 return [1, 2, 3]
1031 endtry
1032 endfunc
1033 func TryReturnNumber()
1034 try
1035 return 123
1036 endtry
1037 endfunc
1038 func TryReturnOverlongString()
1039 try
1040 return repeat('a', 9999)
1041 endtry
1042 endfunc
1043
1044 " This should not cause heap-use-after-free
1045 call assert_match('\n:return \[1, 2, 3\] made pending\n',
1046 \ execute('14verbose call TryReturnList()'))
1047 " This should not cause stack-use-after-scope
1048 call assert_match('\n:return 123 made pending\n',
1049 \ execute('14verbose call TryReturnNumber()'))
1050 " An overlong string is truncated
1051 call assert_match('\n:return a\{100,}\.\.\.',
1052 \ execute('14verbose call TryReturnOverlongString()'))
1053
1054 delfunc TryReturnList
1055 delfunc TryReturnNumber
1056 delfunc TryReturnOverlongString
1057endfunc
1058
Bram Moolenaaree4e0c12020-04-06 21:35:05 +02001059" vim: shiftwidth=2 sts=2 expandtab