blob: 2db558931daf05176de865db75eacd112f33d673 [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
8
Bram Moolenaar53f0c962017-10-22 14:23:59 +02009func Table(title, ...)
10 let ret = a:title
11 let idx = 1
12 while idx <= a:0
13 exe "let ret = ret . a:" . idx
14 let idx = idx + 1
15 endwhile
16 return ret
17endfunc
18
19func Compute(n1, n2, divname)
20 if a:n2 == 0
21 return "fail"
22 endif
23 exe "let g:" . a:divname . " = ". a:n1 / a:n2
24 return "ok"
25endfunc
26
27func Expr1()
28 silent! normal! v
29 return "111"
30endfunc
31
32func Expr2()
33 call search('XX', 'b')
34 return "222"
35endfunc
36
37func ListItem()
38 let g:counter += 1
39 return g:counter . '. '
40endfunc
41
42func ListReset()
43 let g:counter = 0
44 return ''
45endfunc
46
47func FuncWithRef(a)
48 unlet g:FuncRef
49 return a:a
50endfunc
51
52func Test_user_func()
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020053 let g:FuncRef = function("FuncWithRef")
Bram Moolenaar53f0c962017-10-22 14:23:59 +020054 let g:counter = 0
55 inoremap <expr> ( ListItem()
56 inoremap <expr> [ ListReset()
57 imap <expr> + Expr1()
58 imap <expr> * Expr2()
59 let g:retval = "nop"
60
61 call assert_equal('xxx4asdf', Table("xxx", 4, "asdf"))
62 call assert_equal('fail', Compute(45, 0, "retval"))
63 call assert_equal('nop', g:retval)
64 call assert_equal('ok', Compute(45, 5, "retval"))
65 call assert_equal(9, g:retval)
66 call assert_equal(333, g:FuncRef(333))
67
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +020068 let g:retval = "nop"
69 call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
70 call assert_equal('fail', 45->Compute(0, "retval"))
71 call assert_equal('nop', g:retval)
72 call assert_equal('ok', 45->Compute(5, "retval"))
73 call assert_equal(9, g:retval)
74 " call assert_equal(333, 333->g:FuncRef())
75
Bram Moolenaar53f0c962017-10-22 14:23:59 +020076 enew
77
78 normal oXX+-XX
79 call assert_equal('XX111-XX', getline('.'))
80 normal o---*---
81 call assert_equal('---222---', getline('.'))
82 normal o(one
83 call assert_equal('1. one', getline('.'))
84 normal o(two
85 call assert_equal('2. two', getline('.'))
86 normal o[(one again
87 call assert_equal('1. one again', getline('.'))
88
Bram Moolenaar476a6132020-04-08 19:48:56 +020089 " Try to overwrite a function in the global (g:) scope
Bram Moolenaar53f0c962017-10-22 14:23:59 +020090 call assert_equal(3, max([1, 2, 3]))
Bram Moolenaare2e40752020-09-04 21:18:46 +020091 call assert_fails("call extend(g:, {'max': function('min')})", 'E704:')
Bram Moolenaar53f0c962017-10-22 14:23:59 +020092 call assert_equal(3, max([1, 2, 3]))
93
Bram Moolenaar8dfcce32020-03-18 19:32:26 +010094 " Try to overwrite an user defined function with a function reference
95 call assert_fails("let Expr1 = function('min')", 'E705:')
96
Bram Moolenaar53f0c962017-10-22 14:23:59 +020097 " Regression: the first line below used to throw ?E110: Missing ')'?
98 " Second is here just to prove that this line is correct when not skipping
99 " rhs of &&.
100 call assert_equal(0, (0 && (function('tr'))(1, 2, 3)))
101 call assert_equal(1, (1 && (function('tr'))(1, 2, 3)))
102
103 delfunc Table
104 delfunc Compute
105 delfunc Expr1
106 delfunc Expr2
107 delfunc ListItem
108 delfunc ListReset
109 unlet g:retval g:counter
110 enew!
111endfunc
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200112
113func Log(val, base = 10)
114 return log(a:val) / log(a:base)
115endfunc
116
117func Args(mandatory, optional = v:null, ...)
118 return deepcopy(a:)
119endfunc
120
121func Args2(a = 1, b = 2, c = 3)
122 return deepcopy(a:)
123endfunc
124
125func MakeBadFunc()
126 func s:fcn(a, b=1, c)
127 endfunc
128endfunc
129
130func Test_default_arg()
Bram Moolenaar5feabe02020-01-30 18:24:53 +0100131 if has('float')
132 call assert_equal(1.0, Log(10))
133 call assert_equal(log(10), Log(10, exp(1)))
Bram Moolenaare2e40752020-09-04 21:18:46 +0200134 call assert_fails("call Log(1,2,3)", 'E118:')
Bram Moolenaar5feabe02020-01-30 18:24:53 +0100135 endif
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200136
137 let res = Args(1)
138 call assert_equal(res.mandatory, 1)
139 call assert_equal(res.optional, v:null)
140 call assert_equal(res['0'], 0)
141
142 let res = Args(1,2)
143 call assert_equal(res.mandatory, 1)
144 call assert_equal(res.optional, 2)
145 call assert_equal(res['0'], 0)
146
147 let res = Args(1,2,3)
148 call assert_equal(res.mandatory, 1)
149 call assert_equal(res.optional, 2)
150 call assert_equal(res['0'], 1)
151
Bram Moolenaare2e40752020-09-04 21:18:46 +0200152 call assert_fails("call MakeBadFunc()", 'E989:')
153 call assert_fails("fu F(a=1 ,) | endf", 'E475:')
Bram Moolenaar42ae78c2019-05-09 21:08:58 +0200154
155 let d = Args2(7, v:none, 9)
156 call assert_equal([7, 2, 9], [d.a, d.b, d.c])
157
158 call assert_equal("\n"
159 \ .. " function Args2(a = 1, b = 2, c = 3)\n"
160 \ .. "1 return deepcopy(a:)\n"
161 \ .. " endfunction",
162 \ execute('func Args2'))
163endfunc
Bram Moolenaarfcfe1a92019-08-04 23:04:39 +0200164
165func s:addFoo(lead)
166 return a:lead .. 'foo'
167endfunc
168
169func Test_user_method()
170 eval 'bar'->s:addFoo()->assert_equal('barfoo')
171endfunc
Bram Moolenaare51bb172020-02-16 19:42:23 +0100172
173func Test_failed_call_in_try()
174 try | call UnknownFunc() | catch | endtry
175endfunc
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200176
177" Test for listing user-defined functions
178func Test_function_list()
179 call assert_fails("function Xabc", 'E123:')
180endfunc
181
Bram Moolenaar476a6132020-04-08 19:48:56 +0200182" Test for <sfile>, <slnum> in a function
183func Test_sfile_in_function()
184 func Xfunc()
185 call assert_match('..Test_sfile_in_function\[5]..Xfunc', expand('<sfile>'))
186 call assert_equal('2', expand('<slnum>'))
187 endfunc
188 call Xfunc()
189 delfunc Xfunc
190endfunc
191
192" Test trailing text after :endfunction {{{1
193func Test_endfunction_trailing()
194 call assert_false(exists('*Xtest'))
195
196 exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'"
197 call assert_true(exists('*Xtest'))
198 call assert_equal('yes', done)
199 delfunc Xtest
200 unlet done
201
202 exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'"
203 call assert_true(exists('*Xtest'))
204 call assert_equal('yes', done)
205 delfunc Xtest
206 unlet done
207
208 " trailing line break
209 exe "func Xtest()\necho 'hello'\nendfunc\n"
210 call assert_true(exists('*Xtest'))
211 delfunc Xtest
212
213 set verbose=1
214 exe "func Xtest()\necho 'hello'\nendfunc \" garbage"
215 call assert_notmatch('W22:', split(execute('1messages'), "\n")[0])
216 call assert_true(exists('*Xtest'))
217 delfunc Xtest
218
219 exe "func Xtest()\necho 'hello'\nendfunc garbage"
220 call assert_match('W22:', split(execute('1messages'), "\n")[0])
221 call assert_true(exists('*Xtest'))
222 delfunc Xtest
223 set verbose=0
224
Bram Moolenaara0d072e2020-07-01 20:19:37 +0200225 func Xtest(a1, a2)
226 echo a:a1 .. a:a2
227 endfunc
228 set verbose=15
229 redir @a
230 call Xtest(123, repeat('x', 100))
231 redir END
232 call assert_match('calling Xtest(123, ''xxxxxxx.*x\.\.\.x.*xxxx'')', getreg('a'))
233 delfunc Xtest
234 set verbose=0
235
Bram Moolenaar476a6132020-04-08 19:48:56 +0200236 function Foo()
237 echo 'hello'
238 endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
239 delfunc Foo
240endfunc
241
242func Test_delfunction_force()
243 delfunc! Xtest
244 delfunc! Xtest
245 func Xtest()
246 echo 'nothing'
247 endfunc
248 delfunc! Xtest
249 delfunc! Xtest
250
251 " Try deleting the current function
252 call assert_fails('delfunc Test_delfunction_force', 'E131:')
253endfunc
254
255func Test_function_defined_line()
256 CheckNotGui
257
258 let lines =<< trim [CODE]
259 " F1
260 func F1()
261 " F2
262 func F2()
263 "
264 "
265 "
266 return
267 endfunc
268 " F3
269 execute "func F3()\n\n\n\nreturn\nendfunc"
270 " F4
271 execute "func F4()\n
272 \\n
273 \\n
274 \\n
275 \return\n
276 \endfunc"
277 endfunc
278 " F5
279 execute "func F5()\n\n\n\nreturn\nendfunc"
280 " F6
281 execute "func F6()\n
282 \\n
283 \\n
284 \\n
285 \return\n
286 \endfunc"
287 call F1()
288 verbose func F1
289 verbose func F2
290 verbose func F3
291 verbose func F4
292 verbose func F5
293 verbose func F6
294 qall!
295 [CODE]
296
297 call writefile(lines, 'Xtest.vim')
298 let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
299 call assert_equal(0, v:shell_error)
300
301 let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
302 call assert_match(' line 2$', m)
303
304 let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
305 call assert_match(' line 4$', m)
306
307 let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
308 call assert_match(' line 11$', m)
309
310 let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
311 call assert_match(' line 13$', m)
312
313 let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
314 call assert_match(' line 21$', m)
315
316 let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
317 call assert_match(' line 23$', m)
318
319 call delete('Xtest.vim')
320endfunc
321
322" Test for defining a function reference in the global scope
323func Test_add_funcref_to_global_scope()
324 let x = g:
325 let caught_E862 = 0
326 try
327 func x.Xfunc()
328 return 1
329 endfunc
330 catch /E862:/
331 let caught_E862 = 1
332 endtry
333 call assert_equal(1, caught_E862)
334endfunc
335
336func Test_funccall_garbage_collect()
337 func Func(x, ...)
338 call add(a:x, a:000)
339 endfunc
340 call Func([], [])
341 " Must not crash cause by invalid freeing
342 call test_garbagecollect_now()
343 call assert_true(v:true)
344 delfunc Func
345endfunc
346
347" Test for script-local function
348func <SID>DoLast()
349 call append(line('$'), "last line")
350endfunc
351
352func s:DoNothing()
353 call append(line('$'), "nothing line")
354endfunc
355
356func Test_script_local_func()
357 set nocp nomore viminfo+=nviminfo
358 new
359 nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
360
361 normal _x
362 call assert_equal('nothing line', getline(2))
363 call assert_equal('last line', getline(3))
364 close!
365
366 " Try to call a script local function in global scope
367 let lines =<< trim [CODE]
368 :call assert_fails('call s:Xfunc()', 'E81:')
369 :call assert_fails('let x = call("<SID>Xfunc", [])', 'E120:')
370 :call writefile(v:errors, 'Xresult')
371 :qall
372
373 [CODE]
374 call writefile(lines, 'Xscript')
375 if RunVim([], [], '-s Xscript')
376 call assert_equal([], readfile('Xresult'))
377 endif
378 call delete('Xresult')
379 call delete('Xscript')
380endfunc
381
382" Test for errors in defining new functions
383func Test_func_def_error()
384 call assert_fails('func Xfunc abc ()', 'E124:')
385 call assert_fails('func Xfunc(', 'E125:')
386 call assert_fails('func xfunc()', 'E128:')
387
388 " Try to redefine a function that is in use
389 let caught_E127 = 0
390 try
391 func! Test_func_def_error()
392 endfunc
393 catch /E127:/
394 let caught_E127 = 1
395 endtry
396 call assert_equal(1, caught_E127)
397
398 " Try to define a function in a dict twice
399 let d = {}
400 let lines =<< trim END
401 func d.F1()
402 return 1
403 endfunc
404 END
405 let l = join(lines, "\n") . "\n"
406 exe l
407 call assert_fails('exe l', 'E717:')
408
409 " Define an autoload function with an incorrect file name
410 call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
411 call assert_fails('source Xscript', 'E746:')
412 call delete('Xscript')
Bram Moolenaar531be472020-09-23 22:38:05 +0200413
414 " Try to list functions using an invalid search pattern
415 call assert_fails('function /\%(/', 'E53:')
Bram Moolenaar476a6132020-04-08 19:48:56 +0200416endfunc
417
418" Test for deleting a function
419func Test_del_func()
420 call assert_fails('delfunction Xabc', 'E130:')
421 let d = {'a' : 10}
422 call assert_fails('delfunc d.a', 'E718:')
423endfunc
424
425" Test for calling return outside of a function
426func Test_return_outside_func()
427 call writefile(['return 10'], 'Xscript')
428 call assert_fails('source Xscript', 'E133:')
429 call delete('Xscript')
430endfunc
431
432" Test for errors in calling a function
433func Test_func_arg_error()
434 " Too many arguments
435 call assert_fails("call call('min', range(1,20))", 'E118:')
436 call assert_fails("call call('min', range(1,21))", 'E699:')
437 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)',
438 \ 'E740:')
439
440 " Missing dict argument
441 func Xfunc() dict
442 return 1
443 endfunc
444 call assert_fails('call Xfunc()', 'E725:')
445 delfunc Xfunc
446endfunc
447
Bram Moolenaar67322bf2020-12-06 15:03:19 +0100448func Test_func_dict()
449 let mydict = {'a': 'b'}
450 function mydict.somefunc() dict
451 return len(self)
452 endfunc
453
454 call assert_equal("{'a': 'b', 'somefunc': function('2')}", string(mydict))
455 call assert_equal(2, mydict.somefunc())
456 call assert_match("^\n function \\d\\\+() dict"
457 \ .. "\n1 return len(self)"
458 \ .. "\n endfunction$", execute('func mydict.somefunc'))
459endfunc
460
461func Test_func_range()
462 new
463 call setline(1, range(1, 8))
464 func FuncRange() range
465 echo a:firstline
466 echo a:lastline
467 endfunc
468 3
469 call assert_equal("\n3\n3", execute('call FuncRange()'))
470 call assert_equal("\n4\n6", execute('4,6 call FuncRange()'))
471 call assert_equal("\n function FuncRange() range"
472 \ .. "\n1 echo a:firstline"
473 \ .. "\n2 echo a:lastline"
474 \ .. "\n endfunction",
475 \ execute('function FuncRange'))
476
477 bwipe!
478endfunc
479
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200480" vim: shiftwidth=2 sts=2 expandtab