1" Test various aspects of the Vim9 script language.
2
3source check.vim
4source term_util.vim
5source view_util.vim
6source vim9.vim
7source screendump.vim
8
9func Test_def_basic()
10  def SomeFunc(): string
11    return 'yes'
12  enddef
13  call SomeFunc()->assert_equal('yes')
14endfunc
15
16func Test_compiling_error()
17  " use a terminal to see the whole error message
18  CheckRunVimInTerminal
19
20  call TestCompilingError()
21endfunc
22
23def TestCompilingError()
24  var lines =<< trim END
25    vim9script
26    def Fails()
27      echo nothing
28    enddef
29    defcompile
30  END
31  call writefile(lines, 'XTest_compile_error')
32  var buf = RunVimInTerminal('-S XTest_compile_error',
33              {rows: 10, wait_for_ruler: 0})
34  var text = ''
35  for loop in range(100)
36    text = ''
37    for i in range(1, 9)
38      text ..= term_getline(buf, i)
39    endfor
40    if text =~ 'Variable not found: nothing'
41      break
42    endif
43    sleep 20m
44  endfor
45  assert_match('Error detected while compiling command line.*Fails.*Variable not found: nothing', text)
46
47  # clean up
48  call StopVimInTerminal(buf)
49  call delete('XTest_compile_error')
50enddef
51
52def CallRecursive(n: number): number
53  return CallRecursive(n + 1)
54enddef
55
56def CallMapRecursive(l: list<number>): number
57  return map(l, (_, v) => CallMapRecursive([v]))[0]
58enddef
59
60def Test_funcdepth_error()
61  set maxfuncdepth=10
62
63  var caught = false
64  try
65    CallRecursive(1)
66  catch /E132:/
67    caught = true
68  endtry
69  assert_true(caught)
70
71  caught = false
72  try
73    CallMapRecursive([1])
74  catch /E132:/
75    caught = true
76  endtry
77  assert_true(caught)
78
79  set maxfuncdepth&
80enddef
81
82def Test_endfunc_enddef()
83  var lines =<< trim END
84    def Test()
85      echo 'test'
86      endfunc
87    enddef
88  END
89  CheckScriptFailure(lines, 'E1151:', 3)
90
91  lines =<< trim END
92    def Test()
93      func Nested()
94        echo 'test'
95      enddef
96    enddef
97  END
98  CheckScriptFailure(lines, 'E1152:', 4)
99enddef
100
101def Test_missing_endfunc_enddef()
102  var lines =<< trim END
103    vim9script
104    def Test()
105      echo 'test'
106    endef
107  END
108  CheckScriptFailure(lines, 'E1057:', 2)
109
110  lines =<< trim END
111    vim9script
112    func Some()
113      echo 'test'
114    enfffunc
115  END
116  CheckScriptFailure(lines, 'E126:', 2)
117enddef
118
119def Test_white_space_before_paren()
120  var lines =<< trim END
121    vim9script
122    def Test ()
123      echo 'test'
124    enddef
125  END
126  CheckScriptFailure(lines, 'E1068:', 2)
127
128  lines =<< trim END
129    vim9script
130    func Test ()
131      echo 'test'
132    endfunc
133  END
134  CheckScriptFailure(lines, 'E1068:', 2)
135
136  lines =<< trim END
137    def Test ()
138      echo 'test'
139    enddef
140  END
141  CheckScriptFailure(lines, 'E1068:', 1)
142
143  lines =<< trim END
144    func Test ()
145      echo 'test'
146    endfunc
147  END
148  CheckScriptSuccess(lines)
149enddef
150
151def Test_enddef_dict_key()
152  var d = {
153    enddef: 'x',
154    endfunc: 'y',
155  }
156  assert_equal({enddef: 'x', endfunc: 'y'}, d)
157enddef
158
159def ReturnString(): string
160  return 'string'
161enddef
162
163def ReturnNumber(): number
164  return 123
165enddef
166
167let g:notNumber = 'string'
168
169def ReturnGlobal(): number
170  return g:notNumber
171enddef
172
173def Test_return_something()
174  ReturnString()->assert_equal('string')
175  ReturnNumber()->assert_equal(123)
176  assert_fails('ReturnGlobal()', 'E1012: Type mismatch; expected number but got string', '', 1, 'ReturnGlobal')
177enddef
178
179def Test_check_argument_type()
180  var lines =<< trim END
181      vim9script
182      def Val(a: number, b: number): number
183        return 0
184      enddef
185      def Func()
186        var x: any = true
187        Val(0, x)
188      enddef
189      disass Func
190      Func()
191  END
192  CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got bool', 2)
193enddef
194
195def Test_missing_return()
196  CheckDefFailure(['def Missing(): number',
197                   '  if g:cond',
198                   '    echo "no return"',
199                   '  else',
200                   '    return 0',
201                   '  endif'
202                   'enddef'], 'E1027:')
203  CheckDefFailure(['def Missing(): number',
204                   '  if g:cond',
205                   '    return 1',
206                   '  else',
207                   '    echo "no return"',
208                   '  endif'
209                   'enddef'], 'E1027:')
210  CheckDefFailure(['def Missing(): number',
211                   '  if g:cond',
212                   '    return 1',
213                   '  else',
214                   '    return 2',
215                   '  endif'
216                   '  return 3'
217                   'enddef'], 'E1095:')
218enddef
219
220def Test_return_bool()
221  var lines =<< trim END
222      vim9script
223      def MenuFilter(id: number, key: string): bool
224        return popup_filter_menu(id, key)
225      enddef
226      def YesnoFilter(id: number, key: string): bool
227        return popup_filter_yesno(id, key)
228      enddef
229      defcompile
230  END
231  CheckScriptSuccess(lines)
232enddef
233
234let s:nothing = 0
235def ReturnNothing()
236  s:nothing = 1
237  if true
238    return
239  endif
240  s:nothing = 2
241enddef
242
243def Test_return_nothing()
244  ReturnNothing()
245  s:nothing->assert_equal(1)
246enddef
247
248def Test_return_invalid()
249  var lines =<< trim END
250    vim9script
251    def Func(): invalid
252      return xxx
253    enddef
254    defcompile
255  END
256  CheckScriptFailure(lines, 'E1010:', 2)
257enddef
258
259func Increment()
260  let g:counter += 1
261endfunc
262
263def Test_call_ufunc_count()
264  g:counter = 1
265  Increment()
266  Increment()
267  Increment()
268  # works with and without :call
269  g:counter->assert_equal(4)
270  eval g:counter->assert_equal(4)
271  unlet g:counter
272enddef
273
274def MyVarargs(arg: string, ...rest: list<string>): string
275  var res = arg
276  for s in rest
277    res ..= ',' .. s
278  endfor
279  return res
280enddef
281
282def Test_call_varargs()
283  MyVarargs('one')->assert_equal('one')
284  MyVarargs('one', 'two')->assert_equal('one,two')
285  MyVarargs('one', 'two', 'three')->assert_equal('one,two,three')
286enddef
287
288def MyDefaultArgs(name = 'string'): string
289  return name
290enddef
291
292def MyDefaultSecond(name: string, second: bool  = true): string
293  return second ? name : 'none'
294enddef
295
296def Test_call_default_args()
297  MyDefaultArgs()->assert_equal('string')
298  MyDefaultArgs('one')->assert_equal('one')
299  assert_fails('MyDefaultArgs("one", "two")', 'E118:', '', 3, 'Test_call_default_args')
300
301  MyDefaultSecond('test')->assert_equal('test')
302  MyDefaultSecond('test', true)->assert_equal('test')
303  MyDefaultSecond('test', false)->assert_equal('none')
304
305  CheckScriptFailure(['def Func(arg: number = asdf)', 'enddef', 'defcompile'], 'E1001:')
306  delfunc g:Func
307  CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 'defcompile'], 'E1013: Argument 1: type mismatch, expected number but got string')
308  delfunc g:Func
309enddef
310
311def FuncWithComment(  # comment
312  a: number, #comment
313  b: bool, # comment
314  c: string) #comment
315  assert_equal(4, a)
316  assert_equal(true, b)
317  assert_equal('yes', c)
318enddef
319
320def Test_func_with_comments()
321  FuncWithComment(4, true, 'yes')
322
323  var lines =<< trim END
324      def Func(# comment
325        arg: string)
326      enddef
327  END
328  CheckScriptFailure(lines, 'E125:', 1)
329
330  lines =<< trim END
331      def Func(
332        arg: string# comment
333        )
334      enddef
335  END
336  CheckScriptFailure(lines, 'E475:', 2)
337
338  lines =<< trim END
339      def Func(
340        arg: string
341        )# comment
342      enddef
343  END
344  CheckScriptFailure(lines, 'E488:', 3)
345enddef
346
347def Test_nested_function()
348  def Nested(arg: string): string
349    return 'nested ' .. arg
350  enddef
351  Nested('function')->assert_equal('nested function')
352
353  CheckDefFailure(['def Nested()', 'enddef', 'Nested(66)'], 'E118:')
354  CheckDefFailure(['def Nested(arg: string)', 'enddef', 'Nested()'], 'E119:')
355
356  CheckDefFailure(['func Nested()', 'endfunc'], 'E1086:')
357  CheckDefFailure(['def s:Nested()', 'enddef'], 'E1075:')
358  CheckDefFailure(['def b:Nested()', 'enddef'], 'E1075:')
359
360  var lines =<< trim END
361      def Outer()
362        def Inner()
363          # comment
364        enddef
365        def Inner()
366        enddef
367      enddef
368  END
369  CheckDefFailure(lines, 'E1073:')
370
371  lines =<< trim END
372      def Outer()
373        def Inner()
374          # comment
375        enddef
376        def! Inner()
377        enddef
378      enddef
379  END
380  CheckDefFailure(lines, 'E1117:')
381
382  # nested function inside conditional
383  # TODO: should it work when "thecount" is inside the "if"?
384  lines =<< trim END
385      vim9script
386      var thecount = 0
387      if true
388        def Test(): number
389          def TheFunc(): number
390            thecount += 1
391            return thecount
392          enddef
393          return TheFunc()
394        enddef
395      endif
396      defcompile
397      assert_equal(1, Test())
398      assert_equal(2, Test())
399  END
400  CheckScriptSuccess(lines)
401enddef
402
403def Test_not_nested_function()
404  echo printf('%d',
405      function('len')('xxx'))
406enddef
407
408func Test_call_default_args_from_func()
409  call MyDefaultArgs()->assert_equal('string')
410  call MyDefaultArgs('one')->assert_equal('one')
411  call assert_fails('call MyDefaultArgs("one", "two")', 'E118:', '', 3, 'Test_call_default_args_from_func')
412endfunc
413
414def Test_nested_global_function()
415  var lines =<< trim END
416      vim9script
417      def Outer()
418          def g:Inner(): string
419              return 'inner'
420          enddef
421      enddef
422      defcompile
423      Outer()
424      g:Inner()->assert_equal('inner')
425      delfunc g:Inner
426      Outer()
427      g:Inner()->assert_equal('inner')
428      delfunc g:Inner
429      Outer()
430      g:Inner()->assert_equal('inner')
431      delfunc g:Inner
432  END
433  CheckScriptSuccess(lines)
434
435  lines =<< trim END
436      vim9script
437      def Outer()
438          def g:Inner(): string
439              return 'inner'
440          enddef
441      enddef
442      defcompile
443      Outer()
444      Outer()
445  END
446  CheckScriptFailure(lines, "E122:")
447  delfunc g:Inner
448
449  lines =<< trim END
450      vim9script
451      def Outer()
452        def g:Inner()
453          echo map([1, 2, 3], (_, v) => v + 1)
454        enddef
455        g:Inner()
456      enddef
457      Outer()
458  END
459  CheckScriptSuccess(lines)
460  delfunc g:Inner
461
462  lines =<< trim END
463      vim9script
464      def Func()
465        echo 'script'
466      enddef
467      def Outer()
468        def Func()
469          echo 'inner'
470        enddef
471      enddef
472      defcompile
473  END
474  CheckScriptFailure(lines, "E1073:")
475enddef
476
477def DefListAll()
478  def
479enddef
480
481def DefListOne()
482  def DefListOne
483enddef
484
485def DefListMatches()
486  def /DefList
487enddef
488
489def Test_nested_def_list()
490  var funcs = split(execute('call DefListAll()'), "\n")
491  assert_true(len(funcs) > 10)
492  assert_true(funcs->index('def DefListAll()') >= 0)
493
494  funcs = split(execute('call DefListOne()'), "\n")
495  assert_equal(['   def DefListOne()', '1    def DefListOne', '   enddef'], funcs)
496
497  funcs = split(execute('call DefListMatches()'), "\n")
498  assert_true(len(funcs) >= 3)
499  assert_true(funcs->index('def DefListAll()') >= 0)
500  assert_true(funcs->index('def DefListOne()') >= 0)
501  assert_true(funcs->index('def DefListMatches()') >= 0)
502
503  var lines =<< trim END
504    vim9script
505    def Func()
506      def +Func+
507    enddef
508    defcompile
509  END
510  CheckScriptFailure(lines, 'E476:', 1)
511enddef
512
513def Test_global_local_function()
514  var lines =<< trim END
515      vim9script
516      def g:Func(): string
517          return 'global'
518      enddef
519      def Func(): string
520          return 'local'
521      enddef
522      g:Func()->assert_equal('global')
523      Func()->assert_equal('local')
524      delfunc g:Func
525  END
526  CheckScriptSuccess(lines)
527
528  lines =<< trim END
529      vim9script
530      def g:Funcy()
531        echo 'funcy'
532      enddef
533      s:Funcy()
534  END
535  CheckScriptFailure(lines, 'E117:')
536enddef
537
538def Test_local_function_shadows_global()
539  var lines =<< trim END
540      vim9script
541      def g:Gfunc(): string
542        return 'global'
543      enddef
544      def AnotherFunc(): number
545        var Gfunc = function('len')
546        return Gfunc('testing')
547      enddef
548      g:Gfunc()->assert_equal('global')
549      AnotherFunc()->assert_equal(7)
550      delfunc g:Gfunc
551  END
552  CheckScriptSuccess(lines)
553
554  lines =<< trim END
555      vim9script
556      def g:Func(): string
557        return 'global'
558      enddef
559      def AnotherFunc()
560        g:Func = function('len')
561      enddef
562      AnotherFunc()
563  END
564  CheckScriptFailure(lines, 'E705:')
565  delfunc g:Func
566enddef
567
568func TakesOneArg(arg)
569  echo a:arg
570endfunc
571
572def Test_call_wrong_args()
573  CheckDefFailure(['TakesOneArg()'], 'E119:')
574  CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:')
575  CheckDefFailure(['bufnr(xxx)'], 'E1001:')
576  CheckScriptFailure(['def Func(Ref: func(s: string))'], 'E475:')
577
578  var lines =<< trim END
579    vim9script
580    def Func(s: string)
581      echo s
582    enddef
583    Func([])
584  END
585  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got list<unknown>', 5)
586
587  lines =<< trim END
588    vim9script
589    def FuncOne(nr: number)
590      echo nr
591    enddef
592    def FuncTwo()
593      FuncOne()
594    enddef
595    defcompile
596  END
597  writefile(lines, 'Xscript')
598  var didCatch = false
599  try
600    source Xscript
601  catch
602    assert_match('E119: Not enough arguments for function: <SNR>\d\+_FuncOne', v:exception)
603    assert_match('Xscript\[8\]..function <SNR>\d\+_FuncTwo, line 1', v:throwpoint)
604    didCatch = true
605  endtry
606  assert_true(didCatch)
607
608  lines =<< trim END
609    vim9script
610    def FuncOne(nr: number)
611      echo nr
612    enddef
613    def FuncTwo()
614      FuncOne(1, 2)
615    enddef
616    defcompile
617  END
618  writefile(lines, 'Xscript')
619  didCatch = false
620  try
621    source Xscript
622  catch
623    assert_match('E118: Too many arguments for function: <SNR>\d\+_FuncOne', v:exception)
624    assert_match('Xscript\[8\]..function <SNR>\d\+_FuncTwo, line 1', v:throwpoint)
625    didCatch = true
626  endtry
627  assert_true(didCatch)
628
629  delete('Xscript')
630enddef
631
632def Test_call_funcref_wrong_args()
633  var head =<< trim END
634      vim9script
635      def Func3(a1: string, a2: number, a3: list<number>)
636        echo a1 .. a2 .. a3[0]
637      enddef
638      def Testme()
639        var funcMap: dict<func> = {func: Func3}
640  END
641  var tail =<< trim END
642      enddef
643      Testme()
644  END
645  CheckScriptSuccess(head + ["funcMap['func']('str', 123, [1, 2, 3])"] + tail)
646
647  CheckScriptFailure(head + ["funcMap['func']('str', 123)"] + tail, 'E119:')
648  CheckScriptFailure(head + ["funcMap['func']('str', 123, [1], 4)"] + tail, 'E118:')
649
650  var lines =<< trim END
651      vim9script
652      var Ref: func(number): any
653      Ref = (j) => !j
654      echo Ref(false)
655  END
656  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
657
658  lines =<< trim END
659      vim9script
660      var Ref: func(number): any
661      Ref = (j) => !j
662      call Ref(false)
663  END
664  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected number but got bool', 4)
665enddef
666
667def Test_call_lambda_args()
668  CheckDefFailure(['echo ((i) => 0)()'],
669                  'E119: Not enough arguments for function: ((i) => 0)()')
670
671  var lines =<< trim END
672      var Ref = (x: number, y: number) => x + y
673      echo Ref(1, 'x')
674  END
675  CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got string')
676
677  lines =<< trim END
678    var Ref: func(job, string, number)
679    Ref = (x, y) => 0
680  END
681  CheckDefAndScriptFailure(lines, 'E1012:')
682
683  lines =<< trim END
684    var Ref: func(job, string)
685    Ref = (x, y, z) => 0
686  END
687  CheckDefAndScriptFailure(lines, 'E1012:')
688enddef
689
690def Test_lambda_uses_assigned_var()
691  CheckDefSuccess([
692        'var x: any = "aaa"'
693        'x = filter(["bbb"], (_, v) => v =~ x)'])
694enddef
695
696" Default arg and varargs
697def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
698  var res = one .. ',' .. two
699  for s in rest
700    res ..= ',' .. s
701  endfor
702  return res
703enddef
704
705def Test_call_def_varargs()
706  assert_fails('MyDefVarargs()', 'E119:', '', 1, 'Test_call_def_varargs')
707  MyDefVarargs('one')->assert_equal('one,foo')
708  MyDefVarargs('one', 'two')->assert_equal('one,two')
709  MyDefVarargs('one', 'two', 'three')->assert_equal('one,two,three')
710  CheckDefFailure(['MyDefVarargs("one", 22)'],
711      'E1013: Argument 2: type mismatch, expected string but got number')
712  CheckDefFailure(['MyDefVarargs("one", "two", 123)'],
713      'E1013: Argument 3: type mismatch, expected string but got number')
714
715  var lines =<< trim END
716      vim9script
717      def Func(...l: list<string>)
718        echo l
719      enddef
720      Func('a', 'b', 'c')
721  END
722  CheckScriptSuccess(lines)
723
724  lines =<< trim END
725      vim9script
726      def Func(...l: list<string>)
727        echo l
728      enddef
729      Func()
730  END
731  CheckScriptSuccess(lines)
732
733  lines =<< trim END
734      vim9script
735      def Func(...l: any)
736        echo l
737      enddef
738      Func(0)
739  END
740  CheckScriptSuccess(lines)
741
742  lines =<< trim END
743      vim9script
744      def Func(..._l: list<string>)
745        echo _l
746      enddef
747      Func('a', 'b', 'c')
748  END
749  CheckScriptSuccess(lines)
750
751  lines =<< trim END
752      vim9script
753      def Func(...l: list<string>)
754        echo l
755      enddef
756      Func(1, 2, 3)
757  END
758  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch')
759
760  lines =<< trim END
761      vim9script
762      def Func(...l: list<string>)
763        echo l
764      enddef
765      Func('a', 9)
766  END
767  CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch')
768
769  lines =<< trim END
770      vim9script
771      def Func(...l: list<string>)
772        echo l
773      enddef
774      Func(1, 'a')
775  END
776  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch')
777enddef
778
779let s:value = ''
780
781def FuncOneDefArg(opt = 'text')
782  s:value = opt
783enddef
784
785def FuncTwoDefArg(nr = 123, opt = 'text'): string
786  return nr .. opt
787enddef
788
789def FuncVarargs(...arg: list<string>): string
790  return join(arg, ',')
791enddef
792
793def Test_func_type_varargs()
794  var RefDefArg: func(?string)
795  RefDefArg = FuncOneDefArg
796  RefDefArg()
797  s:value->assert_equal('text')
798  RefDefArg('some')
799  s:value->assert_equal('some')
800
801  var RefDef2Arg: func(?number, ?string): string
802  RefDef2Arg = FuncTwoDefArg
803  RefDef2Arg()->assert_equal('123text')
804  RefDef2Arg(99)->assert_equal('99text')
805  RefDef2Arg(77, 'some')->assert_equal('77some')
806
807  CheckDefFailure(['var RefWrong: func(string?)'], 'E1010:')
808  CheckDefFailure(['var RefWrong: func(?string, string)'], 'E1007:')
809
810  var RefVarargs: func(...list<string>): string
811  RefVarargs = FuncVarargs
812  RefVarargs()->assert_equal('')
813  RefVarargs('one')->assert_equal('one')
814  RefVarargs('one', 'two')->assert_equal('one,two')
815
816  CheckDefFailure(['var RefWrong: func(...list<string>, string)'], 'E110:')
817  CheckDefFailure(['var RefWrong: func(...list<string>, ?string)'], 'E110:')
818enddef
819
820" Only varargs
821def MyVarargsOnly(...args: list<string>): string
822  return join(args, ',')
823enddef
824
825def Test_call_varargs_only()
826  MyVarargsOnly()->assert_equal('')
827  MyVarargsOnly('one')->assert_equal('one')
828  MyVarargsOnly('one', 'two')->assert_equal('one,two')
829  CheckDefFailure(['MyVarargsOnly(1)'], 'E1013: Argument 1: type mismatch, expected string but got number')
830  CheckDefFailure(['MyVarargsOnly("one", 2)'], 'E1013: Argument 2: type mismatch, expected string but got number')
831enddef
832
833def Test_using_var_as_arg()
834  writefile(['def Func(x: number)',  'var x = 234', 'enddef', 'defcompile'], 'Xdef')
835  assert_fails('so Xdef', 'E1006:', '', 1, 'Func')
836  delete('Xdef')
837enddef
838
839def DictArg(arg: dict<string>)
840  arg['key'] = 'value'
841enddef
842
843def ListArg(arg: list<string>)
844  arg[0] = 'value'
845enddef
846
847def Test_assign_to_argument()
848  # works for dict and list
849  var d: dict<string> = {}
850  DictArg(d)
851  d['key']->assert_equal('value')
852  var l: list<string> = []
853  ListArg(l)
854  l[0]->assert_equal('value')
855
856  CheckScriptFailure(['def Func(arg: number)', 'arg = 3', 'enddef', 'defcompile'], 'E1090:')
857  delfunc! g:Func
858enddef
859
860" These argument names are reserved in legacy functions.
861def WithReservedNames(firstline: string, lastline: string): string
862  return firstline .. lastline
863enddef
864
865def Test_argument_names()
866  assert_equal('OK', WithReservedNames('O', 'K'))
867enddef
868
869def Test_call_func_defined_later()
870  g:DefinedLater('one')->assert_equal('one')
871  assert_fails('NotDefined("one")', 'E117:', '', 2, 'Test_call_func_defined_later')
872enddef
873
874func DefinedLater(arg)
875  return a:arg
876endfunc
877
878def Test_call_funcref()
879  g:SomeFunc('abc')->assert_equal(3)
880  assert_fails('NotAFunc()', 'E117:', '', 2, 'Test_call_funcref') # comment after call
881  assert_fails('g:NotAFunc()', 'E117:', '', 3, 'Test_call_funcref')
882
883  var lines =<< trim END
884    vim9script
885    def RetNumber(): number
886      return 123
887    enddef
888    var Funcref: func: number = function('RetNumber')
889    Funcref()->assert_equal(123)
890  END
891  CheckScriptSuccess(lines)
892
893  lines =<< trim END
894    vim9script
895    def RetNumber(): number
896      return 123
897    enddef
898    def Bar(F: func: number): number
899      return F()
900    enddef
901    var Funcref = function('RetNumber')
902    Bar(Funcref)->assert_equal(123)
903  END
904  CheckScriptSuccess(lines)
905
906  lines =<< trim END
907    vim9script
908    def UseNumber(nr: number)
909      echo nr
910    enddef
911    var Funcref: func(number) = function('UseNumber')
912    Funcref(123)
913  END
914  CheckScriptSuccess(lines)
915
916  lines =<< trim END
917    vim9script
918    def UseNumber(nr: number)
919      echo nr
920    enddef
921    var Funcref: func(string) = function('UseNumber')
922  END
923  CheckScriptFailure(lines, 'E1012: Type mismatch; expected func(string) but got func(number)')
924
925  lines =<< trim END
926    vim9script
927    def EchoNr(nr = 34)
928      g:echo = nr
929    enddef
930    var Funcref: func(?number) = function('EchoNr')
931    Funcref()
932    g:echo->assert_equal(34)
933    Funcref(123)
934    g:echo->assert_equal(123)
935  END
936  CheckScriptSuccess(lines)
937
938  lines =<< trim END
939    vim9script
940    def EchoList(...l: list<number>)
941      g:echo = l
942    enddef
943    var Funcref: func(...list<number>) = function('EchoList')
944    Funcref()
945    g:echo->assert_equal([])
946    Funcref(1, 2, 3)
947    g:echo->assert_equal([1, 2, 3])
948  END
949  CheckScriptSuccess(lines)
950
951  lines =<< trim END
952    vim9script
953    def OptAndVar(nr: number, opt = 12, ...l: list<number>): number
954      g:optarg = opt
955      g:listarg = l
956      return nr
957    enddef
958    var Funcref: func(number, ?number, ...list<number>): number = function('OptAndVar')
959    Funcref(10)->assert_equal(10)
960    g:optarg->assert_equal(12)
961    g:listarg->assert_equal([])
962
963    Funcref(11, 22)->assert_equal(11)
964    g:optarg->assert_equal(22)
965    g:listarg->assert_equal([])
966
967    Funcref(17, 18, 1, 2, 3)->assert_equal(17)
968    g:optarg->assert_equal(18)
969    g:listarg->assert_equal([1, 2, 3])
970  END
971  CheckScriptSuccess(lines)
972enddef
973
974let SomeFunc = function('len')
975let NotAFunc = 'text'
976
977def CombineFuncrefTypes()
978  # same arguments, different return type
979  var Ref1: func(bool): string
980  var Ref2: func(bool): number
981  var Ref3: func(bool): any
982  Ref3 = g:cond ? Ref1 : Ref2
983
984  # different number of arguments
985  var Refa1: func(bool): number
986  var Refa2: func(bool, number): number
987  var Refa3: func: number
988  Refa3 = g:cond ? Refa1 : Refa2
989
990  # different argument types
991  var Refb1: func(bool, string): number
992  var Refb2: func(string, number): number
993  var Refb3: func(any, any): number
994  Refb3 = g:cond ? Refb1 : Refb2
995enddef
996
997def FuncWithForwardCall()
998  return g:DefinedEvenLater("yes")
999enddef
1000
1001def DefinedEvenLater(arg: string): string
1002  return arg
1003enddef
1004
1005def Test_error_in_nested_function()
1006  # Error in called function requires unwinding the call stack.
1007  assert_fails('FuncWithForwardCall()', 'E1096:', '', 1, 'FuncWithForwardCall')
1008enddef
1009
1010def Test_return_type_wrong()
1011  CheckScriptFailure([
1012        'def Func(): number',
1013        'return "a"',
1014        'enddef',
1015        'defcompile'], 'expected number but got string')
1016  delfunc! g:Func
1017  CheckScriptFailure([
1018        'def Func(): string',
1019        'return 1',
1020        'enddef',
1021        'defcompile'], 'expected string but got number')
1022  delfunc! g:Func
1023  CheckScriptFailure([
1024        'def Func(): void',
1025        'return "a"',
1026        'enddef',
1027        'defcompile'],
1028        'E1096: Returning a value in a function without a return type')
1029  delfunc! g:Func
1030  CheckScriptFailure([
1031        'def Func()',
1032        'return "a"',
1033        'enddef',
1034        'defcompile'],
1035        'E1096: Returning a value in a function without a return type')
1036  delfunc! g:Func
1037
1038  CheckScriptFailure([
1039        'def Func(): number',
1040        'return',
1041        'enddef',
1042        'defcompile'], 'E1003:')
1043  delfunc! g:Func
1044
1045  CheckScriptFailure(['def Func(): list', 'return []', 'enddef'], 'E1008:')
1046  delfunc! g:Func
1047  CheckScriptFailure(['def Func(): dict', 'return {}', 'enddef'], 'E1008:')
1048  delfunc! g:Func
1049  CheckScriptFailure(['def Func()', 'return 1'], 'E1057:')
1050  delfunc! g:Func
1051
1052  CheckScriptFailure([
1053        'vim9script',
1054        'def FuncB()',
1055        '  return 123',
1056        'enddef',
1057        'def FuncA()',
1058        '   FuncB()',
1059        'enddef',
1060        'defcompile'], 'E1096:')
1061enddef
1062
1063def Test_arg_type_wrong()
1064  CheckScriptFailure(['def Func3(items: list)', 'echo "a"', 'enddef'], 'E1008: Missing <type>')
1065  CheckScriptFailure(['def Func4(...)', 'echo "a"', 'enddef'], 'E1055: Missing name after ...')
1066  CheckScriptFailure(['def Func5(items:string)', 'echo "a"'], 'E1069:')
1067  CheckScriptFailure(['def Func5(items)', 'echo "a"'], 'E1077:')
1068enddef
1069
1070def Test_vim9script_call()
1071  var lines =<< trim END
1072    vim9script
1073    var name = ''
1074    def MyFunc(arg: string)
1075       name = arg
1076    enddef
1077    MyFunc('foobar')
1078    name->assert_equal('foobar')
1079
1080    var str = 'barfoo'
1081    str->MyFunc()
1082    name->assert_equal('barfoo')
1083
1084    g:value = 'value'
1085    g:value->MyFunc()
1086    name->assert_equal('value')
1087
1088    var listvar = []
1089    def ListFunc(arg: list<number>)
1090       listvar = arg
1091    enddef
1092    [1, 2, 3]->ListFunc()
1093    listvar->assert_equal([1, 2, 3])
1094
1095    var dictvar = {}
1096    def DictFunc(arg: dict<number>)
1097       dictvar = arg
1098    enddef
1099    {a: 1, b: 2}->DictFunc()
1100    dictvar->assert_equal({a: 1, b: 2})
1101    def CompiledDict()
1102      {a: 3, b: 4}->DictFunc()
1103    enddef
1104    CompiledDict()
1105    dictvar->assert_equal({a: 3, b: 4})
1106
1107    {a: 3, b: 4}->DictFunc()
1108    dictvar->assert_equal({a: 3, b: 4})
1109
1110    ('text')->MyFunc()
1111    name->assert_equal('text')
1112    ("some")->MyFunc()
1113    name->assert_equal('some')
1114
1115    # line starting with single quote is not a mark
1116    # line starting with double quote can be a method call
1117    'asdfasdf'->MyFunc()
1118    name->assert_equal('asdfasdf')
1119    "xyz"->MyFunc()
1120    name->assert_equal('xyz')
1121
1122    def UseString()
1123      'xyork'->MyFunc()
1124    enddef
1125    UseString()
1126    name->assert_equal('xyork')
1127
1128    def UseString2()
1129      "knife"->MyFunc()
1130    enddef
1131    UseString2()
1132    name->assert_equal('knife')
1133
1134    # prepending a colon makes it a mark
1135    new
1136    setline(1, ['aaa', 'bbb', 'ccc'])
1137    normal! 3Gmt1G
1138    :'t
1139    getcurpos()[1]->assert_equal(3)
1140    bwipe!
1141
1142    MyFunc(
1143        'continued'
1144        )
1145    assert_equal('continued',
1146            name
1147            )
1148
1149    call MyFunc(
1150        'more'
1151          ..
1152          'lines'
1153        )
1154    assert_equal(
1155        'morelines',
1156        name)
1157  END
1158  writefile(lines, 'Xcall.vim')
1159  source Xcall.vim
1160  delete('Xcall.vim')
1161enddef
1162
1163def Test_vim9script_call_fail_decl()
1164  var lines =<< trim END
1165    vim9script
1166    var name = ''
1167    def MyFunc(arg: string)
1168       var name = 123
1169    enddef
1170    defcompile
1171  END
1172  CheckScriptFailure(lines, 'E1054:')
1173enddef
1174
1175def Test_vim9script_call_fail_type()
1176  var lines =<< trim END
1177    vim9script
1178    def MyFunc(arg: string)
1179      echo arg
1180    enddef
1181    MyFunc(1234)
1182  END
1183  CheckScriptFailure(lines, 'E1013: Argument 1: type mismatch, expected string but got number')
1184enddef
1185
1186def Test_vim9script_call_fail_const()
1187  var lines =<< trim END
1188    vim9script
1189    const var = ''
1190    def MyFunc(arg: string)
1191       var = 'asdf'
1192    enddef
1193    defcompile
1194  END
1195  writefile(lines, 'Xcall_const.vim')
1196  assert_fails('source Xcall_const.vim', 'E46:', '', 1, 'MyFunc')
1197  delete('Xcall_const.vim')
1198
1199  lines =<< trim END
1200      const g:Aconst = 77
1201      def Change()
1202        # comment
1203        g:Aconst = 99
1204      enddef
1205      call Change()
1206      unlet g:Aconst
1207  END
1208  CheckScriptFailure(lines, 'E741: Value is locked: Aconst', 2)
1209enddef
1210
1211" Test that inside :function a Python function can be defined, :def is not
1212" recognized.
1213func Test_function_python()
1214  CheckFeature python3
1215  let py = 'python3'
1216  execute py "<< EOF"
1217def do_something():
1218  return 1
1219EOF
1220endfunc
1221
1222def Test_delfunc()
1223  var lines =<< trim END
1224    vim9script
1225    def g:GoneSoon()
1226      echo 'hello'
1227    enddef
1228
1229    def CallGoneSoon()
1230      GoneSoon()
1231    enddef
1232    defcompile
1233
1234    delfunc g:GoneSoon
1235    CallGoneSoon()
1236  END
1237  writefile(lines, 'XToDelFunc')
1238  assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon')
1239  assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon')
1240
1241  delete('XToDelFunc')
1242enddef
1243
1244def Test_redef_failure()
1245  writefile(['def Func0(): string',  'return "Func0"', 'enddef'], 'Xdef')
1246  so Xdef
1247  writefile(['def Func1(): string',  'return "Func1"', 'enddef'], 'Xdef')
1248  so Xdef
1249  writefile(['def! Func0(): string', 'enddef', 'defcompile'], 'Xdef')
1250  assert_fails('so Xdef', 'E1027:', '', 1, 'Func0')
1251  writefile(['def Func2(): string',  'return "Func2"', 'enddef'], 'Xdef')
1252  so Xdef
1253  delete('Xdef')
1254
1255  g:Func0()->assert_equal(0)
1256  g:Func1()->assert_equal('Func1')
1257  g:Func2()->assert_equal('Func2')
1258
1259  delfunc! Func0
1260  delfunc! Func1
1261  delfunc! Func2
1262enddef
1263
1264def Test_vim9script_func()
1265  var lines =<< trim END
1266    vim9script
1267    func Func(arg)
1268      echo a:arg
1269    endfunc
1270    Func('text')
1271  END
1272  writefile(lines, 'XVim9Func')
1273  so XVim9Func
1274
1275  delete('XVim9Func')
1276enddef
1277
1278let s:funcResult = 0
1279
1280def FuncNoArgNoRet()
1281  s:funcResult = 11
1282enddef
1283
1284def FuncNoArgRetNumber(): number
1285  s:funcResult = 22
1286  return 1234
1287enddef
1288
1289def FuncNoArgRetString(): string
1290  s:funcResult = 45
1291  return 'text'
1292enddef
1293
1294def FuncOneArgNoRet(arg: number)
1295  s:funcResult = arg
1296enddef
1297
1298def FuncOneArgRetNumber(arg: number): number
1299  s:funcResult = arg
1300  return arg
1301enddef
1302
1303def FuncTwoArgNoRet(one: bool, two: number)
1304  s:funcResult = two
1305enddef
1306
1307def FuncOneArgRetString(arg: string): string
1308  return arg
1309enddef
1310
1311def FuncOneArgRetAny(arg: any): any
1312  return arg
1313enddef
1314
1315def Test_func_type()
1316  var Ref1: func()
1317  s:funcResult = 0
1318  Ref1 = FuncNoArgNoRet
1319  Ref1()
1320  s:funcResult->assert_equal(11)
1321
1322  var Ref2: func
1323  s:funcResult = 0
1324  Ref2 = FuncNoArgNoRet
1325  Ref2()
1326  s:funcResult->assert_equal(11)
1327
1328  s:funcResult = 0
1329  Ref2 = FuncOneArgNoRet
1330  Ref2(12)
1331  s:funcResult->assert_equal(12)
1332
1333  s:funcResult = 0
1334  Ref2 = FuncNoArgRetNumber
1335  Ref2()->assert_equal(1234)
1336  s:funcResult->assert_equal(22)
1337
1338  s:funcResult = 0
1339  Ref2 = FuncOneArgRetNumber
1340  Ref2(13)->assert_equal(13)
1341  s:funcResult->assert_equal(13)
1342enddef
1343
1344def Test_repeat_return_type()
1345  var res = 0
1346  for n in repeat([1], 3)
1347    res += n
1348  endfor
1349  res->assert_equal(3)
1350
1351  res = 0
1352  for n in add([1, 2], 3)
1353    res += n
1354  endfor
1355  res->assert_equal(6)
1356enddef
1357
1358def Test_argv_return_type()
1359  next fileone filetwo
1360  var res = ''
1361  for name in argv()
1362    res ..= name
1363  endfor
1364  res->assert_equal('fileonefiletwo')
1365enddef
1366
1367def Test_func_type_part()
1368  var RefVoid: func: void
1369  RefVoid = FuncNoArgNoRet
1370  RefVoid = FuncOneArgNoRet
1371  CheckDefFailure(['var RefVoid: func: void', 'RefVoid = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...) but got func(): number')
1372  CheckDefFailure(['var RefVoid: func: void', 'RefVoid = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...) but got func(): string')
1373
1374  var RefAny: func(): any
1375  RefAny = FuncNoArgRetNumber
1376  RefAny = FuncNoArgRetString
1377  CheckDefFailure(['var RefAny: func(): any', 'RefAny = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func()')
1378  CheckDefFailure(['var RefAny: func(): any', 'RefAny = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func(): any but got func(number)')
1379
1380  var RefAnyNoArgs: func: any = RefAny
1381
1382  var RefNr: func: number
1383  RefNr = FuncNoArgRetNumber
1384  RefNr = FuncOneArgRetNumber
1385  CheckDefFailure(['var RefNr: func: number', 'RefNr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): number but got func()')
1386  CheckDefFailure(['var RefNr: func: number', 'RefNr = FuncNoArgRetString'], 'E1012: Type mismatch; expected func(...): number but got func(): string')
1387
1388  var RefStr: func: string
1389  RefStr = FuncNoArgRetString
1390  RefStr = FuncOneArgRetString
1391  CheckDefFailure(['var RefStr: func: string', 'RefStr = FuncNoArgNoRet'], 'E1012: Type mismatch; expected func(...): string but got func()')
1392  CheckDefFailure(['var RefStr: func: string', 'RefStr = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func(...): string but got func(): number')
1393enddef
1394
1395def Test_func_type_fails()
1396  CheckDefFailure(['var ref1: func()'], 'E704:')
1397
1398  CheckDefFailure(['var Ref1: func()', 'Ref1 = FuncNoArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(): number')
1399  CheckDefFailure(['var Ref1: func()', 'Ref1 = FuncOneArgNoRet'], 'E1012: Type mismatch; expected func() but got func(number)')
1400  CheckDefFailure(['var Ref1: func()', 'Ref1 = FuncOneArgRetNumber'], 'E1012: Type mismatch; expected func() but got func(number): number')
1401  CheckDefFailure(['var Ref1: func(bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(bool) but got func(bool, number)')
1402  CheckDefFailure(['var Ref1: func(?bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(?bool) but got func(bool, number)')
1403  CheckDefFailure(['var Ref1: func(...bool)', 'Ref1 = FuncTwoArgNoRet'], 'E1012: Type mismatch; expected func(...bool) but got func(bool, number)')
1404
1405  CheckDefFailure(['var RefWrong: func(string ,number)'], 'E1068:')
1406  CheckDefFailure(['var RefWrong: func(string,number)'], 'E1069:')
1407  CheckDefFailure(['var RefWrong: func(bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool)'], 'E1005:')
1408  CheckDefFailure(['var RefWrong: func(bool):string'], 'E1069:')
1409enddef
1410
1411def Test_func_return_type()
1412  var nr: number
1413  nr = FuncNoArgRetNumber()
1414  nr->assert_equal(1234)
1415
1416  nr = FuncOneArgRetAny(122)
1417  nr->assert_equal(122)
1418
1419  var str: string
1420  str = FuncOneArgRetAny('yes')
1421  str->assert_equal('yes')
1422
1423  CheckDefFailure(['var str: string', 'str = FuncNoArgRetNumber()'], 'E1012: Type mismatch; expected string but got number')
1424enddef
1425
1426def Test_func_common_type()
1427  def FuncOne(n: number): number
1428    return n
1429  enddef
1430  def FuncTwo(s: string): number
1431    return len(s)
1432  enddef
1433  def FuncThree(n: number, s: string): number
1434    return n + len(s)
1435  enddef
1436  var list = [FuncOne, FuncTwo, FuncThree]
1437  assert_equal(8, list[0](8))
1438  assert_equal(4, list[1]('word'))
1439  assert_equal(7, list[2](3, 'word'))
1440enddef
1441
1442def MultiLine(
1443    arg1: string,
1444    arg2 = 1234,
1445    ...rest: list<string>
1446      ): string
1447  return arg1 .. arg2 .. join(rest, '-')
1448enddef
1449
1450def MultiLineComment(
1451    arg1: string, # comment
1452    arg2 = 1234, # comment
1453    ...rest: list<string> # comment
1454      ): string # comment
1455  return arg1 .. arg2 .. join(rest, '-')
1456enddef
1457
1458def Test_multiline()
1459  MultiLine('text')->assert_equal('text1234')
1460  MultiLine('text', 777)->assert_equal('text777')
1461  MultiLine('text', 777, 'one')->assert_equal('text777one')
1462  MultiLine('text', 777, 'one', 'two')->assert_equal('text777one-two')
1463enddef
1464
1465func Test_multiline_not_vim9()
1466  call MultiLine('text')->assert_equal('text1234')
1467  call MultiLine('text', 777)->assert_equal('text777')
1468  call MultiLine('text', 777, 'one')->assert_equal('text777one')
1469  call MultiLine('text', 777, 'one', 'two')->assert_equal('text777one-two')
1470endfunc
1471
1472
1473" When using CheckScriptFailure() for the below test, E1010 is generated instead
1474" of E1056.
1475func Test_E1056_1059()
1476  let caught_1056 = 0
1477  try
1478    def F():
1479      return 1
1480    enddef
1481  catch /E1056:/
1482    let caught_1056 = 1
1483  endtry
1484  eval caught_1056->assert_equal(1)
1485
1486  let caught_1059 = 0
1487  try
1488    def F5(items : list)
1489      echo 'a'
1490    enddef
1491  catch /E1059:/
1492    let caught_1059 = 1
1493  endtry
1494  eval caught_1059->assert_equal(1)
1495endfunc
1496
1497func DelMe()
1498  echo 'DelMe'
1499endfunc
1500
1501def Test_error_reporting()
1502  # comment lines at the start of the function
1503  var lines =<< trim END
1504    " comment
1505    def Func()
1506      # comment
1507      # comment
1508      invalid
1509    enddef
1510    defcompile
1511  END
1512  writefile(lines, 'Xdef')
1513  try
1514    source Xdef
1515    assert_report('should have failed')
1516  catch /E476:/
1517    v:exception->assert_match('Invalid command: invalid')
1518    v:throwpoint->assert_match(', line 3$')
1519  endtry
1520  delfunc! g:Func
1521
1522  # comment lines after the start of the function
1523  lines =<< trim END
1524    " comment
1525    def Func()
1526      var x = 1234
1527      # comment
1528      # comment
1529      invalid
1530    enddef
1531    defcompile
1532  END
1533  writefile(lines, 'Xdef')
1534  try
1535    source Xdef
1536    assert_report('should have failed')
1537  catch /E476:/
1538    v:exception->assert_match('Invalid command: invalid')
1539    v:throwpoint->assert_match(', line 4$')
1540  endtry
1541  delfunc! g:Func
1542
1543  lines =<< trim END
1544    vim9script
1545    def Func()
1546      var db = {foo: 1, bar: 2}
1547      # comment
1548      var x = db.asdf
1549    enddef
1550    defcompile
1551    Func()
1552  END
1553  writefile(lines, 'Xdef')
1554  try
1555    source Xdef
1556    assert_report('should have failed')
1557  catch /E716:/
1558    v:throwpoint->assert_match('_Func, line 3$')
1559  endtry
1560  delfunc! g:Func
1561
1562  delete('Xdef')
1563enddef
1564
1565def Test_deleted_function()
1566  CheckDefExecFailure([
1567      'var RefMe: func = function("g:DelMe")',
1568      'delfunc g:DelMe',
1569      'echo RefMe()'], 'E117:')
1570enddef
1571
1572def Test_unknown_function()
1573  CheckDefExecFailure([
1574      'var Ref: func = function("NotExist")',
1575      'delfunc g:NotExist'], 'E700:')
1576enddef
1577
1578def RefFunc(Ref: func(any): any): string
1579  return Ref('more')
1580enddef
1581
1582def Test_closure_simple()
1583  var local = 'some '
1584  RefFunc((s) => local .. s)->assert_equal('some more')
1585enddef
1586
1587def MakeRef()
1588  var local = 'some '
1589  g:Ref = (s) => local .. s
1590enddef
1591
1592def Test_closure_ref_after_return()
1593  MakeRef()
1594  g:Ref('thing')->assert_equal('some thing')
1595  unlet g:Ref
1596enddef
1597
1598def MakeTwoRefs()
1599  var local = ['some']
1600  g:Extend = (s) => local->add(s)
1601  g:Read = () => local
1602enddef
1603
1604def Test_closure_two_refs()
1605  MakeTwoRefs()
1606  join(g:Read(), ' ')->assert_equal('some')
1607  g:Extend('more')
1608  join(g:Read(), ' ')->assert_equal('some more')
1609  g:Extend('even')
1610  join(g:Read(), ' ')->assert_equal('some more even')
1611
1612  unlet g:Extend
1613  unlet g:Read
1614enddef
1615
1616def ReadRef(Ref: func(): list<string>): string
1617  return join(Ref(), ' ')
1618enddef
1619
1620def ExtendRef(Ref: func(string): list<string>, add: string)
1621  Ref(add)
1622enddef
1623
1624def Test_closure_two_indirect_refs()
1625  MakeTwoRefs()
1626  ReadRef(g:Read)->assert_equal('some')
1627  ExtendRef(g:Extend, 'more')
1628  ReadRef(g:Read)->assert_equal('some more')
1629  ExtendRef(g:Extend, 'even')
1630  ReadRef(g:Read)->assert_equal('some more even')
1631
1632  unlet g:Extend
1633  unlet g:Read
1634enddef
1635
1636def MakeArgRefs(theArg: string)
1637  var local = 'loc_val'
1638  g:UseArg = (s) => theArg .. '/' .. local .. '/' .. s
1639enddef
1640
1641def MakeArgRefsVarargs(theArg: string, ...rest: list<string>)
1642  var local = 'the_loc'
1643  g:UseVararg = (s) => theArg .. '/' .. local .. '/' .. s .. '/' .. join(rest)
1644enddef
1645
1646def Test_closure_using_argument()
1647  MakeArgRefs('arg_val')
1648  g:UseArg('call_val')->assert_equal('arg_val/loc_val/call_val')
1649
1650  MakeArgRefsVarargs('arg_val', 'one', 'two')
1651  g:UseVararg('call_val')->assert_equal('arg_val/the_loc/call_val/one two')
1652
1653  unlet g:UseArg
1654  unlet g:UseVararg
1655enddef
1656
1657def MakeGetAndAppendRefs()
1658  var local = 'a'
1659
1660  def Append(arg: string)
1661    local ..= arg
1662  enddef
1663  g:Append = Append
1664
1665  def Get(): string
1666    return local
1667  enddef
1668  g:Get = Get
1669enddef
1670
1671def Test_closure_append_get()
1672  MakeGetAndAppendRefs()
1673  g:Get()->assert_equal('a')
1674  g:Append('-b')
1675  g:Get()->assert_equal('a-b')
1676  g:Append('-c')
1677  g:Get()->assert_equal('a-b-c')
1678
1679  unlet g:Append
1680  unlet g:Get
1681enddef
1682
1683def Test_nested_closure()
1684  var local = 'text'
1685  def Closure(arg: string): string
1686    return local .. arg
1687  enddef
1688  Closure('!!!')->assert_equal('text!!!')
1689enddef
1690
1691func GetResult(Ref)
1692  return a:Ref('some')
1693endfunc
1694
1695def Test_call_closure_not_compiled()
1696  var text = 'text'
1697  g:Ref = (s) =>  s .. text
1698  GetResult(g:Ref)->assert_equal('sometext')
1699enddef
1700
1701def Test_double_closure_fails()
1702  var lines =<< trim END
1703    vim9script
1704    def Func()
1705      var name = 0
1706      for i in range(2)
1707          timer_start(0, () => name)
1708      endfor
1709    enddef
1710    Func()
1711  END
1712  CheckScriptSuccess(lines)
1713enddef
1714
1715def Test_nested_closure_used()
1716  var lines =<< trim END
1717      vim9script
1718      def Func()
1719        var x = 'hello'
1720        var Closure = () => x
1721        g:Myclosure = () => Closure()
1722      enddef
1723      Func()
1724      assert_equal('hello', g:Myclosure())
1725  END
1726  CheckScriptSuccess(lines)
1727enddef
1728
1729def Test_nested_closure_fails()
1730  var lines =<< trim END
1731    vim9script
1732    def FuncA()
1733      FuncB(0)
1734    enddef
1735    def FuncB(n: number): list<string>
1736      return map([0], (_, v) => n)
1737    enddef
1738    FuncA()
1739  END
1740  CheckScriptFailure(lines, 'E1012:')
1741enddef
1742
1743def Test_global_closure()
1744  var lines =<< trim END
1745      vim9script
1746      def ReverseEveryNLines(n: number, line1: number, line2: number)
1747        var mods = 'sil keepj keepp lockm '
1748        var range = ':' .. line1 .. ',' .. line2
1749        def g:Offset(): number
1750            var offset = (line('.') - line1 + 1) % n
1751            return offset != 0 ? offset : n
1752        enddef
1753        exe mods .. range .. 'g/^/exe "m .-" .. g:Offset()'
1754      enddef
1755
1756      new
1757      repeat(['aaa', 'bbb', 'ccc'], 3)->setline(1)
1758      ReverseEveryNLines(3, 1, 9)
1759  END
1760  CheckScriptSuccess(lines)
1761  var expected = repeat(['ccc', 'bbb', 'aaa'], 3)
1762  assert_equal(expected, getline(1, 9))
1763  bwipe!
1764enddef
1765
1766def Test_global_closure_called_directly()
1767  var lines =<< trim END
1768      vim9script
1769      def Outer()
1770        var x = 1
1771        def g:Inner()
1772          var y = x
1773          x += 1
1774          assert_equal(1, y)
1775        enddef
1776        g:Inner()
1777        assert_equal(2, x)
1778      enddef
1779      Outer()
1780  END
1781  CheckScriptSuccess(lines)
1782  delfunc g:Inner
1783enddef
1784
1785def Test_failure_in_called_function()
1786  # this was using the frame index as the return value
1787  var lines =<< trim END
1788      vim9script
1789      au TerminalWinOpen * eval [][0]
1790      def PopupTerm(a: any)
1791        # make sure typvals on stack are string
1792        ['a', 'b', 'c', 'd', 'e', 'f', 'g']->join()
1793        FireEvent()
1794      enddef
1795      def FireEvent()
1796          do TerminalWinOpen
1797      enddef
1798      # use try/catch to make eval fail
1799      try
1800          call PopupTerm(0)
1801      catch
1802      endtry
1803      au! TerminalWinOpen
1804  END
1805  CheckScriptSuccess(lines)
1806enddef
1807
1808def Test_nested_lambda()
1809  var lines =<< trim END
1810    vim9script
1811    def Func()
1812      var x = 4
1813      var Lambda1 = () => 7
1814      var Lambda2 = () => [Lambda1(), x]
1815      var res = Lambda2()
1816      assert_equal([7, 4], res)
1817    enddef
1818    Func()
1819  END
1820  CheckScriptSuccess(lines)
1821enddef
1822
1823def Shadowed(): list<number>
1824  var FuncList: list<func: number> = [() => 42]
1825  return FuncList->mapnew((_, Shadowed) => Shadowed())
1826enddef
1827
1828def Test_lambda_arg_shadows_func()
1829  assert_equal([42], Shadowed())
1830enddef
1831
1832def Line_continuation_in_def(dir: string = ''): string
1833  var path: string = empty(dir)
1834          \ ? 'empty'
1835          \ : 'full'
1836  return path
1837enddef
1838
1839def Test_line_continuation_in_def()
1840  Line_continuation_in_def('.')->assert_equal('full')
1841enddef
1842
1843def Test_script_var_in_lambda()
1844  var lines =<< trim END
1845      vim9script
1846      var script = 'test'
1847      assert_equal(['test'], map(['one'], () => script))
1848  END
1849  CheckScriptSuccess(lines)
1850enddef
1851
1852def Line_continuation_in_lambda(): list<string>
1853  var x = range(97, 100)
1854      ->mapnew((_, v) => nr2char(v)
1855          ->toupper())
1856      ->reverse()
1857  return x
1858enddef
1859
1860def Test_line_continuation_in_lambda()
1861  Line_continuation_in_lambda()->assert_equal(['D', 'C', 'B', 'A'])
1862
1863  var lines =<< trim END
1864      vim9script
1865      var res = [{n: 1, m: 2, s: 'xxx'}]
1866                ->mapnew((_, v: dict<any>): string => printf('%d:%d:%s',
1867                    v.n,
1868                    v.m,
1869                    substitute(v.s, '.*', 'yyy', '')
1870                    ))
1871      assert_equal(['1:2:yyy'], res)
1872  END
1873  CheckScriptSuccess(lines)
1874enddef
1875
1876def Test_list_lambda()
1877  timer_start(1000, (_) => 0)
1878  var body = execute(timer_info()[0].callback
1879         ->string()
1880         ->substitute("('", ' ', '')
1881         ->substitute("')", '', '')
1882         ->substitute('function\zs', ' ', ''))
1883  assert_match('def <lambda>\d\+(_: any, ...): number\n1  return 0\n   enddef', body)
1884enddef
1885
1886def DoFilterThis(a: string): list<string>
1887  # closure nested inside another closure using argument
1888  var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0)
1889  return ['x', 'y', 'a', 'x2', 'c']->Filter()
1890enddef
1891
1892def Test_nested_closure_using_argument()
1893  assert_equal(['x', 'x2'], DoFilterThis('x'))
1894enddef
1895
1896def Test_triple_nested_closure()
1897  var what = 'x'
1898  var Match = (val: string, cmp: string): bool => stridx(val, cmp) == 0
1899  var Filter = (l) => filter(l, (_, v) => Match(v, what))
1900  assert_equal(['x', 'x2'], ['x', 'y', 'a', 'x2', 'c']->Filter())
1901enddef
1902
1903func Test_silent_echo()
1904  CheckScreendump
1905
1906  let lines =<< trim END
1907    vim9script
1908    def EchoNothing()
1909      silent echo ''
1910    enddef
1911    defcompile
1912  END
1913  call writefile(lines, 'XTest_silent_echo')
1914
1915  " Check that the balloon shows up after a mouse move
1916  let buf = RunVimInTerminal('-S XTest_silent_echo', {'rows': 6})
1917  call term_sendkeys(buf, ":abc")
1918  call VerifyScreenDump(buf, 'Test_vim9_silent_echo', {})
1919
1920  " clean up
1921  call StopVimInTerminal(buf)
1922  call delete('XTest_silent_echo')
1923endfunc
1924
1925def SilentlyError()
1926  execute('silent! invalid')
1927  g:did_it = 'yes'
1928enddef
1929
1930func UserError()
1931  silent! invalid
1932endfunc
1933
1934def SilentlyUserError()
1935  UserError()
1936  g:did_it = 'yes'
1937enddef
1938
1939" This can't be a :def function, because the assert would not be reached.
1940func Test_ignore_silent_error()
1941  let g:did_it = 'no'
1942  call SilentlyError()
1943  call assert_equal('yes', g:did_it)
1944
1945  let g:did_it = 'no'
1946  call SilentlyUserError()
1947  call assert_equal('yes', g:did_it)
1948
1949  unlet g:did_it
1950endfunc
1951
1952def Test_ignore_silent_error_in_filter()
1953  var lines =<< trim END
1954      vim9script
1955      def Filter(winid: number, key: string): bool
1956          if key == 'o'
1957              silent! eval [][0]
1958              return true
1959          endif
1960          return popup_filter_menu(winid, key)
1961      enddef
1962
1963      popup_create('popup', {filter: Filter})
1964      feedkeys("o\r", 'xnt')
1965  END
1966  CheckScriptSuccess(lines)
1967enddef
1968
1969def Fibonacci(n: number): number
1970  if n < 2
1971    return n
1972  else
1973    return Fibonacci(n - 1) + Fibonacci(n - 2)
1974  endif
1975enddef
1976
1977def Test_recursive_call()
1978  Fibonacci(20)->assert_equal(6765)
1979enddef
1980
1981def TreeWalk(dir: string): list<any>
1982  return readdir(dir)->mapnew((_, val) =>
1983            fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
1984               ? {[val]: TreeWalk(dir .. '/' .. val)}
1985               : val
1986             )
1987enddef
1988
1989def Test_closure_in_map()
1990  mkdir('XclosureDir/tdir', 'p')
1991  writefile(['111'], 'XclosureDir/file1')
1992  writefile(['222'], 'XclosureDir/file2')
1993  writefile(['333'], 'XclosureDir/tdir/file3')
1994
1995  TreeWalk('XclosureDir')->assert_equal(['file1', 'file2', {tdir: ['file3']}])
1996
1997  delete('XclosureDir', 'rf')
1998enddef
1999
2000def Test_invalid_function_name()
2001  var lines =<< trim END
2002      vim9script
2003      def s: list<string>
2004  END
2005  CheckScriptFailure(lines, 'E129:')
2006
2007  lines =<< trim END
2008      vim9script
2009      def g: list<string>
2010  END
2011  CheckScriptFailure(lines, 'E129:')
2012
2013  lines =<< trim END
2014      vim9script
2015      def <SID>: list<string>
2016  END
2017  CheckScriptFailure(lines, 'E884:')
2018
2019  lines =<< trim END
2020      vim9script
2021      def F list<string>
2022  END
2023  CheckScriptFailure(lines, 'E488:')
2024enddef
2025
2026def Test_partial_call()
2027  var Xsetlist = function('setloclist', [0])
2028  Xsetlist([], ' ', {title: 'test'})
2029  getloclist(0, {title: 1})->assert_equal({title: 'test'})
2030
2031  Xsetlist = function('setloclist', [0, [], ' '])
2032  Xsetlist({title: 'test'})
2033  getloclist(0, {title: 1})->assert_equal({title: 'test'})
2034
2035  Xsetlist = function('setqflist')
2036  Xsetlist([], ' ', {title: 'test'})
2037  getqflist({title: 1})->assert_equal({title: 'test'})
2038
2039  Xsetlist = function('setqflist', [[], ' '])
2040  Xsetlist({title: 'test'})
2041  getqflist({title: 1})->assert_equal({title: 'test'})
2042
2043  var Len: func: number = function('len', ['word'])
2044  assert_equal(4, Len())
2045enddef
2046
2047def Test_cmd_modifier()
2048  tab echo '0'
2049  CheckDefFailure(['5tab echo 3'], 'E16:')
2050enddef
2051
2052def Test_restore_modifiers()
2053  # check that when compiling a :def function command modifiers are not messed
2054  # up.
2055  var lines =<< trim END
2056      vim9script
2057      set eventignore=
2058      autocmd QuickFixCmdPost * copen
2059      def AutocmdsDisabled()
2060        eval 0
2061      enddef
2062      func Func()
2063        noautocmd call s:AutocmdsDisabled()
2064        let g:ei_after = &eventignore
2065      endfunc
2066      Func()
2067  END
2068  CheckScriptSuccess(lines)
2069  g:ei_after->assert_equal('')
2070enddef
2071
2072def StackTop()
2073  eval 1
2074  eval 2
2075  # call not on fourth line
2076  StackBot()
2077enddef
2078
2079def StackBot()
2080  # throw an error
2081  eval [][0]
2082enddef
2083
2084def Test_callstack_def()
2085  try
2086    StackTop()
2087  catch
2088    v:throwpoint->assert_match('Test_callstack_def\[2\]..StackTop\[4\]..StackBot, line 2')
2089  endtry
2090enddef
2091
2092" Re-using spot for variable used in block
2093def Test_block_scoped_var()
2094  var lines =<< trim END
2095      vim9script
2096      def Func()
2097        var x = ['a', 'b', 'c']
2098        if 1
2099          var y = 'x'
2100          map(x, () => y)
2101        endif
2102        var z = x
2103        assert_equal(['x', 'x', 'x'], z)
2104      enddef
2105      Func()
2106  END
2107  CheckScriptSuccess(lines)
2108enddef
2109
2110def Test_reset_did_emsg()
2111  var lines =<< trim END
2112      @s = 'blah'
2113      au BufWinLeave * #
2114      def Func()
2115        var winid = popup_create('popup', {})
2116        exe '*s'
2117        popup_close(winid)
2118      enddef
2119      Func()
2120  END
2121  CheckScriptFailure(lines, 'E492:', 8)
2122  delfunc! g:Func
2123enddef
2124
2125def Test_did_emsg_reset()
2126  # executing an autocommand resets did_emsg, this should not result in a
2127  # builtin function considered failing
2128  var lines =<< trim END
2129      vim9script
2130      au BufWinLeave * #
2131      def Func()
2132          popup_menu('', {callback: () => popup_create('', {})->popup_close()})
2133          eval [][0]
2134      enddef
2135      nno <F3> <cmd>call <sid>Func()<cr>
2136      feedkeys("\<F3>\e", 'xt')
2137  END
2138  writefile(lines, 'XemsgReset')
2139  assert_fails('so XemsgReset', ['E684:', 'E684:'], lines, 2)
2140  delete('XemsgReset')
2141  nunmap <F3>
2142  au! BufWinLeave
2143enddef
2144
2145def Test_abort_with_silent_call()
2146  var lines =<< trim END
2147      vim9script
2148      g:result = 'none'
2149      def Func()
2150        g:result += 3
2151        g:result = 'yes'
2152      enddef
2153      # error is silenced, but function aborts on error
2154      silent! Func()
2155      assert_equal('none', g:result)
2156      unlet g:result
2157  END
2158  CheckScriptSuccess(lines)
2159enddef
2160
2161def Test_continues_with_silent_error()
2162  var lines =<< trim END
2163      vim9script
2164      g:result = 'none'
2165      def Func()
2166        silent!  g:result += 3
2167        g:result = 'yes'
2168      enddef
2169      # error is silenced, function does not abort
2170      Func()
2171      assert_equal('yes', g:result)
2172      unlet g:result
2173  END
2174  CheckScriptSuccess(lines)
2175enddef
2176
2177def Test_abort_even_with_silent()
2178  var lines =<< trim END
2179      vim9script
2180      g:result = 'none'
2181      def Func()
2182        eval {-> ''}() .. '' .. {}['X']
2183        g:result = 'yes'
2184      enddef
2185      silent! Func()
2186      assert_equal('none', g:result)
2187      unlet g:result
2188  END
2189  CheckScriptSuccess(lines)
2190enddef
2191
2192def Test_cmdmod_silent_restored()
2193  var lines =<< trim END
2194      vim9script
2195      def Func()
2196        g:result = 'none'
2197        silent! g:result += 3
2198        g:result = 'none'
2199        g:result += 3
2200      enddef
2201      Func()
2202  END
2203  # can't use CheckScriptFailure, it ignores the :silent!
2204  var fname = 'Xdefsilent'
2205  writefile(lines, fname)
2206  var caught = 'no'
2207  try
2208    exe 'source ' .. fname
2209  catch /E1030:/
2210    caught = 'yes'
2211    assert_match('Func, line 4', v:throwpoint)
2212  endtry
2213  assert_equal('yes', caught)
2214  delete(fname)
2215enddef
2216
2217def Test_dict_member_with_silent()
2218  var lines =<< trim END
2219      vim9script
2220      g:result = 'none'
2221      var d: dict<any>
2222      def Func()
2223        try
2224          g:result = map([], (_, v) => ({}[v]))->join() .. d['']
2225        catch
2226        endtry
2227      enddef
2228      silent! Func()
2229      assert_equal('0', g:result)
2230      unlet g:result
2231  END
2232  CheckScriptSuccess(lines)
2233enddef
2234
2235def Test_skip_cmds_with_silent()
2236  var lines =<< trim END
2237      vim9script
2238
2239      def Func(b: bool)
2240        Crash()
2241      enddef
2242
2243      def Crash()
2244        sil! :/not found/d _
2245        sil! :/not found/put _
2246      enddef
2247
2248      Func(true)
2249  END
2250  CheckScriptSuccess(lines)
2251enddef
2252
2253def Test_opfunc()
2254  nnoremap <F3> <cmd>set opfunc=Opfunc<cr>g@
2255  def g:Opfunc(_: any): string
2256    setline(1, 'ASDF')
2257    return ''
2258  enddef
2259  new
2260  setline(1, 'asdf')
2261  feedkeys("\<F3>$", 'x')
2262  assert_equal('ASDF', getline(1))
2263
2264  bwipe!
2265  nunmap <F3>
2266enddef
2267
2268" this was crashing on exit
2269def Test_nested_lambda_in_closure()
2270  var lines =<< trim END
2271      vim9script
2272      def Outer()
2273          def g:Inner()
2274              echo map([1, 2, 3], {_, v -> v + 1})
2275          enddef
2276          g:Inner()
2277      enddef
2278      defcompile
2279      writefile(['Done'], 'XnestedDone')
2280      quit
2281  END
2282  if !RunVim([], lines, '--clean')
2283    return
2284  endif
2285  assert_equal(['Done'], readfile('XnestedDone'))
2286  delete('XnestedDone')
2287enddef
2288
2289
2290
2291" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
2292