blob: b9f6a40e80825c227f76489be65620d62ce520df [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
Shane Harper2d187892025-03-12 21:12:12 +0100164 func! s:f(x = s:undefined)
165 return a:x
166 endfunc
167 call assert_fails('echo s:f()', ['E121: Undefined variable: s:undefined',
168 \ 'E121: Undefined variable: a:x'])
169
170 func! s:f(x = s:undefined) abort
171 return a:x
172 endfunc
173 const expected_error = 'E121: Undefined variable: s:undefined'
174 " Only one error should be output; execution of the function should be aborted
175 " after the default argument expression error.
176 call assert_fails('echo s:f()', [expected_error, expected_error])
177endfunc
178
179func Test_default_argument_expression_error_while_inside_of_a_try_block()
180 func! s:f(v = s:undefined_variable)
181 let s:entered_fn_body = 1
182 return a:v
183 endfunc
184
185 unlet! s:entered_fn_body
186 try
187 call s:f()
188 throw "No exception."
189 catch
190 call assert_exception("E121: Undefined variable: s:undefined_variable")
191 endtry
192 call assert_false(exists('s:entered_fn_body'), "exists('s:entered_fn_body')")
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200193endfunc
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +0200194
195func s:addFoo(lead)
196 return a:lead .. 'foo'
197endfunc
198
199func Test_user_method()
200 eval 'bar'->s:addFoo()->assert_equal('barfoo')
201endfunc
Bram Moolenaare51bb172020-02-16 19:42:23 +0100202
Bram Moolenaar34820942022-12-19 20:28:38 +0000203func Test_method_with_linebreaks()
204 let lines =<< trim END
205 vim9script
206
207 export def Scan(ll: list<number>): func(func(number))
208 return (Emit: func(number)) => {
209 for v in ll
210 Emit(v)
211 endfor
212 }
213 enddef
214
215 export def Build(Cont: func(func(number))): list<number>
216 var result: list<number> = []
217 Cont((v) => {
218 add(result, v)
219 })
220 return result
221 enddef
222
223 export def Noop(Cont: func(func(number))): func(func(number))
224 return (Emit: func(number)) => {
225 Cont(Emit)
226 }
227 enddef
228 END
229 call writefile(lines, 'Xlib.vim', 'D')
230
231 let lines =<< trim END
232 vim9script
233
234 import "./Xlib.vim" as lib
235
236 const x = [1, 2, 3]
237
238 var result = lib.Scan(x)->lib.Noop()->lib.Build()
239 assert_equal([1, 2, 3], result)
240
241 result = lib.Scan(x)->lib.Noop()
242 ->lib.Build()
243 assert_equal([1, 2, 3], result)
244
245 result = lib.Scan(x)
246 ->lib.Noop()->lib.Build()
247 assert_equal([1, 2, 3], result)
248
249 result = lib.Scan(x)
250 ->lib.Noop()
251 ->lib.Build()
252 assert_equal([1, 2, 3], result)
253 END
254 call v9.CheckScriptSuccess(lines)
255endfunc
256
Bram Moolenaare51bb172020-02-16 19:42:23 +0100257func Test_failed_call_in_try()
258 try | call UnknownFunc() | catch | endtry
259endfunc
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200260
261" Test for listing user-defined functions
262func Test_function_list()
263 call assert_fails("function Xabc", 'E123:')
264endfunc
265
Bram Moolenaar476a6132020-04-08 19:48:56 +0200266" Test for <sfile>, <slnum> in a function
267func Test_sfile_in_function()
268 func Xfunc()
269 call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
270 call assert_equal('2', expand('<slnum>'))
271 endfunc
272 call Xfunc()
273 delfunc Xfunc
274endfunc
275
276" Test trailing text after :endfunction {{{1
277func Test_endfunction_trailing()
278 call assert_false(exists('*Xtest'))
279
280 exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
281 call assert_true(exists('*Xtest'))
282 call assert_equal('yes', done)
283 delfunc Xtest
284 unlet done
285
286 exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
287 call assert_true(exists('*Xtest'))
288 call assert_equal('yes', done)
289 delfunc Xtest
290 unlet done
291
292 " trailing line break
293 exe "func Xtest()\necho 'hello'\nendfunc\n"
294 call assert_true(exists('*Xtest'))
295 delfunc Xtest
296
297 set verbose=1
298 exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
299 call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
300 call assert_true(exists('*Xtest'))
301 delfunc Xtest
302
303 exe "func Xtest()\necho 'hello'\nendfunc garbage"
304 call assert_match('W22:', split(execute('1messages'), "\n")[0])
305 call assert_true(exists('*Xtest'))
306 delfunc Xtest
307 set verbose=0
308
Bram Moolenaara0d072e2020-07-01 20:19:37 +0200309 func Xtest(a1, a2)
310 echo a:a1 .. a:a2
311 endfunc
312 set verbose=15
313 redir @a
314 call Xtest(123, repeat('x', 100))
315 redir END
316 call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a'))
317 delfunc Xtest
318 set verbose=0
319
Bram Moolenaar476a6132020-04-08 19:48:56 +0200320 function Foo()
321 echo 'hello'
322 endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
323 delfunc Foo
324endfunc
325
326func Test_delfunction_force()
327 delfunc! Xtest
328 delfunc! Xtest
329 func Xtest()
330 echo 'nothing'
331 endfunc
332 delfunc! Xtest
333 delfunc! Xtest
334
335 " Try deleting the current function
336 call assert_fails('delfunc Test_delfunction_force', 'E131:')
337endfunc
338
339func Test_function_defined_line()
340 CheckNotGui
341
342 let lines =<< trim [CODE]
343 " F1
344 func F1()
345 " F2
346 func F2()
347 "
348 "
349 "
350 return
351 endfunc
352 " F3
353 execute "func F3()\n\n\n\nreturn\nendfunc"
354 " F4
355 execute "func F4()\n
356 \\n
357 \\n
358 \\n
359 \return\n
360 \endfunc"
361 endfunc
362 " F5
363 execute "func F5()\n\n\n\nreturn\nendfunc"
364 " F6
365 execute "func F6()\n
366 \\n
367 \\n
368 \\n
369 \return\n
370 \endfunc"
371 call F1()
372 verbose func F1
373 verbose func F2
374 verbose func F3
375 verbose func F4
376 verbose func F5
377 verbose func F6
378 qall!
379 [CODE]
380
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100381 call writefile(lines, 'Xtest.vim', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200382 let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
383 call assert_equal(0, v:shell_error)
384
385 let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
386 call assert_match(' line 2$', m)
387
388 let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
389 call assert_match(' line 4$', m)
390
391 let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
392 call assert_match(' line 11$', m)
393
394 let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
395 call assert_match(' line 13$', m)
396
397 let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
398 call assert_match(' line 21$', m)
399
400 let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
401 call assert_match(' line 23$', m)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200402endfunc
403
404" Test for defining a function reference in the global scope
405func Test_add_funcref_to_global_scope()
406 let x = g:
407 let caught_E862 = 0
408 try
409 func x.Xfunc()
410 return 1
411 endfunc
412 catch /E862:/
413 let caught_E862 = 1
414 endtry
415 call assert_equal(1, caught_E862)
416endfunc
417
418func Test_funccall_garbage_collect()
419 func Func(x, ...)
420 call add(a:x, a:000)
421 endfunc
422 call Func([], [])
423 " Must not crash cause by invalid freeing
424 call test_garbagecollect_now()
425 call assert_true(v:true)
426 delfunc Func
427endfunc
428
429" Test for script-local function
430func <SID>DoLast()
431 call append(line('$'), "last line")
432endfunc
433
434func s:DoNothing()
435 call append(line('$'), "nothing line")
436endfunc
437
438func Test_script_local_func()
439 set nocp nomore viminfo+=nviminfo
440 new
441 nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
442
443 normal _x
444 call assert_equal('nothing line', getline(2))
445 call assert_equal('last line', getline(3))
446 close!
447
448 " Try to call a script local function in global scope
449 let lines =<< trim [CODE]
450 :call assert_fails('call s:Xfunc()', 'E81:')
Yegappan Lakshmanan6289f912025-01-14 17:13:36 +0100451 :call assert_fails('let x = call("<SID>Xfunc", [])', ['E81:', 'E117:'])
Bram Moolenaar476a6132020-04-08 19:48:56 +0200452 :call writefile(v:errors, 'Xresult')
453 :qall
454
455 [CODE]
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100456 call writefile(lines, 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200457 if RunVim([], [], '-s Xscript')
458 call assert_equal([], readfile('Xresult'))
459 endif
460 call delete('Xresult')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200461endfunc
462
463" Test for errors in defining new functions
464func Test_func_def_error()
465 call assert_fails('func Xfunc abc ()', 'E124:')
466 call assert_fails('func Xfunc(', 'E125:')
467 call assert_fails('func xfunc()', 'E128:')
468
469 " Try to redefine a function that is in use
470 let caught_E127 = 0
471 try
472 func! Test_func_def_error()
473 endfunc
474 catch /E127:/
475 let caught_E127 = 1
476 endtry
477 call assert_equal(1, caught_E127)
478
479 " Try to define a function in a dict twice
480 let d = {}
481 let lines =<< trim END
482 func d.F1()
483 return 1
484 endfunc
485 END
486 let l = join(lines, "\n") . "\n"
487 exe l
488 call assert_fails('exe l', 'E717:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200489 call assert_fails('call feedkeys(":func d.F1()\<CR>", "xt")', 'E717:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200490
491 " Define an autoload function with an incorrect file name
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100492 call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200493 call assert_fails('source Xscript', 'E746:')
Bram Moolenaar531be472020-09-23 22:38:05 +0200494
495 " Try to list functions using an invalid search pattern
496 call assert_fails('function /\%(/', 'E53:')
zeertzjq04d2a3f2025-02-02 19:03:17 +0100497
498 " Use a script-local function to cover uf_name_exp.
499 func s:TestRedefine(arg1 = 1, arg2 = 10)
500 let caught_E122 = 0
501 try
502 func s:TestRedefine(arg1 = 1, arg2 = 10)
503 endfunc
504 catch /E122:/
505 let caught_E122 = 1
506 endtry
507 call assert_equal(1, caught_E122)
508
509 let caught_E127 = 0
510 try
511 func! s:TestRedefine(arg1 = 1, arg2 = 10)
512 endfunc
513 catch /E127:/
514 let caught_E127 = 1
515 endtry
516 call assert_equal(1, caught_E127)
517
518 " The failures above shouldn't cause heap-use-after-free here.
519 return [a:arg1 + a:arg2, expand('<stack>')]
520 endfunc
521
522 let stacks = []
523 " Call the function twice.
524 " Failing to redefine a function shouldn't clear its argument list.
525 for i in range(2)
526 let [val, stack] = s:TestRedefine(1000)
527 call assert_equal(1010, val)
528 call assert_match(expand('<SID>') .. 'TestRedefine\[20\]$', stack)
529 call add(stacks, stack)
530 endfor
531 call assert_equal(stacks[0], stacks[1])
532
533 delfunc s:TestRedefine
Bram Moolenaar476a6132020-04-08 19:48:56 +0200534endfunc
535
536" Test for deleting a function
537func Test_del_func()
Bram Moolenaarc553a212021-12-26 20:20:34 +0000538 call assert_fails('delfunction Xabc', 'E117:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200539 let d = {'a' : 10}
540 call assert_fails('delfunc d.a', 'E718:')
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200541 func d.fn()
542 return 1
543 endfunc
Bram Moolenaarddfc0512021-09-06 20:56:56 +0200544
545 " cannot delete the dict function by number
546 let nr = substitute(execute('echo d'), '.*function(''\(\d\+\)'').*', '\1', '')
547 call assert_fails('delfunction g:' .. nr, 'E475: Invalid argument: g:')
548
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200549 delfunc d.fn
550 call assert_equal({'a' : 10}, d)
Bram Moolenaar476a6132020-04-08 19:48:56 +0200551endfunc
552
553" Test for calling return outside of a function
554func Test_return_outside_func()
Bram Moolenaar5b148ef2022-10-15 21:35:56 +0100555 call writefile(['return 10'], 'Xscript', 'D')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200556 call assert_fails('source Xscript', 'E133:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200557endfunc
558
559" Test for errors in calling a function
560func Test_func_arg_error()
561 " Too many arguments
562 call assert_fails("call call('min', range(1,20))", 'E118:')
563 call assert_fails("call call('min', range(1,21))", 'E699:')
564 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)',
565 \ 'E740:')
566
567 " Missing dict argument
568 func Xfunc() dict
569 return 1
570 endfunc
571 call assert_fails('call Xfunc()', 'E725:')
572 delfunc Xfunc
573endfunc
574
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100575func Test_func_dict()
576 let mydict = {'a': 'b'}
577 function mydict.somefunc() dict
578 return len(self)
579 endfunc
580
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200581 call assert_equal("{'a': 'b', 'somefunc': function('3')}", string(mydict))
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100582 call assert_equal(2, mydict.somefunc())
583 call assert_match("^\n function \\d\\\+() dict"
584 \ .. "\n1 return len(self)"
585 \ .. "\n endfunction$", execute('func mydict.somefunc'))
Yegappan Lakshmanan611728f2021-05-24 15:15:47 +0200586 call assert_fails('call mydict.nonexist()', 'E716:')
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100587endfunc
588
589func Test_func_range()
590 new
591 call setline(1, range(1, 8))
592 func FuncRange() range
593 echo a:firstline
594 echo a:lastline
595 endfunc
596 3
597 call assert_equal("\n3\n3", execute('call FuncRange()'))
598 call assert_equal("\n4\n6", execute('4,6 call FuncRange()'))
599 call assert_equal("\n function FuncRange() range"
600 \ .. "\n1 echo a:firstline"
601 \ .. "\n2 echo a:lastline"
602 \ .. "\n endfunction",
603 \ execute('function FuncRange'))
604
605 bwipe!
606endfunc
607
Yegappan Lakshmanan7c7e19c2022-04-09 11:09:07 +0100608" Test for memory allocation failure when defining a new function
609func Test_funcdef_alloc_failure()
610 new
611 let lines =<< trim END
612 func Xtestfunc()
613 return 321
614 endfunc
615 END
616 call setline(1, lines)
617 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
618 call assert_fails('source', 'E342:')
619 call assert_false(exists('*Xtestfunc'))
620 call assert_fails('delfunc Xtestfunc', 'E117:')
621 %d _
622 let lines =<< trim END
623 def g:Xvim9func(): number
624 return 456
625 enddef
626 END
627 call setline(1, lines)
628 call test_alloc_fail(GetAllocId('get_func'), 0, 0)
629 call assert_fails('source', 'E342:')
630 call assert_false(exists('*Xvim9func'))
631 "call test_alloc_fail(GetAllocId('get_func'), 0, 0)
632 "call assert_fails('source', 'E342:')
633 "call assert_false(exists('*Xtestfunc'))
634 "call assert_fails('delfunc Xtestfunc', 'E117:')
635 bw!
636endfunc
637
Bram Moolenaar86d87252022-09-05 21:21:25 +0100638func AddDefer(arg1, ...)
639 call extend(g:deferred, [a:arg1])
640 if a:0 == 1
641 call extend(g:deferred, [a:1])
642 endif
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100643endfunc
644
645func WithDeferTwo()
646 call extend(g:deferred, ['in Two'])
647 for nr in range(3)
648 defer AddDefer('Two' .. nr)
649 endfor
650 call extend(g:deferred, ['end Two'])
651endfunc
652
653func WithDeferOne()
654 call extend(g:deferred, ['in One'])
655 call writefile(['text'], 'Xfuncdefer')
656 defer delete('Xfuncdefer')
657 defer AddDefer('One')
658 call WithDeferTwo()
659 call extend(g:deferred, ['end One'])
660endfunc
661
Bram Moolenaar86d87252022-09-05 21:21:25 +0100662func WithPartialDefer()
663 call extend(g:deferred, ['in Partial'])
664 let Part = funcref('AddDefer', ['arg1'])
665 defer Part("arg2")
666 call extend(g:deferred, ['end Partial'])
667endfunc
668
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100669func Test_defer()
670 let g:deferred = []
671 call WithDeferOne()
672
673 call assert_equal(['in One', 'in Two', 'end Two', 'Two2', 'Two1', 'Two0', 'end One', 'One'], g:deferred)
674 unlet g:deferred
675
676 call assert_equal('', glob('Xfuncdefer'))
Bram Moolenaar86d87252022-09-05 21:21:25 +0100677
678 call assert_fails('defer delete("Xfuncdefer")->Another()', 'E488:')
679 call assert_fails('defer delete("Xfuncdefer").member', 'E488:')
680
681 let g:deferred = []
682 call WithPartialDefer()
683 call assert_equal(['in Partial', 'end Partial', 'arg1', 'arg2'], g:deferred)
684 unlet g:deferred
685
686 let Part = funcref('AddDefer', ['arg1'], {})
687 call assert_fails('defer Part("arg2")', 'E1300:')
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100688endfunc
689
Bram Moolenaar58779852022-09-06 18:31:14 +0100690func DeferLevelTwo()
691 call writefile(['text'], 'XDeleteTwo', 'D')
692 throw 'someerror'
693endfunc
694
695def DeferLevelOne()
696 call writefile(['text'], 'XDeleteOne', 'D')
697 call g:DeferLevelTwo()
698enddef
699
700func Test_defer_throw()
701 let caught = 'no'
702 try
703 call DeferLevelOne()
704 catch /someerror/
705 let caught = 'yes'
706 endtry
707 call assert_equal('yes', caught)
708 call assert_false(filereadable('XDeleteOne'))
709 call assert_false(filereadable('XDeleteTwo'))
710endfunc
711
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100712func Test_defer_quitall_func()
Bram Moolenaar58779852022-09-06 18:31:14 +0100713 let lines =<< trim END
Bram Moolenaar58779852022-09-06 18:31:14 +0100714 func DeferLevelTwo()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100715 call writefile(['text'], 'XQuitallFuncTwo', 'D')
716 call writefile(['quit'], 'XQuitallFuncThree', 'a')
Bram Moolenaar58779852022-09-06 18:31:14 +0100717 qa!
718 endfunc
719
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100720 func DeferLevelOne()
721 call writefile(['text'], 'XQuitalFunclOne', 'D')
722 defer DeferLevelTwo()
723 endfunc
724
725 call DeferLevelOne()
726 END
727 call writefile(lines, 'XdeferQuitallFunc', 'D')
728 call system(GetVimCommand() .. ' -X -S XdeferQuitallFunc')
729 call assert_equal(0, v:shell_error)
730 call assert_false(filereadable('XQuitallFuncOne'))
731 call assert_false(filereadable('XQuitallFuncTwo'))
732 call assert_equal(['quit'], readfile('XQuitallFuncThree'))
733
734 call delete('XQuitallFuncThree')
735endfunc
736
737func Test_defer_quitall_def()
738 let lines =<< trim END
739 vim9script
740 def DeferLevelTwo()
741 call writefile(['text'], 'XQuitallDefTwo', 'D')
742 call writefile(['quit'], 'XQuitallDefThree', 'a')
743 qa!
744 enddef
745
Bram Moolenaar58779852022-09-06 18:31:14 +0100746 def DeferLevelOne()
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100747 call writefile(['text'], 'XQuitallDefOne', 'D')
748 defer DeferLevelTwo()
Bram Moolenaar58779852022-09-06 18:31:14 +0100749 enddef
750
751 DeferLevelOne()
752 END
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100753 call writefile(lines, 'XdeferQuitallDef', 'D')
754 call system(GetVimCommand() .. ' -X -S XdeferQuitallDef')
Bram Moolenaar58779852022-09-06 18:31:14 +0100755 call assert_equal(0, v:shell_error)
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100756 call assert_false(filereadable('XQuitallDefOne'))
757 call assert_false(filereadable('XQuitallDefTwo'))
758 call assert_equal(['quit'], readfile('XQuitallDefThree'))
Bram Moolenaar42994bf2023-04-17 19:23:45 +0100759
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100760 call delete('XQuitallDefThree')
Bram Moolenaar58779852022-09-06 18:31:14 +0100761endfunc
762
zeertzjq960cf912023-04-18 21:52:54 +0100763func Test_defer_quitall_autocmd()
764 let lines =<< trim END
zeertzjq1be4b812023-04-19 14:21:24 +0100765 func DeferLevelFive()
766 defer writefile(['5'], 'XQuitallAutocmd', 'a')
767 qa!
zeertzjq960cf912023-04-18 21:52:54 +0100768 endfunc
769
zeertzjq1be4b812023-04-19 14:21:24 +0100770 autocmd User DeferAutocmdFive call DeferLevelFive()
zeertzjq960cf912023-04-18 21:52:54 +0100771
zeertzjq1be4b812023-04-19 14:21:24 +0100772 def DeferLevelFour()
773 defer writefile(['4'], 'XQuitallAutocmd', 'a')
774 doautocmd User DeferAutocmdFive
zeertzjq960cf912023-04-18 21:52:54 +0100775 enddef
776
zeertzjq1be4b812023-04-19 14:21:24 +0100777 func DeferLevelThree()
778 defer writefile(['3'], 'XQuitallAutocmd', 'a')
779 call DeferLevelFour()
780 endfunc
781
782 autocmd User DeferAutocmdThree ++nested call DeferLevelThree()
783
784 def DeferLevelTwo()
785 defer writefile(['2'], 'XQuitallAutocmd', 'a')
786 doautocmd User DeferAutocmdThree
787 enddef
788
789 func DeferLevelOne()
790 defer writefile(['1'], 'XQuitallAutocmd', 'a')
791 call DeferLevelTwo()
792 endfunc
793
zeertzjq960cf912023-04-18 21:52:54 +0100794 autocmd User DeferAutocmdOne ++nested call DeferLevelOne()
795
796 doautocmd User DeferAutocmdOne
797 END
798 call writefile(lines, 'XdeferQuitallAutocmd', 'D')
zeertzjq1be4b812023-04-19 14:21:24 +0100799 call system(GetVimCommand() .. ' -X -S XdeferQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100800 call assert_equal(0, v:shell_error)
zeertzjq1be4b812023-04-19 14:21:24 +0100801 call assert_equal(['5', '4', '3', '2', '1'], readfile('XQuitallAutocmd'))
802
803 call delete('XQuitallAutocmd')
zeertzjq960cf912023-04-18 21:52:54 +0100804endfunc
805
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100806func Test_defer_quitall_in_expr_func()
807 let lines =<< trim END
808 def DefIndex(idx: number, val: string): bool
809 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
810 if val == 'b'
811 qa!
812 endif
813 return val == 'c'
814 enddef
815
816 def Test_defer_in_funcref()
817 assert_equal(2, indexof(['a', 'b', 'c'], funcref('g:DefIndex')))
818 enddef
819 call Test_defer_in_funcref()
820 END
821 call writefile(lines, 'XdeferQuitallExpr', 'D')
zeertzjqa1f2b5d2023-04-18 21:04:53 +0100822 call system(GetVimCommand() .. ' -X -S XdeferQuitallExpr')
Bram Moolenaar9667b2c2022-09-07 17:28:09 +0100823 call assert_equal(0, v:shell_error)
824 call assert_false(filereadable('Xentry0'))
825 call assert_false(filereadable('Xentry1'))
826 call assert_false(filereadable('Xentry2'))
827endfunc
828
Bram Moolenaar98aff652022-09-06 21:02:35 +0100829func FuncIndex(idx, val)
830 call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D')
831 return a:val == 'c'
832endfunc
833
834def DefIndex(idx: number, val: string): bool
835 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
836 return val == 'c'
837enddef
838
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100839def DefIndexXtra(xtra: string, idx: number, val: string): bool
840 call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
841 return val == 'c'
842enddef
843
Bram Moolenaar98aff652022-09-06 21:02:35 +0100844def Test_defer_in_funcref()
845 assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex')))
846 assert_false(filereadable('Xentry0'))
847 assert_false(filereadable('Xentry1'))
848 assert_false(filereadable('Xentry2'))
849
850 assert_equal(2, indexof(['a', 'b', 'c'], g:DefIndex))
851 assert_false(filereadable('Xentry0'))
852 assert_false(filereadable('Xentry1'))
853 assert_false(filereadable('Xentry2'))
854
855 assert_equal(2, indexof(['a', 'b', 'c'], function('g:DefIndex')))
856 assert_false(filereadable('Xentry0'))
857 assert_false(filereadable('Xentry1'))
858 assert_false(filereadable('Xentry2'))
859
860 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndex)))
861 assert_false(filereadable('Xentry0'))
862 assert_false(filereadable('Xentry1'))
863 assert_false(filereadable('Xentry2'))
Bram Moolenaarc9c967d2022-09-07 16:48:46 +0100864
865 assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra'])))
866 assert_false(filereadable('Xentry0'))
867 assert_false(filereadable('Xentry1'))
868 assert_false(filereadable('Xentry2'))
869
870 assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra'])))
871 assert_false(filereadable('Xentry0'))
872 assert_false(filereadable('Xentry1'))
873 assert_false(filereadable('Xentry2'))
Bram Moolenaar98aff652022-09-06 21:02:35 +0100874enddef
875
Bram Moolenaar16900322022-09-08 19:51:45 +0100876func Test_defer_wrong_arguments()
877 call assert_fails('defer delete()', 'E119:')
878 call assert_fails('defer FuncIndex(1)', 'E119:')
879 call assert_fails('defer delete(1, 2, 3)', 'E118:')
880 call assert_fails('defer FuncIndex(1, 2, 3)', 'E118:')
881
882 let lines =<< trim END
883 def DeferFunc0()
884 defer delete()
885 enddef
886 defcompile
887 END
888 call v9.CheckScriptFailure(lines, 'E119:')
889 let lines =<< trim END
890 def DeferFunc3()
891 defer delete(1, 2, 3)
892 enddef
893 defcompile
894 END
895 call v9.CheckScriptFailure(lines, 'E118:')
896 let lines =<< trim END
897 def DeferFunc2()
898 defer delete(1, 2)
899 enddef
900 defcompile
901 END
902 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
903
904 def g:FuncOneArg(arg: string)
905 echo arg
906 enddef
907
908 let lines =<< trim END
909 def DeferUserFunc0()
910 defer g:FuncOneArg()
911 enddef
912 defcompile
913 END
914 call v9.CheckScriptFailure(lines, 'E119:')
915 let lines =<< trim END
916 def DeferUserFunc2()
917 defer g:FuncOneArg(1, 2)
918 enddef
919 defcompile
920 END
921 call v9.CheckScriptFailure(lines, 'E118:')
922 let lines =<< trim END
923 def DeferUserFunc1()
924 defer g:FuncOneArg(1)
925 enddef
926 defcompile
927 END
928 call v9.CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
929endfunc
930
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200931" Test for calling a deferred function after an exception
932func Test_defer_after_exception()
933 let g:callTrace = []
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200934 func Bar()
935 let g:callTrace += [1]
936 throw 'InnerException'
937 endfunc
938
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200939 func Defer()
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200940 let g:callTrace += [2]
941 let g:callTrace += [3]
942 try
943 call Bar()
944 catch /InnerException/
945 let g:callTrace += [4]
946 endtry
947 let g:callTrace += [5]
948 let g:callTrace += [6]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200949 endfunc
950
951 func Foo()
952 defer Defer()
953 throw "TestException"
954 endfunc
955
956 try
957 call Foo()
958 catch /TestException/
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200959 let g:callTrace += [7]
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200960 endtry
Yegappan Lakshmananc59c1e02023-10-19 10:52:34 +0200961 call assert_equal([2, 3, 1, 4, 5, 6, 7], g:callTrace)
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200962
963 delfunc Defer
964 delfunc Foo
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200965 delfunc Bar
Yegappan Lakshmanan06725952023-10-18 11:47:37 +0200966 unlet g:callTrace
967endfunc
Bram Moolenaar1d84f762022-09-03 21:35:53 +0100968
Yegappan Lakshmanan0ab500d2023-10-21 11:59:42 +0200969" Test for multiple deferred function which throw exceptions.
970" Exceptions thrown by deferred functions should result in error messages but
971" not propagated into the calling functions.
972func Test_multidefer_with_exception()
973 let g:callTrace = []
974 func Except()
975 let g:callTrace += [1]
976 throw 'InnerException'
977 let g:callTrace += [2]
978 endfunc
979
980 func FirstDefer()
981 let g:callTrace += [3]
982 let g:callTrace += [4]
983 endfunc
984
985 func SecondDeferWithExcept()
986 let g:callTrace += [5]
987 call Except()
988 let g:callTrace += [6]
989 endfunc
990
991 func ThirdDefer()
992 let g:callTrace += [7]
993 let g:callTrace += [8]
994 endfunc
995
996 func Foo()
997 let g:callTrace += [9]
998 defer FirstDefer()
999 defer SecondDeferWithExcept()
1000 defer ThirdDefer()
1001 let g:callTrace += [10]
1002 endfunc
1003
1004 let v:errmsg = ''
1005 try
1006 let g:callTrace += [11]
1007 call Foo()
1008 let g:callTrace += [12]
1009 catch /TestException/
1010 let g:callTrace += [13]
1011 catch
1012 let g:callTrace += [14]
1013 finally
1014 let g:callTrace += [15]
1015 endtry
1016 let g:callTrace += [16]
1017
1018 call assert_equal('E605: Exception not caught: InnerException', v:errmsg)
1019 call assert_equal([11, 9, 10, 7, 8, 5, 1, 3, 4, 12, 15, 16], g:callTrace)
1020
1021 unlet g:callTrace
1022 delfunc Except
1023 delfunc FirstDefer
1024 delfunc SecondDeferWithExcept
1025 delfunc ThirdDefer
1026 delfunc Foo
1027endfunc
1028
zeertzjq6a04bf52024-03-16 09:39:06 +01001029func Test_func_curly_brace_invalid_name()
1030 func Fail()
1031 func Foo{'()'}bar()
1032 endfunc
1033 endfunc
1034
1035 call assert_fails('call Fail()', 'E475: Invalid argument: Foo()bar')
1036
1037 silent! call Fail()
1038 call assert_equal([], getcompletion('Foo', 'function'))
1039
1040 set formatexpr=Fail()
1041 normal! gqq
1042 call assert_equal([], getcompletion('Foo', 'function'))
1043
1044 set formatexpr&
1045 delfunc Fail
1046endfunc
1047
zeertzjq21012302025-02-02 08:55:57 +01001048func Test_func_return_in_try_verbose()
1049 func TryReturnList()
1050 try
1051 return [1, 2, 3]
1052 endtry
1053 endfunc
1054 func TryReturnNumber()
1055 try
1056 return 123
1057 endtry
1058 endfunc
1059 func TryReturnOverlongString()
1060 try
1061 return repeat('a', 9999)
1062 endtry
1063 endfunc
1064
1065 " This should not cause heap-use-after-free
1066 call assert_match('\n:return \[1, 2, 3\] made pending\n',
1067 \ execute('14verbose call TryReturnList()'))
1068 " This should not cause stack-use-after-scope
1069 call assert_match('\n:return 123 made pending\n',
1070 \ execute('14verbose call TryReturnNumber()'))
1071 " An overlong string is truncated
1072 call assert_match('\n:return a\{100,}\.\.\.',
1073 \ execute('14verbose call TryReturnOverlongString()'))
1074
1075 delfunc TryReturnList
1076 delfunc TryReturnNumber
1077 delfunc TryReturnOverlongString
1078endfunc
1079
Bram Moolenaaree4e0c12020-04-06 21:35:05 +02001080" vim: shiftwidth=2 sts=2 expandtab