blob: aec3619e77af83a007dad7e49195b19b41e176a0 [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]))
91 call assert_fails("call extend(g:, {'max': function('min')})", 'E704')
92 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)))
134 call assert_fails("call Log(1,2,3)", 'E118')
135 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
152 call assert_fails("call MakeBadFunc()", 'E989')
153 call assert_fails("fu F(a=1 ,) | endf", 'E475')
154
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
225 function Foo()
226 echo 'hello'
227 endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
228 delfunc Foo
229endfunc
230
231func Test_delfunction_force()
232 delfunc! Xtest
233 delfunc! Xtest
234 func Xtest()
235 echo 'nothing'
236 endfunc
237 delfunc! Xtest
238 delfunc! Xtest
239
240 " Try deleting the current function
241 call assert_fails('delfunc Test_delfunction_force', 'E131:')
242endfunc
243
244func Test_function_defined_line()
245 CheckNotGui
246
247 let lines =<< trim [CODE]
248 " F1
249 func F1()
250 " F2
251 func F2()
252 "
253 "
254 "
255 return
256 endfunc
257 " F3
258 execute "func F3()\n\n\n\nreturn\nendfunc"
259 " F4
260 execute "func F4()\n
261 \\n
262 \\n
263 \\n
264 \return\n
265 \endfunc"
266 endfunc
267 " F5
268 execute "func F5()\n\n\n\nreturn\nendfunc"
269 " F6
270 execute "func F6()\n
271 \\n
272 \\n
273 \\n
274 \return\n
275 \endfunc"
276 call F1()
277 verbose func F1
278 verbose func F2
279 verbose func F3
280 verbose func F4
281 verbose func F5
282 verbose func F6
283 qall!
284 [CODE]
285
286 call writefile(lines, 'Xtest.vim')
287 let res = system(GetVimCommandClean() .. ' -es -X -S Xtest.vim')
288 call assert_equal(0, v:shell_error)
289
290 let m = matchstr(res, 'function F1()[^[:print:]]*[[:print:]]*')
291 call assert_match(' line 2$', m)
292
293 let m = matchstr(res, 'function F2()[^[:print:]]*[[:print:]]*')
294 call assert_match(' line 4$', m)
295
296 let m = matchstr(res, 'function F3()[^[:print:]]*[[:print:]]*')
297 call assert_match(' line 11$', m)
298
299 let m = matchstr(res, 'function F4()[^[:print:]]*[[:print:]]*')
300 call assert_match(' line 13$', m)
301
302 let m = matchstr(res, 'function F5()[^[:print:]]*[[:print:]]*')
303 call assert_match(' line 21$', m)
304
305 let m = matchstr(res, 'function F6()[^[:print:]]*[[:print:]]*')
306 call assert_match(' line 23$', m)
307
308 call delete('Xtest.vim')
309endfunc
310
311" Test for defining a function reference in the global scope
312func Test_add_funcref_to_global_scope()
313 let x = g:
314 let caught_E862 = 0
315 try
316 func x.Xfunc()
317 return 1
318 endfunc
319 catch /E862:/
320 let caught_E862 = 1
321 endtry
322 call assert_equal(1, caught_E862)
323endfunc
324
325func Test_funccall_garbage_collect()
326 func Func(x, ...)
327 call add(a:x, a:000)
328 endfunc
329 call Func([], [])
330 " Must not crash cause by invalid freeing
331 call test_garbagecollect_now()
332 call assert_true(v:true)
333 delfunc Func
334endfunc
335
336" Test for script-local function
337func <SID>DoLast()
338 call append(line('$'), "last line")
339endfunc
340
341func s:DoNothing()
342 call append(line('$'), "nothing line")
343endfunc
344
345func Test_script_local_func()
346 set nocp nomore viminfo+=nviminfo
347 new
348 nnoremap <buffer> _x :call <SID>DoNothing()<bar>call <SID>DoLast()<bar>delfunc <SID>DoNothing<bar>delfunc <SID>DoLast<cr>
349
350 normal _x
351 call assert_equal('nothing line', getline(2))
352 call assert_equal('last line', getline(3))
353 close!
354
355 " Try to call a script local function in global scope
356 let lines =<< trim [CODE]
357 :call assert_fails('call s:Xfunc()', 'E81:')
358 :call assert_fails('let x = call("<SID>Xfunc", [])', 'E120:')
359 :call writefile(v:errors, 'Xresult')
360 :qall
361
362 [CODE]
363 call writefile(lines, 'Xscript')
364 if RunVim([], [], '-s Xscript')
365 call assert_equal([], readfile('Xresult'))
366 endif
367 call delete('Xresult')
368 call delete('Xscript')
369endfunc
370
371" Test for errors in defining new functions
372func Test_func_def_error()
373 call assert_fails('func Xfunc abc ()', 'E124:')
374 call assert_fails('func Xfunc(', 'E125:')
375 call assert_fails('func xfunc()', 'E128:')
376
377 " Try to redefine a function that is in use
378 let caught_E127 = 0
379 try
380 func! Test_func_def_error()
381 endfunc
382 catch /E127:/
383 let caught_E127 = 1
384 endtry
385 call assert_equal(1, caught_E127)
386
387 " Try to define a function in a dict twice
388 let d = {}
389 let lines =<< trim END
390 func d.F1()
391 return 1
392 endfunc
393 END
394 let l = join(lines, "\n") . "\n"
395 exe l
396 call assert_fails('exe l', 'E717:')
397
398 " Define an autoload function with an incorrect file name
399 call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
400 call assert_fails('source Xscript', 'E746:')
401 call delete('Xscript')
402endfunc
403
404" Test for deleting a function
405func Test_del_func()
406 call assert_fails('delfunction Xabc', 'E130:')
407 let d = {'a' : 10}
408 call assert_fails('delfunc d.a', 'E718:')
409endfunc
410
411" Test for calling return outside of a function
412func Test_return_outside_func()
413 call writefile(['return 10'], 'Xscript')
414 call assert_fails('source Xscript', 'E133:')
415 call delete('Xscript')
416endfunc
417
418" Test for errors in calling a function
419func Test_func_arg_error()
420 " Too many arguments
421 call assert_fails("call call('min', range(1,20))", 'E118:')
422 call assert_fails("call call('min', range(1,21))", 'E699:')
423 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)',
424 \ 'E740:')
425
426 " Missing dict argument
427 func Xfunc() dict
428 return 1
429 endfunc
430 call assert_fails('call Xfunc()', 'E725:')
431 delfunc Xfunc
432endfunc
433
Bram Moolenaaree4e0c12020-04-06 21:35:05 +0200434" vim: shiftwidth=2 sts=2 expandtab