blob: d12ad8e402ed01116404bd127b49864f001c8613 [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
Christian Brabandteb380b92025-07-07 20:53:55 +02006import './util/vim9.vim' as v9
Bram Moolenaar476a6132020-04-08 19:48:56 +02007
Bram Moolenaar53f0c962017-10-22 14:23:59 +02008func Table(title, ...)
9 let ret = a:title
10 let idx = 1
11 while idx <= a:0
12 exe "let ret = ret . a:" . idx
13 let idx = idx + 1
14 endwhile
15 return ret
16endfunc
17
18func Compute(n1, n2, divname)
19 if a:n2 == 0
20 return "fail"
21 endif
22 exe "let g:" . a:divname . " = ". a:n1 / a:n2
23 return "ok"
24endfunc
25
26func Expr1()
27 silent! normal! v
28 return "111"
29endfunc
30
31func Expr2()
32 call search('XX', 'b')
33 return "222"
34endfunc
35
36func ListItem()
37 let g:counter += 1
38 return g:counter . '. '
39endfunc
40
41func ListReset()
42 let g:counter = 0
43 return ''
44endfunc
45
46func FuncWithRef(a)
47 unlet g:FuncRef
48 return a:a
49endfunc
50
51func Test_user_func()
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020052 let g:FuncRef = function("FuncWithRef")
Bram Moolenaar53f0c962017-10-22 14:23:59 +020053 let g:counter = 0
54 inoremap <expr> ( ListItem()
55 inoremap <expr> [ ListReset()
56 imap <expr> + Expr1()
57 imap <expr> * Expr2()
58 let g:retval = "nop"
59
60 call assert_equal('xxx4asdf', Table("xxx", 4, "asdf"))
61 call assert_equal('fail', Compute(45, 0, "retval"))
62 call assert_equal('nop', g:retval)
63 call assert_equal('ok', Compute(45, 5, "retval"))
64 call assert_equal(9, g:retval)
65 call assert_equal(333, g:FuncRef(333))
66
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020067 let g:retval = "nop"
68 call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
69 call assert_equal('fail', 45->Compute(0, "retval"))
70 call assert_equal('nop', g:retval)
71 call assert_equal('ok', 45->Compute(5, "retval"))
72 call assert_equal(9, g:retval)
73 " call assert_equal(333, 333->g:FuncRef())
74
Bram Moolenaar53f0c962017-10-22 14:23:59 +020075 enew
76
77 normal oXX+-XX
78 call assert_equal('XX111-XX', getline('.'))
79 normal o---*---
80 call assert_equal('---222---', getline('.'))
81 normal o(one
82 call assert_equal('1. one', getline('.'))
83 normal o(two
84 call assert_equal('2. two', getline('.'))
85 normal o[(one again
86 call assert_equal('1. one again', getline('.'))
87
Bram Moolenaar476a6132020-04-08 19:48:56 +020088 " Try to overwrite a function in the global (g:) scope
Bram Moolenaar53f0c962017-10-22 14:23:59 +020089 call assert_equal(3, max([1, 2, 3]))
Bram Moolenaare2e40752020-09-04 21:18:46 +020090 call assert_fails("call extend(g:, {'max': function('min')})", 'E704:')
Bram Moolenaar53f0c962017-10-22 14:23:59 +020091 call assert_equal(3, max([1, 2, 3]))
92
Bram Moolenaar8dfcce32020-03-18 19:32:26 +010093 " Try to overwrite an user defined function with a function reference
94 call assert_fails("let Expr1 = function('min')", 'E705:')
95
Bram Moolenaar53f0c962017-10-22 14:23:59 +020096 " Regression: the first line below used to throw ?E110: Missing ')'?
97 " Second is here just to prove that this line is correct when not skipping
98 " rhs of &&.
99 call assert_equal(0, (0 && (function('tr'))(1, 2, 3)))
100 call assert_equal(1, (1 && (function('tr'))(1, 2, 3)))
101
102 delfunc Table
103 delfunc Compute
104 delfunc Expr1
105 delfunc Expr2
106 delfunc ListItem
107 delfunc ListReset
108 unlet g:retval g:counter
109 enew!
110endfunc
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200111
112func Log(val, base = 10)
113 return log(a:val) / log(a:base)
114endfunc
115
116func Args(mandatory, optional = v:null, ...)
117 return deepcopy(a:)
118endfunc
119
120func Args2(a = 1, b = 2, c = 3)
121 return deepcopy(a:)
122endfunc
123
124func MakeBadFunc()
125 func s:fcn(a, b=1, c)
126 endfunc
127endfunc
128
129func Test_default_arg()
Bram Moolenaar73e28dc2022-09-17 21:08:33 +0100130 call assert_equal(1.0, Log(10))
131 call assert_equal(log(10), Log(10, exp(1)))
132 call assert_fails("call Log(1,2,3)", 'E118:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200133
134 let res = Args(1)
135 call assert_equal(res.mandatory, 1)
136 call assert_equal(res.optional, v:null)
137 call assert_equal(res['0'], 0)
138
139 let res = Args(1,2)
140 call assert_equal(res.mandatory, 1)
141 call assert_equal(res.optional, 2)
142 call assert_equal(res['0'], 0)
143
144 let res = Args(1,2,3)
145 call assert_equal(res.mandatory, 1)
146 call assert_equal(res.optional, 2)
147 call assert_equal(res['0'], 1)
148
Bram Moolenaare2e40752020-09-04 21:18:46 +0200149 call assert_fails("call MakeBadFunc()", 'E989:')
Bram Moolenaare9b8b782021-04-06 20:18:29 +0200150 call assert_fails("fu F(a=1 ,) | endf", 'E1068:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200151
152 let d = Args2(7, v:none, 9)
153 call assert_equal([7, 2, 9], [d.a, d.b, d.c])
154
155 call assert_equal("\n"
156 \ .. " function Args2(a = 1, b = 2, c = 3)\n"
157 \ .. "1 return deepcopy(a:)\n"
158 \ .. " endfunction",
159 \ execute('func Args2'))
Yegappan Lakshmanan34fcb692021-05-25 20:14:00 +0200160
161 " Error in default argument expression
Shane Harper2d187892025-03-12 21:12:12 +0100162 func! s:f(x = s:undefined)
163 return a:x
164 endfunc
165 call assert_fails('echo s:f()', ['E121: Undefined variable: s:undefined',
166 \ 'E121: Undefined variable: a:x'])
167
168 func! s:f(x = s:undefined) abort
169 return a:x
170 endfunc
171 const expected_error = 'E121: Undefined variable: s:undefined'
172 " Only one error should be output; execution of the function should be aborted
173 " after the default argument expression error.
174 call assert_fails('echo s:f()', [expected_error, expected_error])
175endfunc
176
177func Test_default_argument_expression_error_while_inside_of_a_try_block()
178 func! s:f(v = s:undefined_variable)
179 let s:entered_fn_body = 1
180 return a:v
181 endfunc
182
183 unlet! s:entered_fn_body
184 try
185 call s:f()
186 throw "No exception."
187 catch
188 call assert_exception("E121: Undefined variable: s:undefined_variable")
189 endtry
190 call assert_false(exists('s:entered_fn_body'), "exists('s:entered_fn_body')")
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200191endfunc
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +0200192
193func s:addFoo(lead)
194 return a:lead .. 'foo'
195endfunc
196
197func Test_user_method()
198 eval 'bar'->s:addFoo()->assert_equal('barfoo')
199endfunc
Bram Moolenaare51bb172020-02-16 19:42:23 +0100200
Bram Moolenaar34820942022-12-19 20:28:38 +0000201func Test_method_with_linebreaks()
202 let lines =<< trim END
203 vim9script
204
205 export def Scan(ll: list<number>): func(func(number))
206 return (Emit: func(number)) => {
207 for v in ll
208 Emit(v)
209 endfor
210 }
211 enddef
212
213 export def Build(Cont: func(func(number))): list<number>
214 var result: list<number> = []
215 Cont((v) => {
216 add(result, v)
217 })
218 return result
219 enddef
220
221 export def Noop(Cont: func(func(number))): func(func(number))
222 return (Emit: func(number)) => {
223 Cont(Emit)
224 }
225 enddef
226 END
227 call writefile(lines, 'Xlib.vim', 'D')
228
229 let lines =<< trim END
230 vim9script
231
232 import "./Xlib.vim" as lib
233
234 const x = [1, 2, 3]
235
236 var result = lib.Scan(x)->lib.Noop()->lib.Build()
237 assert_equal([1, 2, 3], result)
238
239 result = lib.Scan(x)->lib.Noop()
240 ->lib.Build()
241 assert_equal([1, 2, 3], result)
242
243 result = lib.Scan(x)
244 ->lib.Noop()->lib.Build()
245 assert_equal([1, 2, 3], result)
246
247 result = lib.Scan(x)
248 ->lib.Noop()
249 ->lib.Build()
250 assert_equal([1, 2, 3], result)
251 END
252 call v9.CheckScriptSuccess(lines)
253endfunc
254
Bram Moolenaare51bb172020-02-16 19:42:23 +0100255func Test_failed_call_in_try()
256 try | call UnknownFunc() | catch | endtry
257endfunc
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200258
259" Test for listing user-defined functions
260func Test_function_list()
261 call assert_fails("function Xabc", 'E123:')
262endfunc
263
Bram Moolenaar476a6132020-04-08 19:48:56 +0200264" Test for <sfile>, <slnum> in a function
265func Test_sfile_in_function()
266 func Xfunc()
267 call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
268 call assert_equal('2', expand('<slnum>'))
269 endfunc
270 call Xfunc()
271 delfunc Xfunc
272endfunc
273
274" Test trailing text after :endfunction {{{1
275func Test_endfunction_trailing()
276 call assert_false(exists('*Xtest'))
277
278 exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
279 call assert_true(exists('*Xtest'))
280 call assert_equal('yes', done)
281 delfunc Xtest
282 unlet done
283
284 exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
285 call assert_true(exists('*Xtest'))
286 call assert_equal('yes', done)
287 delfunc Xtest
288 unlet done
289
290 " trailing line break
291 exe "func Xtest()\necho 'hello'\nendfunc\n"
292 call assert_true(exists('*Xtest'))
293 delfunc Xtest
294
295 set verbose=1
296 exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
297 call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
298 call assert_true(exists('*Xtest'))
299 delfunc Xtest
300
301 exe "func Xtest()\necho 'hello'\nendfunc garbage"
302 call assert_match('W22:', split(execute('1messages'), "\n")[0])
303 call assert_true(exists('*Xtest'))
304 delfunc Xtest
305 set verbose=0
306
Bram Moolenaara0d072e2020-07-01 20:19:37 +0200307 func Xtest(a1, a2)
308 echo a:a1 .. a:a2
309 endfunc
310 set verbose=15
311 redir @a
312 call Xtest(123, repeat('x', 100))
313 redir END
314 call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a'))
315 delfunc Xtest
316 set verbose=0
317
Bram Moolenaar476a6132020-04-08 19:48:56 +0200318 function Foo()
319 echo 'hello'
320 endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
321 delfunc Foo
322endfunc
323
324func Test_delfunction_force()
325 delfunc! Xtest
326 delfunc! Xtest
327 func Xtest()
328 echo 'nothing'
329 endfunc
330 delfunc! Xtest
331 delfunc! Xtest
332
333 " Try deleting the current function
334 call assert_fails('delfunc Test_delfunction_force', 'E131:')
335endfunc
336
337func Test_function_defined_line()
338 CheckNotGui
339
340 let lines =<< trim [CODE]
341 " F1
342 func F1()
343 " F2
344 func F2()
345 "
346 "
347 "
348 return
349 endfunc
350 " F3
351 execute "func F3()\n\n\n\nreturn\nendfunc"
352 " F4
353 execute "func F4()\n
354 \\n
355 \\n
356 \\n
357 \return\n
358 \endfunc"
359 endfunc
360 " F5
361 execute "func F5()\n\n\n\nreturn\nendfunc"
362 " F6
363 execute "func F6()\n
364 \\n
365 \\n
366 \\n
367 \return\n
368 \endfunc"
369 call F1()
370 verbose func F1
371 verbose func F2
372 verbose func F3
373 verbose func F4
374 verbose func F5
375 verbose func F6
376 qall!
377 [CODE]
378
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100379 call writefile(lines, 'Xtest.vim', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200380 let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
381 call assert_equal(0, v:shell_error)
382
383 let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
384 call assert_match(' line 2$', m)
385
386 let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
387 call assert_match(' line 4$', m)
388
389 let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
390 call assert_match(' line 11$', m)
391
392 let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
393 call assert_match(' line 13$', m)
394
395 let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
396 call assert_match(' line 21$', m)
397
398 let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
399 call assert_match(' line 23$', m)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200400endfunc
401
402" Test for defining a function reference in the global scope
403func Test_add_funcref_to_global_scope()
404 let x = g:
405 let caught_E862 = 0
406 try
407 func x.Xfunc()
408 return 1
409 endfunc
410 catch /E862:/
411 let caught_E862 = 1
412 endtry
413 call assert_equal(1, caught_E862)
414endfunc
415
416func Test_funccall_garbage_collect()
417 func Func(x, ...)
418 call add(a:x, a:000)
419 endfunc
420 call Func([], [])
421 " Must not crash cause by invalid freeing
422 call test_garbagecollect_now()
423 call assert_true(v:true)
424 delfunc Func
425endfunc
426
427" Test for script-local function
428func <SID>DoLast()
429 call append(line('$'), "last line")
430endfunc
431
432func s:DoNothing()
433 call append(line('$'), "nothing line")
434endfunc
435
436func Test_script_local_func()
437 set nocp nomore viminfo+=nviminfo
438 new
439 nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
440
441 normal _x
442 call assert_equal('nothing line', getline(2))
443 call assert_equal('last line', getline(3))
444 close!
445
446 " Try to call a script local function in global scope
447 let lines =<< trim [CODE]
448 :call assert_fails('call s:Xfunc()', 'E81:')
Yegappan Lakshmanan6289f912025-01-14 17:13:36 +0100449 :call assert_fails('let x = call("<SID>Xfunc", [])', ['E81:', 'E117:'])
Bram Moolenaar476a6132020-04-08 19:48:56 +0200450 :call writefile(v:errors, 'Xresult')
451 :qall
452
453 [CODE]
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100454 call writefile(lines, 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200455 if RunVim([], [], '-s Xscript')
456 call assert_equal([], readfile('Xresult'))
457 endif
458 call delete('Xresult')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200459endfunc
460
461" Test for errors in defining new functions
462func Test_func_def_error()
463 call assert_fails('func Xfunc abc ()', 'E124:')
464 call assert_fails('func Xfunc(', 'E125:')
465 call assert_fails('func xfunc()', 'E128:')
466
467 " Try to redefine a function that is in use
468 let caught_E127 = 0
469 try
470 func! Test_func_def_error()
471 endfunc
472 catch /E127:/
473 let caught_E127 = 1
474 endtry
475 call assert_equal(1, caught_E127)
476
477 " Try to define a function in a dict twice
478 let d = {}
479 let lines =<< trim END
480 func d.F1()
481 return 1
482 endfunc
483 END
484 let l = join(lines, "\n") . "\n"
485 exe l
486 call assert_fails('exe l', 'E717:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200487 call assert_fails('call feedkeys(":func d.F1()\<CR>", "xt")', 'E717:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200488
489 " Define an autoload function with an incorrect file name
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100490 call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200491 call assert_fails('source Xscript', 'E746:')
Bram Moolenaar531be472020-09-23 22:38:05 +0200492
493 " Try to list functions using an invalid search pattern
494 call assert_fails('function /\%(/', 'E53:')
zeertzjq04d2a3f2025-02-02 19:03:17 +0100495
496 " Use a script-local function to cover uf_name_exp.
497 func s:TestRedefine(arg1 = 1, arg2 = 10)
498 let caught_E122 = 0
499 try
500 func s:TestRedefine(arg1 = 1, arg2 = 10)
501 endfunc
502 catch /E122:/
503 let caught_E122 = 1
504 endtry
505 call assert_equal(1, caught_E122)
506
507 let caught_E127 = 0
508 try
509 func! s:TestRedefine(arg1 = 1, arg2 = 10)
510 endfunc
511 catch /E127:/
512 let caught_E127 = 1
513 endtry
514 call assert_equal(1, caught_E127)
515
516 " The failures above shouldn't cause heap-use-after-free here.
517 return [a:arg1 + a:arg2, expand('<stack>')]
518 endfunc
519
520 let stacks = []
521 " Call the function twice.
522 " Failing to redefine a function shouldn't clear its argument list.
523 for i in range(2)
524 let [val, stack] = s:TestRedefine(1000)
525 call assert_equal(1010, val)
526 call assert_match(expand('<SID>') .. 'TestRedefine\[20\]$', stack)
527 call add(stacks, stack)
528 endfor
529 call assert_equal(stacks[0], stacks[1])
530
531 delfunc s:TestRedefine
Bram Moolenaar476a6132020-04-08 19:48:56 +0200532endfunc
533
534" Test for deleting a function
535func Test_del_func()
Bram Moolenaarc553a212021-12-26 20:20:34 +0000536 call assert_fails('delfunction Xabc', 'E117:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200537 let d = {'a' : 10}
538 call assert_fails('delfunc d.a', 'E718:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200539 func d.fn()
540 return 1
541 endfunc
Bram Moolenaarddfc0512021-09-06 20:56:56 +0200542
543 " cannot delete the dict function by number
544 let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '')
545 call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:')
546
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200547 delfunc d.fn
548 call assert_equal({'a' : 10}, d)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200549endfunc
550
551" Test for calling return outside of a function
552func Test_return_outside_func()
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100553 call writefile(['return 10'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200554 call assert_fails('source Xscript', 'E133:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200555endfunc
556
557" Test for errors in calling a function
558func Test_func_arg_error()
559 " Too many arguments
560 call assert_fails("call call('min', range(1,20))", 'E118:')
561 call assert_fails("call call('min', range(1,21))", 'E699:')
562 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)',
563 \ 'E740:')
564
565 " Missing dict argument
566 func Xfunc() dict
567 return 1
568 endfunc
569 call assert_fails('call Xfunc()', 'E725:')
570 delfunc Xfunc
571endfunc
572
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100573func Test_func_dict()
574 let mydict = {'a': 'b'}
575 function mydict.somefunc() dict
576 return len(self)
577 endfunc
578
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200579 call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict))
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100580 call assert_equal(2, mydict.somefunc())
581 call assert_match("^\n function \\d\\\+() dict"
582 \ .. "\n1 return len(self)"
583 \ .. "\n endfunction$", execute('func mydict.somefunc'))
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200584 call assert_fails('call mydict.nonexist()', 'E716:')
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100585endfunc
586
587func Test_func_range()
588 new
589 call setline(1, range(1, 8))
590 func FuncRange() range
591 echo a:firstline
592 echo a:lastline
593 endfunc
594 3
595 call assert_equal("\n3\n3", execute('call FuncRange()'))
596 call assert_equal("\n4\n6", execute('4,6 call FuncRange()'))
597 call assert_equal("\n function FuncRange() range"
598 \ .. "\n1 echo a:firstline"
599 \ .. "\n2 echo a:lastline"
600 \ .. "\n endfunction",
601 \ execute('function FuncRange'))
602
603 bwipe!
604endfunc
605
Yegappan Lakshmanan7c7e19c2022-04-09 11:09:07 +0100606" Test for memory allocation failure when defining a new function
607func Test_funcdef_alloc_failure()
608 new
609 let lines =<< trim END
610 func Xtestfunc()
611 return 321
612 endfunc
613 END
614 call setline(1, lines)
615 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
616 call assert_fails('source', 'E342:')
617 call assert_false(exists('*Xtestfunc'))
618 call assert_fails('delfunc Xtestfunc', 'E117:')
619 %d _
620 let lines =<< trim END
621 def g:Xvim9func(): number
622 return 456
623 enddef
624 END
625 call setline(1, lines)
626 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
627 call assert_fails('source', 'E342:')
628 call assert_false(exists('*Xvim9func'))
629 "call test_alloc_fail(GetAllocId('get_func'), 0, 0)
630 "call assert_fails('source', 'E342:')
631 "call assert_false(exists('*Xtestfunc'))
632 "call assert_fails('delfunc Xtestfunc', 'E117:')
633 bw!
634endfunc
635
Bram Moolenaar86d87252022-09-05 21:21:25 +0100636func AddDefer(arg1, ...)
637 call extend(g:deferred, [a:arg1])
638 if a:0 == 1
639 call extend(g:deferred, [a:1])
640 endif
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100641endfunc
642
643func WithDeferTwo()
644 call extend(g:deferred, ['in Two'])
645 for nr in range(3)
646 defer AddDefer('Two' .. nr)
647 endfor
648 call extend(g:deferred, ['end Two'])
649endfunc
650
651func WithDeferOne()
652 call extend(g:deferred, ['in One'])
653 call writefile(['text'], 'Xfuncdefer')
654 defer delete('Xfuncdefer')
655 defer AddDefer('One')
656 call WithDeferTwo()
657 call extend(g:deferred, ['end One'])
658endfunc
659
Bram Moolenaar86d87252022-09-05 21:21:25 +0100660func WithPartialDefer()
661 call extend(g:deferred, ['in Partial'])
662 let Part = funcref('AddDefer', ['arg1'])
663 defer Part("arg2")
664 call extend(g:deferred, ['end Partial'])
665endfunc
666
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100667func Test_defer()
668 let g:deferred = []
669 call WithDeferOne()
670
671 call assert_equal(['in One', 'in Two', 'end Two', 'Two2', 'Two1', 'Two0', 'end One', 'One'], g:deferred)
672 unlet g:deferred
673
674 call assert_equal('', glob('Xfuncdefer'))
Bram Moolenaar86d87252022-09-05 21:21:25 +0100675
676 call assert_fails('defer delete("Xfuncdefer")->Another()', 'E488:')
677 call assert_fails('defer delete("Xfuncdefer").member', 'E488:')
678
679 let g:deferred = []
680 call WithPartialDefer()
681 call assert_equal(['in Partial', 'end Partial', 'arg1', 'arg2'], g:deferred)
682 unlet g:deferred
683
684 let Part = funcref('AddDefer', ['arg1'], {})
685 call assert_fails('defer Part("arg2")', 'E1300:')
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100686endfunc
687
Bram Moolenaar58779852022-09-06 18:31:14 +0100688func DeferLevelTwo()
689 call writefile(['text'], 'XDeleteTwo', 'D')
690 throw 'someerror'
691endfunc
692
693def DeferLevelOne()
694 call writefile(['text'], 'XDeleteOne', 'D')
695 call g:DeferLevelTwo()
696enddef
697
698func Test_defer_throw()
699 let caught = 'no'
700 try
701 call DeferLevelOne()
702 catch /someerror/
703 let caught = 'yes'
704 endtry
705 call assert_equal('yes', caught)
706 call assert_false(filereadable('XDeleteOne'))
707 call assert_false(filereadable('XDeleteTwo'))
708endfunc
709
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100710func Test_defer_quitall_func()
Bram Moolenaar58779852022-09-06 18:31:14 +0100711 let lines =<< trim END
Bram Moolenaar58779852022-09-06 18:31:14 +0100712 func DeferLevelTwo()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100713 call writefile(['text'], 'XQuitallFuncTwo', 'D')
714 call writefile(['quit'], 'XQuitallFuncThree', 'a')
Bram Moolenaar58779852022-09-06 18:31:14 +0100715 qa!
716 endfunc
717
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100718 func DeferLevelOne()
719 call writefile(['text'], 'XQuitalFunclOne', 'D')
720 defer DeferLevelTwo()
721 endfunc
722
723 call DeferLevelOne()
724 END
725 call writefile(lines, 'XdeferQuitallFunc', 'D')
726 call system(GetVimCommand() .. ' -X -S XdeferQuitallFunc')
727 call assert_equal(0, v:shell_error)
728 call assert_false(filereadable('XQuitallFuncOne'))
729 call assert_false(filereadable('XQuitallFuncTwo'))
730 call assert_equal(['quit'], readfile('XQuitallFuncThree'))
731
732 call delete('XQuitallFuncThree')
733endfunc
734
735func Test_defer_quitall_def()
736 let lines =<< trim END
737 vim9script
738 def DeferLevelTwo()
739 call writefile(['text'], 'XQuitallDefTwo', 'D')
740 call writefile(['quit'], 'XQuitallDefThree', 'a')
741 qa!
742 enddef
743
Bram Moolenaar58779852022-09-06 18:31:14 +0100744 def DeferLevelOne()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100745 call writefile(['text'], 'XQuitallDefOne', 'D')
746 defer DeferLevelTwo()
Bram Moolenaar58779852022-09-06 18:31:14 +0100747 enddef
748
749 DeferLevelOne()
750 END
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100751 call writefile(lines, 'XdeferQuitallDef', 'D')
752 call system(GetVimCommand() .. ' -X -S XdeferQuitallDef')
Bram Moolenaar58779852022-09-06 18:31:14 +0100753 call assert_equal(0, v:shell_error)
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100754 call assert_false(filereadable('XQuitallDefOne'))
755 call assert_false(filereadable('XQuitallDefTwo'))
756 call assert_equal(['quit'], readfile('XQuitallDefThree'))
Bram Moolenaar42994bf2023-04-17 19:23:45 +0100757
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100758 call delete('XQuitallDefThree')
Bram Moolenaar58779852022-09-06 18:31:14 +0100759endfunc
760
zeertzjq960cf912023-04-18 21:52:54 +0100761func Test_defer_quitall_autocmd()
762 let lines =<< trim END
zeertzjq1be4b812023-04-19 14:21:24 +0100763 func DeferLevelFive()
764 defer writefile(['5'], 'XQuitallAutocmd', 'a')
765 qa!
zeertzjq960cf912023-04-18 21:52:54 +0100766 endfunc
767
zeertzjq1be4b812023-04-19 14:21:24 +0100768 autocmd User DeferAutocmdFive call DeferLevelFive()
zeertzjq960cf912023-04-18 21:52:54 +0100769
zeertzjq1be4b812023-04-19 14:21:24 +0100770 def DeferLevelFour()
771 defer writefile(['4'], 'XQuitallAutocmd', 'a')
772 doautocmd User DeferAutocmdFive
zeertzjq960cf912023-04-18 21:52:54 +0100773 enddef
774
zeertzjq1be4b812023-04-19 14:21:24 +0100775 func DeferLevelThree()
776 defer writefile(['3'], 'XQuitallAutocmd', 'a')
777 call DeferLevelFour()
778 endfunc
779
780 autocmd User DeferAutocmdThree ++nested call DeferLevelThree()
781
782 def DeferLevelTwo()
783 defer writefile(['2'], 'XQuitallAutocmd', 'a')
784 doautocmd User DeferAutocmdThree
785 enddef
786
787 func DeferLevelOne()
788 defer writefile(['1'], 'XQuitallAutocmd', 'a')
789 call DeferLevelTwo()
790 endfunc
791
zeertzjq960cf912023-04-18 21:52:54 +0100792 autocmd User DeferAutocmdOne ++nested call DeferLevelOne()
793
794 doautocmd User DeferAutocmdOne
795 END
796 call writefile(lines, 'XdeferQuitallAutocmd', 'D')
zeertzjq1be4b812023-04-19 14:21:24 +0100797 call system(GetVimCommand() .. ' -X -S XdeferQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100798 call assert_equal(0, v:shell_error)
zeertzjq1be4b812023-04-19 14:21:24 +0100799 call assert_equal(['5', '4', '3', '2', '1'], readfile('XQuitallAutocmd'))
800
801 call delete('XQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100802endfunc
803
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100804func Test_defer_quitall_in_expr_func()
805 let lines =<< trim END
806 def DefIndex(idx: number, val: string): bool
807 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
808 if val == 'b'
809 qa!
810 endif
811 return val == 'c'
812 enddef
813
814 def Test_defer_in_funcref()
815 assert_equal(2, indexof(['a', 'b', 'c'], funcref('g:DefIndex')))
816 enddef
817 call Test_defer_in_funcref()
818 END
819 call writefile(lines, 'XdeferQuitallExpr', 'D')
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100820 call system(GetVimCommand() .. ' -X -S XdeferQuitallExpr')
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100821 call assert_equal(0, v:shell_error)
822 call assert_false(filereadable('Xentry0'))
823 call assert_false(filereadable('Xentry1'))
824 call assert_false(filereadable('Xentry2'))
825endfunc
826
Bram Moolenaar98aff652022-09-06 21:02:35 +0100827func FuncIndex(idx, val)
828 call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D')
829 return a:val == 'c'
830endfunc
831
832def DefIndex(idx: number, val: string): bool
833 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
834 return val == 'c'
835enddef
836
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100837def DefIndexXtra(xtra: string, idx: number, val: string): bool
838 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
839 return val == 'c'
840enddef
841
Bram Moolenaar98aff652022-09-06 21:02:35 +0100842def Test_defer_in_funcref()
843 assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex')))
844 assert_false(filereadable('Xentry0'))
845 assert_false(filereadable('Xentry1'))
846 assert_false(filereadable('Xentry2'))
847
848 assert_equal(2, indexof(['a', 'b', 'c'], g:DefIndex))
849 assert_false(filereadable('Xentry0'))
850 assert_false(filereadable('Xentry1'))
851 assert_false(filereadable('Xentry2'))
852
853 assert_equal(2, indexof(['a', 'b', 'c'], function('g:DefIndex')))
854 assert_false(filereadable('Xentry0'))
855 assert_false(filereadable('Xentry1'))
856 assert_false(filereadable('Xentry2'))
857
858 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndex)))
859 assert_false(filereadable('Xentry0'))
860 assert_false(filereadable('Xentry1'))
861 assert_false(filereadable('Xentry2'))
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100862
863 assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra'])))
864 assert_false(filereadable('Xentry0'))
865 assert_false(filereadable('Xentry1'))
866 assert_false(filereadable('Xentry2'))
867
868 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra'])))
869 assert_false(filereadable('Xentry0'))
870 assert_false(filereadable('Xentry1'))
871 assert_false(filereadable('Xentry2'))
Bram Moolenaar98aff652022-09-06 21:02:35 +0100872enddef
873
Bram Moolenaar16900322022-09-08 19:51:45 +0100874func Test_defer_wrong_arguments()
875 call assert_fails('defer delete()', 'E119:')
876 call assert_fails('defer FuncIndex(1)', 'E119:')
877 call assert_fails('defer delete(1, 2, 3)', 'E118:')
878 call assert_fails('defer FuncIndex(1, 2, 3)', 'E118:')
879
880 let lines =<< trim END
881 def DeferFunc0()
882 defer delete()
883 enddef
884 defcompile
885 END
886 call v9.CheckScriptFailure(lines, 'E119:')
887 let lines =<< trim END
888 def DeferFunc3()
889 defer delete(1, 2, 3)
890 enddef
891 defcompile
892 END
893 call v9.CheckScriptFailure(lines, 'E118:')
894 let lines =<< trim END
895 def DeferFunc2()
896 defer delete(1, 2)
897 enddef
898 defcompile
899 END
900 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
901
902 def g:FuncOneArg(arg: string)
903 echo arg
904 enddef
905
906 let lines =<< trim END
907 def DeferUserFunc0()
908 defer g:FuncOneArg()
909 enddef
910 defcompile
911 END
912 call v9.CheckScriptFailure(lines, 'E119:')
913 let lines =<< trim END
914 def DeferUserFunc2()
915 defer g:FuncOneArg(1, 2)
916 enddef
917 defcompile
918 END
919 call v9.CheckScriptFailure(lines, 'E118:')
920 let lines =<< trim END
921 def DeferUserFunc1()
922 defer g:FuncOneArg(1)
923 enddef
924 defcompile
925 END
926 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
927endfunc
928
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200929" Test for calling a deferred function after an exception
930func Test_defer_after_exception()
931 let g:callTrace = []
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200932 func Bar()
933 let g:callTrace += [1]
934 throw 'InnerException'
935 endfunc
936
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200937 func Defer()
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200938 let g:callTrace += [2]
939 let g:callTrace += [3]
940 try
941 call Bar()
942 catch /InnerException/
943 let g:callTrace += [4]
944 endtry
945 let g:callTrace += [5]
946 let g:callTrace += [6]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200947 endfunc
948
949 func Foo()
950 defer Defer()
951 throw "TestException"
952 endfunc
953
954 try
955 call Foo()
956 catch /TestException/
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200957 let g:callTrace += [7]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200958 endtry
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200959 call assert_equal([2, 3, 1, 4, 5, 6, 7], g:callTrace)
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200960
961 delfunc Defer
962 delfunc Foo
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200963 delfunc Bar
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200964 unlet g:callTrace
965endfunc
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100966
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200967" Test for multiple deferred function which throw exceptions.
968" Exceptions thrown by deferred functions should result in error messages but
969" not propagated into the calling functions.
970func Test_multidefer_with_exception()
971 let g:callTrace = []
972 func Except()
973 let g:callTrace += [1]
974 throw 'InnerException'
975 let g:callTrace += [2]
976 endfunc
977
978 func FirstDefer()
979 let g:callTrace += [3]
980 let g:callTrace += [4]
981 endfunc
982
983 func SecondDeferWithExcept()
984 let g:callTrace += [5]
985 call Except()
986 let g:callTrace += [6]
987 endfunc
988
989 func ThirdDefer()
990 let g:callTrace += [7]
991 let g:callTrace += [8]
992 endfunc
993
994 func Foo()
995 let g:callTrace += [9]
996 defer FirstDefer()
997 defer SecondDeferWithExcept()
998 defer ThirdDefer()
999 let g:callTrace += [10]
1000 endfunc
1001
1002 let v:errmsg = ''
1003 try
1004 let g:callTrace += [11]
1005 call Foo()
1006 let g:callTrace += [12]
1007 catch /TestException/
1008 let g:callTrace += [13]
1009 catch
1010 let g:callTrace += [14]
1011 finally
1012 let g:callTrace += [15]
1013 endtry
1014 let g:callTrace += [16]
1015
1016 call assert_equal('E605: Exception not caught: InnerException', v:errmsg)
1017 call assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], g:callTrace)
1018
1019 unlet g:callTrace
1020 delfunc Except
1021 delfunc FirstDefer
1022 delfunc SecondDeferWithExcept
1023 delfunc ThirdDefer
1024 delfunc Foo
1025endfunc
1026
zeertzjq6a04bf52024-03-16 09:39:06 +01001027func Test_func_curly_brace_invalid_name()
1028 func Fail()
1029 func Foo{'()'}bar()
1030 endfunc
1031 endfunc
1032
1033 call assert_fails('call Fail()', 'E475: Invalid argument: Foo()bar')
1034
1035 silent! call Fail()
1036 call assert_equal([], getcompletion('Foo', 'function'))
1037
1038 set formatexpr=Fail()
1039 normal! gqq
1040 call assert_equal([], getcompletion('Foo', 'function'))
1041
1042 set formatexpr&
1043 delfunc Fail
1044endfunc
1045
zeertzjq21012302025-02-02 08:55:57 +01001046func Test_func_return_in_try_verbose()
1047 func TryReturnList()
1048 try
1049 return [1, 2, 3]
1050 endtry
1051 endfunc
1052 func TryReturnNumber()
1053 try
1054 return 123
1055 endtry
1056 endfunc
1057 func TryReturnOverlongString()
1058 try
1059 return repeat('a', 9999)
1060 endtry
1061 endfunc
1062
1063 " This should not cause heap-use-after-free
1064 call assert_match('\n:return \[1, 2, 3\] made pending\n',
1065 \ execute('14verbose call TryReturnList()'))
1066 " This should not cause stack-use-after-scope
1067 call assert_match('\n:return 123 made pending\n',
1068 \ execute('14verbose call TryReturnNumber()'))
1069 " An overlong string is truncated
1070 call assert_match('\n:return a\{100,}\.\.\.',
1071 \ execute('14verbose call TryReturnOverlongString()'))
1072
1073 delfunc TryReturnList
1074 delfunc TryReturnNumber
1075 delfunc TryReturnOverlongString
1076endfunc
1077
Bram Moolenaaree4e0c12020-04-06 21:35:05 +02001078" vim: shiftwidth=2 sts=2 expandtab