diff options
Diffstat (limited to 'src/testdir/test_vim9_assign.vim')
-rw-r--r-- | src/testdir/test_vim9_assign.vim | 2825 |
1 files changed, 2825 insertions, 0 deletions
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim new file mode 100644 index 0000000..4f4e58e --- /dev/null +++ b/src/testdir/test_vim9_assign.vim @@ -0,0 +1,2825 @@ +" Test Vim9 assignments + +source check.vim +import './vim9.vim' as v9 +source term_util.vim + +let s:appendToMe = 'xxx' +let s:addToMe = 111 +let s:newVar = '' +let g:existing = 'yes' +let g:inc_counter = 1 +let $SOME_ENV_VAR = 'some' +let g:alist = [7] +let g:adict = #{a: 1} +let g:astring = 'text' + +def Test_assignment_bool() + var bool1: bool = true + assert_equal(v:true, bool1) + var bool2: bool = false + assert_equal(v:false, bool2) + + var bool3: bool = 0 + assert_equal(false, bool3) + var bool4: bool = 1 + assert_equal(true, bool4) + + var bool5: bool = 1 && true + assert_equal(true, bool5) + var bool6: bool = 0 && 1 + assert_equal(false, bool6) + var bool7: bool = 0 || 1 && true + assert_equal(true, bool7) + + var lines =<< trim END + vim9script + def GetFlag(): bool + var flag: bool = 1 + return flag + enddef + var flag: bool = GetFlag() + assert_equal(true, flag) + flag = 0 + assert_equal(false, flag) + flag = 1 + assert_equal(true, flag) + flag = 1 || true + assert_equal(true, flag) + flag = 1 && false + assert_equal(false, flag) + + var cp: bool = &cp + var fen: bool = &l:fen + END + v9.CheckScriptSuccess(lines) + v9.CheckDefAndScriptFailure(['var x: bool = 2'], 'E1012:') + v9.CheckDefAndScriptFailure(['var x: bool = -1'], 'E1012:') + v9.CheckDefAndScriptFailure(['var x: bool = [1]'], 'E1012:') + v9.CheckDefAndScriptFailure(['var x: bool = {}'], 'E1012:') + v9.CheckDefAndScriptFailure(['var x: bool = "x"'], 'E1012:') + + v9.CheckDefAndScriptFailure(['var x: bool = "x"', '', 'eval 0'], 'E1012:', 1) +enddef + +def Test_syntax() + var name = 234 + var other: list<string> = ['asdf'] +enddef + +def Test_assignment() + v9.CheckDefFailure(['var x:string'], 'E1069:') + v9.CheckDefFailure(['var x:string = "x"'], 'E1069:') + v9.CheckDefFailure(['var a:string = "x"'], 'E1069:') + v9.CheckDefFailure(['var lambda = () => "lambda"'], 'E704:') + v9.CheckScriptFailure(['var x = "x"'], 'E1124:') + + # lower case name is OK for a list + var lambdaLines =<< trim END + var lambdaList: list<func> = [g:Test_syntax] + lambdaList[0] = () => "lambda" + END + v9.CheckDefAndScriptSuccess(lambdaLines) + + var nr: number = 1234 + v9.CheckDefFailure(['var nr: number = "asdf"'], 'E1012:') + + var a: number = 6 #comment + assert_equal(6, a) + + if has('channel') + var chan1: channel + assert_equal('fail', ch_status(chan1)) + + var job1: job + assert_equal('fail', job_status(job1)) + + # calling job_start() is in test_vim9_fails.vim, it causes leak reports + endif + var float1: float = 3.4 + var Funky1: func + var Funky2: func = function('len') + var Party2: func = funcref('g:Test_syntax') + + g:newvar = 'new' #comment + assert_equal('new', g:newvar) + + assert_equal('yes', g:existing) + g:existing = 'no' + assert_equal('no', g:existing) + + v:char = 'abc' + assert_equal('abc', v:char) + + $ENVVAR = 'foobar' + assert_equal('foobar', $ENVVAR) + $ENVVAR = '' + + var lines =<< trim END + vim9script + $ENVVAR = 'barfoo' + assert_equal('barfoo', $ENVVAR) + $ENVVAR = '' + END + v9.CheckScriptSuccess(lines) + + appendToMe ..= 'yyy' + assert_equal('xxxyyy', appendToMe) + addToMe += 222 + assert_equal(333, addToMe) + newVar = 'new' + assert_equal('new', newVar) + + set ts=7 + var ts: number = &ts + assert_equal(7, ts) + &ts += 1 + assert_equal(8, &ts) + &ts -= 3 + assert_equal(5, &ts) + &ts *= 2 + assert_equal(10, &ts) + &ts /= 3 + assert_equal(3, &ts) + set ts=10 + &ts %= 4 + assert_equal(2, &ts) + + var f100: float = 100.0 + f100 /= 5 + assert_equal(20.0, f100) + + var f200: float = 200.0 + f200 /= 5.0 + assert_equal(40.0, f200) + + v9.CheckDefFailure(['var nr: number = 200', 'nr /= 5.0'], 'E1012:') + + lines =<< trim END + &ts = 6 + &ts += 3 + assert_equal(9, &ts) + + &l:ts = 6 + assert_equal(6, &ts) + &l:ts += 2 + assert_equal(8, &ts) + + &g:ts = 6 + assert_equal(6, &g:ts) + &g:ts += 2 + assert_equal(8, &g:ts) + + &number = true + assert_equal(true, &number) + &number = 0 + assert_equal(false, &number) + &number = 1 + assert_equal(true, &number) + &number = false + assert_equal(false, &number) + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefFailure(['¬ex += 3'], 'E113:') + v9.CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') + v9.CheckDefFailure(['&ts = [7]'], 'E1012:') + v9.CheckDefExecFailure(['&ts = g:alist'], 'E1012: Type mismatch; expected number but got list<number>') + v9.CheckDefFailure(['&ts = "xx"'], 'E1012:') + v9.CheckDefExecFailure(['&ts = g:astring'], 'E1012: Type mismatch; expected number but got string') + v9.CheckDefFailure(['&path += 3'], 'E1012:') + v9.CheckDefExecFailure(['&bs = "asdf"'], 'E474:') + # test freeing ISN_STOREOPT + v9.CheckDefFailure(['&ts = 3', 'var asdf'], 'E1022:') + &ts = 8 + + lines =<< trim END + var save_TI = &t_TI + &t_TI = '' + assert_equal('', &t_TI) + &t_TI = 'xxx' + assert_equal('xxx', &t_TI) + &t_TI = save_TI + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefFailure(['&t_TI = 123'], 'E1012:') + v9.CheckScriptFailure(['vim9script', '&t_TI = 123'], 'E928:') + + v9.CheckDefFailure(['var s:var = 123'], 'E1101:') + v9.CheckDefFailure(['var s:var: number'], 'E1101:') + + v9.CheckDefAndScriptFailure(['var $VAR: number'], ['E1016:', 'E475:']) + + lines =<< trim END + vim9script + def SomeFunc() + s:var = 123 + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1268:') + + g:inc_counter += 1 + assert_equal(2, g:inc_counter) + + var f: float + f += 1 + assert_equal(1.0, f) + + $SOME_ENV_VAR ..= 'more' + assert_equal('somemore', $SOME_ENV_VAR) + v9.CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1051:') + v9.CheckDefFailure(['$SOME_ENV_VAR += 123'], 'E1012:') + + v:errmsg = 'none' + v:errmsg ..= 'again' + assert_equal('noneagain', v:errmsg) + v9.CheckDefFailure(['v:errmsg += "more"'], 'E1051:') + v9.CheckDefFailure(['v:errmsg += 123'], 'E1012:') + + var text =<< trim END + some text + END +enddef + +def Test_float_and_number() + var lines =<< trim END + var f: float + f += 2 + f -= 1 + assert_equal(1.0, f) + ++f + --f + assert_equal(1.0, f) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +let g:someNumber = 43 + +def Test_assign_concat() + var lines =<< trim END + var s = '-' + s ..= 99 + s ..= true + s ..= '-' + s ..= v:null + s ..= g:someNumber + assert_equal('-99true-null43', s) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var s = '-' + s ..= [1, 2] + END + v9.CheckDefAndScriptFailure(lines, ['E1105: Cannot convert list to string', 'E734: Wrong variable type for .='], 2) + lines =<< trim END + var s = '-' + s ..= {a: 2} + END + v9.CheckDefAndScriptFailure(lines, ['E1105: Cannot convert dict to string', 'E734: Wrong variable type for .='], 2) + + lines =<< trim END + var ls: list<string> = [] + ls[-1] ..= 'foo' + END + v9.CheckDefExecAndScriptFailure(lines, 'E684: List index out of range: -1', 2) +enddef + +def Test_assign_register() + var lines =<< trim END + @c = 'areg' + @c ..= 'add' + assert_equal('aregadd', @c) + + @@ = 'some text' + assert_equal('some text', getreg('"')) + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefFailure(['@a += "more"'], 'E1051:') + v9.CheckDefFailure(['@a += 123'], 'E1012:') +enddef + +def Test_reserved_name() + var more_names = ['null_job', 'null_channel'] + if !has('job') + more_names = [] + endif + + for name in ['true', + 'false', + 'null', + 'null_blob', + 'null_dict', + 'null_function', + 'null_list', + 'null_partial', + 'null_string', + ] + more_names + v9.CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:') + v9.CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:') + endfor +enddef + +def Test_null_values() + var lines =<< trim END + var b: blob = null_blob + var dn: dict<number> = null_dict + var ds: dict<string> = null_dict + var ln: list<number> = null_list + var ls: list<string> = null_list + var Ff: func(string): string = null_function + var Fp: func(number): number = null_partial + var s: string = null_string + if has('job') + var j: job = null_job + var c: channel = null_channel + endif + + var d: dict<func> = {a: function('tr'), b: null_function} + + var bl: list<blob> = [0z12, null_blob] + var dnl: list<dict<number>> = [{a: 1}, null_dict] + var dsl: list<dict<string>> = [{a: 'x'}, null_dict] + var lnl: list<list<number>> = [[1], null_list] + var lsl: list<list<string>> = [['x'], null_list] + def Len(v: string): number + return len(v) + enddef + var Ffl: list<func(string): number> = [Len, null_function] + var Fpl: list<func(string): number> = [Len, null_partial] + var sl: list<string> = ['x', null_string] + if has('job') + var jl: list<job> = [null_job] + var cl: list<channel> = [null_channel] + endif + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_type_with_extra_white() + var lines =<< trim END + const x : number = 3 + END + v9.CheckDefExecAndScriptFailure(lines, 'E1059') +enddef + +def Test_keep_type_after_assigning_null() + var lines =<< trim END + var b: blob + b = null_blob + b = 'text' + END + v9.CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected blob but got string') + + lines =<< trim END + var l: list<number> + l = null_list + l = ['text'] + END + v9.CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>') + + lines =<< trim END + var d: dict<string> + d = null_dict + d = {a: 1, b: 2} + END + v9.CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected dict<string> but got dict<number>') +enddef + +def Test_skipped_assignment() + var lines =<< trim END + for x in [] + var i: number = 1 + while false + i += 1 + endwhile + endfor + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_assign_keep_type() + var lines =<< trim END + vim9script + var l: list<number> = [123] + l = [123] + l->add('string') + END + v9.CheckScriptFailure(lines, 'E1012:', 4) +enddef + +def Test_assign_unpack() + var lines =<< trim END + var v1: number + var v2: number + [v1, v2] = [1, 2] + assert_equal(1, v1) + assert_equal(2, v2) + + [v1, _, v2, _] = [1, 99, 2, 77] + assert_equal(1, v1) + assert_equal(2, v2) + + [v1, v2; _] = [1, 2, 3, 4, 5] + assert_equal(1, v1) + assert_equal(2, v2) + + var _x: number + [_x, v2] = [6, 7] + assert_equal(6, _x) + assert_equal(7, v2) + + var reslist = [] + for text in ['aaa {bbb} ccc', 'ddd {eee} fff'] + var before: string + var middle: string + var after: string + [_, before, middle, after; _] = text->matchlist('\(.\{-\}\){\(.\{-\}\)}\(.*\)') + reslist->add(before)->add(middle)->add(after) + endfor + assert_equal(['aaa ', 'bbb', ' ccc', 'ddd ', 'eee', ' fff'], reslist) + + var a = 1 + var b = 3 + [a, b] += [2, 4] + assert_equal(3, a) + assert_equal(7, b) + + [a, b] -= [1, 2] + assert_equal(2, a) + assert_equal(5, b) + + [a, b] *= [3, 2] + assert_equal(6, a) + assert_equal(10, b) + + [a, b] /= [2, 4] + assert_equal(3, a) + assert_equal(2, b) + + [a, b] = [17, 15] + [a, b] %= [5, 3] + assert_equal(2, a) + assert_equal(0, b) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = [1, 2, 3] + END + v9.CheckDefFailure(lines, 'E1093: Expected 2 items but got 3', 3) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = [1] + END + v9.CheckDefFailure(lines, 'E1093: Expected 2 items but got 1', 3) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2; _] = [1] + END + v9.CheckDefFailure(lines, 'E1093: Expected 2 items but got 1', 3) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = + END + v9.CheckDefFailure(lines, 'E1097:', 5) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = xxx + END + v9.CheckDefFailure(lines, 'E1001:', 3) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = popup_clear() + END + v9.CheckDefFailure(lines, 'E1031:', 3) + + lines =<< trim END + [v1, v2] = [1, 2] + END + v9.CheckDefFailure(lines, 'E1089', 1) + v9.CheckScriptFailure(['vim9script'] + lines, 'E1089', 2) + + lines =<< trim END + var v1: number + var v2: number + [v1, v2] = '' + END + v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 3) + + lines =<< trim END + g:values = [false, 0] + var x: bool + var y: string + [x, y] = g:values + END + v9.CheckDefExecAndScriptFailure(lines, 'E1163: Variable 2: type mismatch, expected string but got number') + + lines =<< trim END + var x: number + var y: number + var z: string + [x, y, z] = [1, 2, 3] + END + v9.CheckDefAndScriptFailure(lines, 'E1163: Variable 3: type mismatch, expected string but got number') + + lines =<< trim END + var x: number + var y: string + var z: string + [x, y, z] = [1, '2', 3] + END + v9.CheckDefExecAndScriptFailure(lines, 'E1163: Variable 3: type mismatch, expected string but got number') +enddef + +def Test_assign_linebreak() + var nr: number + nr = + 123 + assert_equal(123, nr) + + var n2: number + [nr, n2] = + [12, 34] + assert_equal(12, nr) + assert_equal(34, n2) + + v9.CheckDefFailure(["var x = #"], 'E1097:', 3) + + var lines =<< trim END + var x: list<string> = ['a'] + var y: list<number> = x + ->copy() + ->copy() + END + v9.CheckDefExecFailure(lines, 'E1012:', 4) + + lines =<< trim END + var x: any + x.key = 1 + + 2 + + 3 + + 4 + + 5 + END + v9.CheckDefExecAndScriptFailure(lines, ['E1148:', 'E1203:'], 2) +enddef + +def Test_assign_index() + # list of list + var l1: list<number> + l1[0] = 123 + assert_equal([123], l1) + + var l2: list<list<number>> + l2[0] = [] + l2[0][0] = 123 + assert_equal([[123]], l2) + + var l3: list<list<list<number>>> + l3[0] = [] + l3[0][0] = [] + l3[0][0][0] = 123 + assert_equal([[[123]]], l3) + + var lines =<< trim END + var l3: list<list<number>> + l3[0] = [] + l3[0][0] = [] + END + v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got list<unknown>', 3) + + # dict of dict + var d1: dict<number> + d1.one = 1 + assert_equal({one: 1}, d1) + + var d2: dict<dict<number>> + d2.one = {} + d2.one.two = 123 + assert_equal({one: {two: 123}}, d2) + + var d3: dict<dict<dict<number>>> + d3.one = {} + d3.one.two = {} + d3.one.two.three = 123 + assert_equal({one: {two: {three: 123}}}, d3) + + # blob + var bl: blob = 0z11223344 + bl[0] = 0x77 + assert_equal(0z77223344, bl) + bl[-2] = 0x66 + assert_equal(0z77226644, bl) + + lines =<< trim END + g:val = '22' + var bl = 0z11 + bl[1] = g:val + END + v9.CheckDefExecAndScriptFailure(lines, 'E1030: Using a String as a Number: "22"') + + # should not read the next line when generating "a.b" + var a = {} + a.b = {} + a.b.c = {} + ->copy() + + lines =<< trim END + var d3: dict<dict<number>> + d3.one = {} + d3.one.two = {} + END + v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got dict<unknown>', 3) + + lines =<< trim END + var lines: list<string> + lines['a'] = 'asdf' + END + v9.CheckDefFailure(lines, 'E1012:', 2) + + lines =<< trim END + var lines: string + lines[9] = 'asdf' + END + v9.CheckDefFailure(lines, 'E1141:', 2) + + # list of dict + var ld: list<dict<number>> + ld[0] = {} + ld[0].one = 123 + assert_equal([{one: 123}], ld) + + lines =<< trim END + var ld: list<dict<number>> + ld[0] = [] + END + v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected dict<number> but got list<unknown>', 2) + + # dict of list + var dl: dict<list<number>> + dl.one = [] + dl.one[0] = 123 + assert_equal({one: [123]}, dl) + + lines =<< trim END + var dl: dict<list<number>> + dl.one = {} + END + v9.CheckDefFailure(lines, 'E1012: Type mismatch; expected list<number> but got dict<unknown>', 2) + + lines =<< trim END + g:l = [1, 2] + g:l['x'] = 3 + END + v9.CheckDefExecAndScriptFailure(lines, ['E39:', 'E1030:'], 2) + + lines =<< trim END + var bl: blob = test_null_blob() + bl[1] = 8 + END + v9.CheckDefExecAndScriptFailure(lines, ['E1184:', 'E979:'], 2) + + lines =<< trim END + g:bl = 'not a blob' + g:bl[1 : 2] = 8 + END + v9.CheckDefExecAndScriptFailure(lines, ['E897:', 'E689:'], 2) +enddef + +def Test_init_in_for_loop() + var lines =<< trim END + var l: list<number> = [] + for i in [3, 4] + var n: number + add(l, n) + n = 123 + endfor + assert_equal([0, 0], l) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var l: list<number> = [] + for i in [3, 4] + var n: number = 0 + add(l, n) + n = 123 + endfor + assert_equal([0, 0], l) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var l: list<number> = [] + for i in [3, 4] + var n: number = 3 + add(l, n) + n = 123 + endfor + assert_equal([3, 3], l) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_redir_is_not_assign() + if false + redir => res + echo var_job + redir END + endif +enddef + +def Test_extend_list() + # using uninitialized list assigns empty list + var lines =<< trim END + var l1: list<number> + var l2 = l1 + assert_true(l1 is l2) + l1 += [123] + assert_equal([123], l1) + assert_true(l1 is l2) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var list: list<string> + extend(list, ['x']) + assert_equal(['x'], list) + END + v9.CheckDefAndScriptSuccess(lines) + + # appending to uninitialized list from a function works + lines =<< trim END + vim9script + var list: list<string> + def Func() + list += ['a', 'b'] + enddef + Func() + assert_equal(['a', 'b'], list) + END + v9.CheckScriptSuccess(lines) + lines =<< trim END + vim9script + var list: list<string> + def Func() + extend(list, ['x', 'b']) + enddef + Func() + assert_equal(['x', 'b'], list) + END + v9.CheckScriptSuccess(lines) + + # initialized to null, with type, does not default to empty list + lines =<< trim END + vim9script + var l: list<string> = test_null_list() + extend(l, ['x']) + END + v9.CheckScriptFailure(lines, 'E1134:', 3) + + # initialized to null, without type, does not default to empty list + lines =<< trim END + vim9script + var l = null_list + extend(l, ['x']) + END + v9.CheckScriptFailure(lines, 'E1134:', 3) + + # assigned null, does not default to empty list + lines =<< trim END + vim9script + var l: list<string> + l = null_list + extend(l, ['x']) + END + v9.CheckScriptFailure(lines, 'E1134:', 4) + + lines =<< trim END + vim9script + extend(test_null_list(), ['x']) + END + v9.CheckScriptFailure(lines, 'E1134:', 2) + + # using global var has no declared type + g:myList = [] + g:myList->extend([1]) + g:myList->extend(['x']) + assert_equal([1, 'x'], g:myList) + unlet g:myList + + # using declared list gives an error + lines =<< trim END + var l: list<number> + g:myList = l + g:myList->extend([1]) + g:myList->extend(['x']) + END + v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected list<number> but got list<string>', 4) + unlet g:myList + + lines =<< trim END + vim9script + var lds = [1, 2, 3] + def Func() + echo lds->extend(['x']) + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1013:') +enddef + +def Test_extend_dict() + var lines =<< trim END + vim9script + var d: dict<number> + extend(d, {a: 1}) + assert_equal({a: 1}, d) + + var d2: dict<number> + d2['one'] = 1 + assert_equal({one: 1}, d2) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var d: dict<string> = test_null_dict() + extend(d, {a: 'x'}) + END + v9.CheckScriptFailure(lines, 'E1133:', 3) + + lines =<< trim END + vim9script + extend(test_null_dict(), {a: 'x'}) + END + v9.CheckScriptFailure(lines, 'E1133:', 2) +enddef + +def Test_single_letter_vars() + # single letter variables + var a: number = 123 + a = 123 + assert_equal(123, a) + var b: number + b = 123 + assert_equal(123, b) + var g: number + g = 123 + assert_equal(123, g) + var s: number + s = 123 + assert_equal(123, s) + var t: number + t = 123 + assert_equal(123, t) + var v: number + v = 123 + assert_equal(123, v) + var w: number + w = 123 + assert_equal(123, w) +enddef + +def Test_vim9_single_char_vars() + var lines =<< trim END + vim9script + + # single character variable declarations work + var a: string + var b: number + var l: list<any> + var s: string + var t: number + var v: number + var w: number + + # script-local variables can be used without s: prefix + a = 'script-a' + b = 111 + l = [1, 2, 3] + s = 'script-s' + t = 222 + v = 333 + w = 444 + + assert_equal('script-a', a) + assert_equal(111, b) + assert_equal([1, 2, 3], l) + assert_equal('script-s', s) + assert_equal(222, t) + assert_equal(333, v) + assert_equal(444, w) + END + writefile(lines, 'Xsinglechar', 'D') + source Xsinglechar +enddef + +def Test_assignment_list() + var list1: list<bool> = [false, true, false] + var list2: list<number> = [1, 2, 3] + var list3: list<string> = ['sdf', 'asdf'] + var list4: list<any> = ['yes', true, 1234] + var list5: list<blob> = [0z01, 0z02] + + var listS: list<string> = [] + var listN: list<number> = [] + + assert_equal([1, 2, 3], list2) + list2[-1] = 99 + assert_equal([1, 2, 99], list2) + list2[-2] = 88 + assert_equal([1, 88, 99], list2) + list2[-3] = 77 + assert_equal([77, 88, 99], list2) + list2 += [100] + assert_equal([77, 88, 99, 100], list2) + + list3 += ['end'] + assert_equal(['sdf', 'asdf', 'end'], list3) + + v9.CheckDefExecFailure(['var ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:') + v9.CheckDefExecFailure(['var ll = [1, 2, 3]', 'unlet ll[8 : 9]'], 'E684:') + v9.CheckDefExecFailure(['var ll = [1, 2, 3]', 'unlet ll[1 : -9]'], 'E684:') + v9.CheckDefExecFailure(['var ll = [1, 2, 3]', 'unlet ll[2 : 1]'], 'E684:') + + # type becomes list<any> + var somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c'] + + # type is list<any> even though initializer is list<number> + var anyList: list<any> = [0] + assert_equal([0, 'x'], extend(anyList, ['x'])) + + var lines =<< trim END + var d = {dd: test_null_list()} + d.dd[0] = 0 + END + v9.CheckDefExecFailure(lines, 'E1147:', 2) + + lines =<< trim END + def OneArg(x: bool) + enddef + def TwoArgs(x: bool, y: bool) + enddef + var fl: list<func(bool, bool, bool)> = [OneArg, TwoArgs] + END + v9.CheckDefExecAndScriptFailure(lines, 'E1012:', 5) +enddef + +def Test_list_declaration() + var [v1, v2] = [1, 2] + v1 += 3 + assert_equal(4, v1) + v2 *= 3 + assert_equal(6, v2) + + var lines =<< trim END + var [v1, v2] = [1] + END + v9.CheckDefExecAndScriptFailure(lines, ['E1093: Expected 2 items but got 1', 'E688:']) + lines =<< trim END + var testlist = [1] + var [v1, v2] = testlist + END + v9.CheckDefExecAndScriptFailure(lines, ['E1093: Expected 2 items but got 1', 'E688:']) + lines =<< trim END + var [v1, v2] = [1, 2, 3] + END + v9.CheckDefExecAndScriptFailure(lines, ['E1093: Expected 2 items but got 3', 'E687:']) + lines =<< trim END + var testlist = [1, 2, 3] + var [v1, v2] = testlist + END + v9.CheckDefExecAndScriptFailure(lines, ['E1093: Expected 2 items but got 3', 'E687:']) + + var [vnr, vstr] = [123, 'text'] + vnr += 3 + assert_equal(126, vnr) + vstr ..= 'end' + assert_equal('textend', vstr) + + var [vnr2: number, vstr2: string] = [123, 'text'] + vnr2 += 3 + assert_equal(126, vnr2) + vstr2 ..= 'end' + assert_equal('textend', vstr2) + + var [vnr3: number; vlist: list<string>] = [123, 'foo', 'bar'] + vnr3 += 5 + assert_equal(128, vnr3) + assert_equal(['foo', 'bar'], vlist) + + lines =<< trim END + var [vnr2: number, vstr2: number] = [123, 'text'] + END + v9.CheckDefExecAndScriptFailure(lines, ['E1163: Variable 2: type mismatch, expected number but got string', 'E1012: Type mismatch; expected number but got string']) + lines =<< trim END + var testlist = [234, 'text'] + var [vnr2: number, vstr2: number] = testlist + END + v9.CheckDefExecAndScriptFailure(lines, ['E1163: Variable 2: type mismatch, expected number but got string', 'E1012: Type mismatch; expected number but got string']) +enddef + +def PartFuncBool(b: bool): string + return 'done' +enddef + +def Test_assignment_partial() + var lines =<< trim END + var Partial: func(): string = function(g:PartFuncBool, [true]) + assert_equal('done', Partial()) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + vim9script + def Func(b: bool) + enddef + var Ref: func = function(Func, [true]) + assert_equal('func()', typename(Ref)) + Ref() + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + + var nres: any + var sres: any + def Func(nr: number, s = '') + nres = nr + sres = s + enddef + + var n: number + var Ref = function(Func, [n]) + Ref('x') + assert_equal(0, nres) + assert_equal('x', sres) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + + def Func(nr: number, s = '') + enddef + + var n: number + var Ref = function(Func, [n]) + Ref(0) + END + v9.CheckScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected string but got number') +enddef + +def Test_assignment_list_any_index() + var l: list<number> = [1, 2] + for [x, y, _] + in [[0, 1, ''], [1, 3, '']] + l[x] = l[x] + y + endfor + assert_equal([2, 5], l) +enddef + +def Test_assignment_list_vim9script() + var lines =<< trim END + vim9script + var v1: number + var v2: number + var v3: number + [v1, v2, v3] = [1, 2, 3] + assert_equal([1, 2, 3], [v1, v2, v3]) + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_assignment_dict() + var dict1: dict<bool> = {one: false, two: true} + var dict2: dict<number> = {one: 1, two: 2} + var dict3: dict<string> = {key: 'value'} + var dict4: dict<any> = {one: 1, two: '2'} + var dict5: dict<blob> = {one: 0z01, two: 0z02} + + # check the type is OK + var events: dict<string> = v:event + + # overwrite + dict3['key'] = 'another' + assert_equal(dict3, {key: 'another'}) + dict3.key = 'yet another' + assert_equal(dict3, {key: 'yet another'}) + + # member "any" can also be a dict and assigned to + var anydict: dict<any> = {nest: {}, nr: 0} + anydict.nest['this'] = 123 + anydict.nest.that = 456 + assert_equal({nest: {this: 123, that: 456}, nr: 0}, anydict) + + var lines =<< trim END + var dd = {} + dd.two = 2 + assert_equal({two: 2}, dd) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var d = {dd: {}} + d.dd[0] = 2 + d.dd['x'] = 3 + d.dd.y = 4 + assert_equal({dd: {0: 2, x: 3, y: 4}}, d) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var key = 'foo' + g:[key] = 'value' + assert_equal('value', g:foo) + unlet g:foo + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var dd = {one: 1} + dd.one) = 2 + END + v9.CheckDefFailure(lines, 'E488:', 2) + + lines =<< trim END + var dd = {one: 1} + var dd.one = 2 + END + v9.CheckDefAndScriptFailure(lines, 'E1017:', 2) + + # empty key can be used + var dd = {} + dd[""] = 6 + assert_equal({['']: 6}, dd) + + # type becomes dict<any> + var somedict = rand() > 0 ? {a: 1, b: 2} : {a: 'a', b: 'b'} + + # type is dict<any> even though initializer is dict<number> + var anyDict: dict<any> = {a: 0} + assert_equal({a: 0, b: 'x'}, extend(anyDict, {b: 'x'})) + + # using global var, which has no declared type + g:myDict = {} + g:myDict->extend({a: 1}) + g:myDict->extend({b: 'x'}) + assert_equal({a: 1, b: 'x'}, g:myDict) + unlet g:myDict + + # using list with declared type gives an error + lines =<< trim END + var d: dict<number> + g:myDict = d + g:myDict->extend({a: 1}) + g:myDict->extend({b: 'x'}) + END + v9.CheckDefExecAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected dict<number> but got dict<string>', 4) + unlet g:myDict + + # assignment to script-local dict + lines =<< trim END + vim9script + var test: dict<any> = {} + def FillDict(): dict<any> + test['a'] = 43 + return test + enddef + assert_equal({a: 43}, FillDict()) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var test: dict<any> + def FillDict(): dict<any> + test['a'] = 43 + return test + enddef + FillDict() + assert_equal({a: 43}, test) + END + v9.CheckScriptSuccess(lines) + + # assignment to global dict + lines =<< trim END + vim9script + g:test = {} + def FillDict(): dict<any> + g:test['a'] = 43 + return g:test + enddef + assert_equal({a: 43}, FillDict()) + END + v9.CheckScriptSuccess(lines) + + # assignment to buffer dict + lines =<< trim END + vim9script + b:test = {} + def FillDict(): dict<any> + b:test['a'] = 43 + return b:test + enddef + assert_equal({a: 43}, FillDict()) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + var d = {dd: test_null_dict()} + d.dd[0] = 0 + END + v9.CheckDefExecFailure(lines, 'E1103:', 2) + + lines =<< trim END + var d = {dd: 'string'} + d.dd[0] = 0 + END + v9.CheckDefExecFailure(lines, 'E1148:', 2) + + lines =<< trim END + var n: any + n.key = 5 + END + v9.CheckDefExecAndScriptFailure(lines, ['E1148:', 'E1203: Dot can only be used on a dictionary: n.key = 5'], 2) +enddef + +def Test_assignment_local() + # Test in a separated file in order not to the current buffer/window/tab is + # changed. + var script_lines: list<string> =<< trim END + let b:existing = 'yes' + let w:existing = 'yes' + let t:existing = 'yes' + + def Test_assignment_local_internal() + b:newvar = 'new' + assert_equal('new', b:newvar) + assert_equal('yes', b:existing) + b:existing = 'no' + assert_equal('no', b:existing) + b:existing ..= 'NO' + assert_equal('noNO', b:existing) + + w:newvar = 'new' + assert_equal('new', w:newvar) + assert_equal('yes', w:existing) + w:existing = 'no' + assert_equal('no', w:existing) + w:existing ..= 'NO' + assert_equal('noNO', w:existing) + + t:newvar = 'new' + assert_equal('new', t:newvar) + assert_equal('yes', t:existing) + t:existing = 'no' + assert_equal('no', t:existing) + t:existing ..= 'NO' + assert_equal('noNO', t:existing) + enddef + call Test_assignment_local_internal() + END + v9.CheckScriptSuccess(script_lines) +enddef + +def Test_assignment_default() + # Test default values. + var thebool: bool + assert_equal(v:false, thebool) + + var thenumber: number + assert_equal(0, thenumber) + + var thefloat: float + assert_equal(0.0, thefloat) + + var thestring: string + assert_equal('', thestring) + + var theblob: blob + assert_equal(0z, theblob) + + var Thefunc: func + assert_equal(test_null_function(), Thefunc) + + var thelist: list<any> + assert_equal([], thelist) + + var thedict: dict<any> + assert_equal({}, thedict) + + if has('channel') + var thejob: job + assert_equal(test_null_job(), thejob) + + var thechannel: channel + assert_equal(test_null_channel(), thechannel) + + if has('unix') && executable('cat') + # check with non-null job and channel, types must match + thejob = job_start("cat ", {}) + thechannel = job_getchannel(thejob) + job_stop(thejob, 'kill') + endif + endif + + var nr = 1234 | nr = 5678 + assert_equal(5678, nr) +enddef + +def Test_script_var_default() + var lines =<< trim END + vim9script + var l: list<number> + var li = [1, 2] + var bl: blob + var bli = 0z12 + var d: dict<number> + var di = {'a': 1, 'b': 2} + def Echo() + assert_equal([], l) + assert_equal([1, 2], li) + assert_equal(0z, bl) + assert_equal(0z12, bli) + assert_equal({}, d) + assert_equal({'a': 1, 'b': 2}, di) + enddef + Echo() + END + v9.CheckScriptSuccess(lines) +enddef + +let s:scriptvar = 'init' + +def Test_assignment_var_list() + var lines =<< trim END + var v1: string + var v2: string + var vrem: list<string> + [v1] = ['aaa'] + assert_equal('aaa', v1) + + [v1, v2] = ['one', 'two'] + assert_equal('one', v1) + assert_equal('two', v2) + + [v1, v2; vrem] = ['one', 'two'] + assert_equal('one', v1) + assert_equal('two', v2) + assert_equal([], vrem) + + [v1, v2; vrem] = ['one', 'two', 'three'] + assert_equal('one', v1) + assert_equal('two', v2) + assert_equal(['three'], vrem) + + [&ts, &sw] = [3, 4] + assert_equal(3, &ts) + assert_equal(4, &sw) + set ts=8 sw=4 + + [@a, @z] = ['aa', 'zz'] + assert_equal('aa', @a) + assert_equal('zz', @z) + + [$SOME_VAR, $OTHER_VAR] = ['some', 'other'] + assert_equal('some', $SOME_VAR) + assert_equal('other', $OTHER_VAR) + + [g:globalvar, b:bufvar, w:winvar, t:tabvar, v:errmsg] = + ['global', 'buf', 'win', 'tab', 'error'] + assert_equal('global', g:globalvar) + assert_equal('buf', b:bufvar) + assert_equal('win', w:winvar) + assert_equal('tab', t:tabvar) + assert_equal('error', v:errmsg) + unlet g:globalvar + END + v9.CheckDefAndScriptSuccess(lines) + + [g:globalvar, scriptvar, b:bufvar] = ['global', 'script', 'buf'] + assert_equal('global', g:globalvar) + assert_equal('script', scriptvar) + assert_equal('buf', b:bufvar) + + lines =<< trim END + vim9script + var scriptvar = 'init' + [g:globalvar, scriptvar, w:winvar] = ['global', 'script', 'win'] + assert_equal('global', g:globalvar) + assert_equal('script', scriptvar) + assert_equal('win', w:winvar) + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_assignment_empty_list() + var lines =<< trim END + var l2: list<any> = [] + var l: list<string> + l = l2 + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_assignment_vim9script() + var lines =<< trim END + vim9script + def Func(): list<number> + return [1, 2] + enddef + var name1: number + var name2: number + [name1, name2] = + Func() + assert_equal(1, name1) + assert_equal(2, name2) + var ll = + Func() + assert_equal([1, 2], ll) + + @/ = 'text' + assert_equal('text', @/) + @0 = 'zero' + assert_equal('zero', @0) + @1 = 'one' + assert_equal('one', @1) + @9 = 'nine' + assert_equal('nine', @9) + @- = 'minus' + assert_equal('minus', @-) + if has('clipboard_working') + @* = 'star' + assert_equal('star', @*) + @+ = 'plus' + assert_equal('plus', @+) + endif + + var a: number = 123 + assert_equal(123, a) + var s: string = 'yes' + assert_equal('yes', s) + var b: number = 42 + assert_equal(42, b) + var w: number = 43 + assert_equal(43, w) + var t: number = 44 + assert_equal(44, t) + + var to_var = 0 + to_var = 3 + assert_equal(3, to_var) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var n: number + def Func() + n = 'string' + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') +enddef + +def Mess(): string + v:foldstart = 123 + return 'xxx' +enddef + +def Test_assignment_failure() + v9.CheckDefFailure(['var name=234'], 'E1004:') + v9.CheckDefFailure(['var name =234'], 'E1004:') + v9.CheckDefFailure(['var name= 234'], 'E1004:') + + v9.CheckScriptFailure(['vim9script', 'var name=234'], 'E1004:') + v9.CheckScriptFailure(['vim9script', 'var name=234'], "before and after '='") + v9.CheckScriptFailure(['vim9script', 'var name =234'], 'E1004:') + v9.CheckScriptFailure(['vim9script', 'var name= 234'], 'E1004:') + v9.CheckScriptFailure(['vim9script', 'var name = 234', 'name+=234'], 'E1004:') + v9.CheckScriptFailure(['vim9script', 'var name = 234', 'name+=234'], "before and after '+='") + v9.CheckScriptFailure(['vim9script', 'var name = "x"', 'name..="y"'], 'E1004:') + v9.CheckScriptFailure(['vim9script', 'var name = "x"', 'name..="y"'], "before and after '..='") + + v9.CheckDefFailure(['var true = 1'], 'E1034:') + v9.CheckDefFailure(['var false = 1'], 'E1034:') + v9.CheckDefFailure(['var null = 1'], 'E1034:') + v9.CheckDefFailure(['var this = 1'], 'E1034:') + + v9.CheckDefFailure(['[a; b; c] = g:list'], 'E1001:') + v9.CheckDefFailure(['var [a; b; c] = g:list'], 'E1080:') + v9.CheckDefExecFailure(['var a: number', + '[a] = test_null_list()'], 'E1093:') + v9.CheckDefExecFailure(['var a: number', + '[a] = []'], 'E1093:') + v9.CheckDefExecFailure(['var x: number', + 'var y: number', + '[x, y] = [1]'], 'E1093:') + v9.CheckDefExecFailure(['var x: string', + 'var y: string', + '[x, y] = ["x"]'], 'E1093:') + v9.CheckDefExecFailure(['var x: number', + 'var y: number', + 'var z: list<number>', + '[x, y; z] = [1]'], 'E1093:') + + v9.CheckDefFailure(['var somevar'], "E1022:") + v9.CheckDefFailure(['var &tabstop = 4'], 'E1052:') + v9.CheckDefFailure(['&g:option = 5'], 'E113:') + v9.CheckScriptFailure(['vim9script', 'var &tabstop = 4'], 'E1052:') + + v9.CheckDefFailure(['var $VAR = 5'], 'E1016: Cannot declare an environment variable:') + v9.CheckScriptFailure(['vim9script', 'var $ENV = "xxx"'], 'E1016:') + + if has('dnd') + v9.CheckDefFailure(['var @~ = 5'], 'E1066:') + else + v9.CheckDefFailure(['var @~ = 5'], 'E354:') + v9.CheckDefFailure(['@~ = 5'], 'E354:') + endif + v9.CheckDefFailure(['var @a = 5'], 'E1066:') + v9.CheckDefFailure(['var @/ = "x"'], 'E1066:') + v9.CheckScriptFailure(['vim9script', 'var @a = "abc"'], 'E1066:') + + v9.CheckDefFailure(['var g:var = 5'], 'E1016: Cannot declare a global variable:') + v9.CheckDefFailure(['var w:var = 5'], 'E1016: Cannot declare a window variable:') + v9.CheckDefFailure(['var b:var = 5'], 'E1016: Cannot declare a buffer variable:') + v9.CheckDefFailure(['var t:var = 5'], 'E1016: Cannot declare a tab variable:') + + v9.CheckDefFailure(['var anr = 4', 'anr ..= "text"'], 'E1019:') + v9.CheckDefFailure(['var xnr += 4'], 'E1020:', 1) + v9.CheckScriptFailure(['vim9script', 'var xnr += 4'], 'E1020:') + v9.CheckDefFailure(["var xnr = xnr + 1"], 'E1001:', 1) + v9.CheckScriptFailure(['vim9script', 'var xnr = xnr + 4'], 'E121:') + + v9.CheckScriptFailure(['vim9script', 'def Func()', 'var dummy = notfound', 'enddef', 'defcompile'], 'E1001:') + + v9.CheckDefFailure(['var name: list<string> = [123]'], 'expected list<string> but got list<number>') + v9.CheckDefFailure(['var name: list<number> = ["xx"]'], 'expected list<number> but got list<string>') + + v9.CheckDefFailure(['var name: dict<string> = {key: 123}'], 'expected dict<string> but got dict<number>') + v9.CheckDefFailure(['var name: dict<number> = {key: "xx"}'], 'expected dict<number> but got dict<string>') + + v9.CheckDefFailure(['var name = feedkeys("0")'], 'E1031:') + v9.CheckDefFailure(['var name: number = feedkeys("0")'], 'expected number but got void') + + v9.CheckDefFailure(['var name: dict <number>'], 'E1068:') + v9.CheckDefFailure(['var name: dict<number'], 'E1009: Missing > after type: <number') + + assert_fails('s/^/\=g:Mess()/n', 'E794:') + v9.CheckDefFailure(['var name: dict<number'], 'E1009:') + + v9.CheckDefFailure(['w:foo: number = 10'], + 'E1016: Cannot declare a window variable: w:foo') + v9.CheckDefFailure(['t:foo: bool = true'], + 'E1016: Cannot declare a tab variable: t:foo') + v9.CheckDefFailure(['b:foo: string = "x"'], + 'E1016: Cannot declare a buffer variable: b:foo') + v9.CheckDefFailure(['g:foo: number = 123'], + 'E1016: Cannot declare a global variable: g:foo') + + v9.CheckScriptFailure(['vim9script', 'w:foo: number = 123'], + 'E1304: Cannot use type with this variable: w:foo:') + v9.CheckScriptFailure(['vim9script', 't:foo: number = 123'], + 'E1304: Cannot use type with this variable: t:foo:') + v9.CheckScriptFailure(['vim9script', 'b:foo: number = 123'], + 'E1304: Cannot use type with this variable: b:foo:') + v9.CheckScriptFailure(['vim9script', 'g:foo: number = 123'], + 'E1304: Cannot use type with this variable: g:foo:') + + v9.CheckScriptFailure(['vim9script', 'const w:FOO: number = 123'], + 'E1304: Cannot use type with this variable: w:FOO:') + v9.CheckScriptFailure(['vim9script', 'const t:FOO: number = 123'], + 'E1304: Cannot use type with this variable: t:FOO:') + v9.CheckScriptFailure(['vim9script', 'const b:FOO: number = 123'], + 'E1304: Cannot use type with this variable: b:FOO:') + v9.CheckScriptFailure(['vim9script', 'const g:FOO: number = 123'], + 'E1304: Cannot use type with this variable: g:FOO:') +enddef + +def Test_assign_list() + var lines =<< trim END + var l: list<string> = [] + l[0] = 'value' + assert_equal('value', l[0]) + + l[1] = 'asdf' + assert_equal('value', l[0]) + assert_equal('asdf', l[1]) + assert_equal('asdf', l[-1]) + assert_equal('value', l[-2]) + + var nrl: list<number> = [] + for i in range(5) + nrl[i] = i + endfor + assert_equal([0, 1, 2, 3, 4], nrl) + + var ul: list<any> + ul[0] = 1 + ul[1] = 2 + ul[2] = 3 + assert_equal([1, 2, 3], ul) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var l = [1, 2] + g:idx = 'x' + l[g:idx : 1] = [0] + echo l + END + v9.CheckDefExecAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "x"']) + + lines =<< trim END + var l = [1, 2] + g:idx = 3 + l[g:idx : 1] = [0] + echo l + END + v9.CheckDefExecAndScriptFailure(lines, 'E684: List index out of range: 3') + + lines =<< trim END + var l = [1, 2] + g:idx = 'y' + l[1 : g:idx] = [0] + echo l + END + v9.CheckDefExecAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "y"']) + + v9.CheckDefFailure(["var l: list<number> = ['', true]"], 'E1012: Type mismatch; expected list<number> but got list<any>', 1) + v9.CheckDefFailure(["var l: list<list<number>> = [['', true]]"], 'E1012: Type mismatch; expected list<list<number>> but got list<list<any>>', 1) +enddef + +def Test_assign_dict() + var lines =<< trim END + var d: dict<string> = {} + d['key'] = 'value' + assert_equal('value', d['key']) + + d[123] = 'qwerty' + assert_equal('qwerty', d[123]) + assert_equal('qwerty', d['123']) + + var nrd: dict<number> = {} + for i in range(3) + nrd[i] = i + endfor + assert_equal({0: 0, 1: 1, 2: 2}, nrd) + + d.somekey = 'someval' + assert_equal({key: 'value', '123': 'qwerty', somekey: 'someval'}, d) + unlet d.somekey + assert_equal({key: 'value', '123': 'qwerty'}, d) + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefFailure(["var d: dict<number> = {a: '', b: true}"], 'E1012: Type mismatch; expected dict<number> but got dict<any>', 1) + v9.CheckDefFailure(["var d: dict<dict<number>> = {x: {a: '', b: true}}"], 'E1012: Type mismatch; expected dict<dict<number>> but got dict<dict<any>>', 1) + v9.CheckDefFailure(["var d = {x: 1}", "d[1 : 2] = {y: 2}"], 'E1165: Cannot use a range with an assignment: d[1 : 2] =', 2) +enddef + +def Test_assign_dict_unknown_type() + var lines =<< trim END + vim9script + var mylist = [] + mylist += [{one: 'one'}] + def Func() + var dd = mylist[0] + assert_equal('one', dd.one) + enddef + Func() + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var mylist = [[]] + mylist[0] += [{one: 'one'}] + def Func() + var dd = mylist[0][0] + assert_equal('one', dd.one) + enddef + Func() + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_assign_dict_with_op() + var lines =<< trim END + var ds: dict<string> = {a: 'x'} + ds['a'] ..= 'y' + ds.a ..= 'z' + assert_equal('xyz', ds.a) + + var dn: dict<number> = {a: 9} + dn['a'] += 2 + assert_equal(11, dn.a) + dn.a += 2 + assert_equal(13, dn.a) + + dn['a'] -= 3 + assert_equal(10, dn.a) + dn.a -= 2 + assert_equal(8, dn.a) + + dn['a'] *= 2 + assert_equal(16, dn.a) + dn.a *= 2 + assert_equal(32, dn.a) + + dn['a'] /= 3 + assert_equal(10, dn.a) + dn.a /= 2 + assert_equal(5, dn.a) + + dn['a'] %= 3 + assert_equal(2, dn.a) + dn.a %= 6 + assert_equal(2, dn.a) + + var dd: dict<dict<list<any>>> + dd.a = {} + dd.a.b = [0] + dd.a.b += [1] + assert_equal({a: {b: [0, 1]}}, dd) + + var dab = {a: ['b']} + dab.a[0] ..= 'c' + assert_equal({a: ['bc']}, dab) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_assign_list_with_op() + var lines =<< trim END + var ls: list<string> = ['x'] + ls[0] ..= 'y' + assert_equal('xy', ls[0]) + + var ln: list<number> = [9] + ln[0] += 2 + assert_equal(11, ln[0]) + + ln[0] -= 3 + assert_equal(8, ln[0]) + + ln[0] *= 2 + assert_equal(16, ln[0]) + + ln[0] /= 3 + assert_equal(5, ln[0]) + + ln[0] %= 3 + assert_equal(2, ln[0]) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_assign_with_op_fails() + var lines =<< trim END + var s = 'abc' + s[1] += 'x' + END + v9.CheckDefAndScriptFailure(lines, ['E1141:', 'E689:'], 2) + + lines =<< trim END + var s = 'abc' + s[1] ..= 'x' + END + v9.CheckDefAndScriptFailure(lines, ['E1141:', 'E689:'], 2) + + lines =<< trim END + var dd: dict<dict<list<any>>> + dd.a = {} + dd.a.b += [1] + END + v9.CheckDefExecAndScriptFailure(lines, 'E716:', 3) +enddef + +def Test_assign_lambda() + # check if assign a lambda to a variable which type is func or any. + var lines =<< trim END + vim9script + var FuncRef = () => 123 + assert_equal(123, FuncRef()) + var FuncRef_Func: func = () => 123 + assert_equal(123, FuncRef_Func()) + var FuncRef_Any: any = () => 123 + assert_equal(123, FuncRef_Any()) + var FuncRef_Number: func(): number = () => 321 + assert_equal(321, FuncRef_Number()) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + var Ref: func(number) + Ref = (j) => !j + END + v9.CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected func(number) but got func(any): bool') + + lines =<< trim END + echo filter([1, 2, 3], (_, v: string) => v + 1) + END + v9.CheckDefAndScriptFailure(lines, 'E1051:') +enddef + +def Test_heredoc() + # simple heredoc + var lines =<< trim END + var text =<< trim TEXT # comment + abc + TEXT + assert_equal(['abc'], text) + END + v9.CheckDefAndScriptSuccess(lines) + + # empty heredoc + lines =<< trim END + var text =<< trim TEXT + TEXT + assert_equal([], text) + END + v9.CheckDefAndScriptSuccess(lines) + + # heredoc with a single empty line + lines =<< trim END + var text =<< trim TEXT + + TEXT + assert_equal([''], text) + END + v9.CheckDefAndScriptSuccess(lines) + + v9.CheckDefFailure(['var lines =<< trim END X', 'END'], 'E488:') + v9.CheckDefFailure(['var lines =<< trim END " comment', 'END'], 'E488:') + + lines =<< trim [END] + def Func() + var&lines =<< trim END + x + x + enddef + defcompile + [END] + v9.CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') + delfunc! g:Func + + lines =<< trim [END] + def Func() + var lines =<< trim END + x + x + x + x + x + x + x + x + enddef + call Func() + [END] + v9.CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END') + delfunc! g:Func + + lines =<< trim END + var lines: number =<< trim STOP + aaa + bbb + STOP + END + v9.CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list<string>', 1) + + lines =<< trim END + var lines=<< STOP + xxx + STOP + END + v9.CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''=<<'' at "=<< STOP"', 1) + lines =<< trim END + var lines =<<STOP + xxx + STOP + END + v9.CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''=<<'' at "=<<STOP"', 1) + lines =<< trim END + var lines=<<STOP + xxx + STOP + END + v9.CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''=<<'' at "=<<STOP"', 1) +enddef + +def Test_var_func_call() + var lines =<< trim END + vim9script + func GetValue() + if exists('g:count') + let g:count += 1 + else + let g:count = 1 + endif + return 'this' + endfunc + var val: string = GetValue() + # env var is always a string + var env = $TERM + END + writefile(lines, 'Xfinished', 'D') + source Xfinished + # GetValue() is not called during discovery phase + assert_equal(1, g:count) + + unlet g:count +enddef + +def Test_var_missing_type() + var lines =<< trim END + vim9script + var name = g:unknown + END + v9.CheckScriptFailure(lines, 'E121:') + + lines =<< trim END + vim9script + var nr: number = 123 + var name = nr + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_var_declaration() + var lines =<< trim END + vim9script + var name: string + g:var_uninit = name + name = 'text' + g:var_test = name + # prefixing s: is not allowed + name = 'prefixed' + g:var_prefixed = name + + const FOO: number = 123 + assert_equal(123, FOO) + const FOOS = 'foos' + assert_equal('foos', FOOS) + final FLIST = [1] + assert_equal([1], FLIST) + FLIST[0] = 11 + assert_equal([11], FLIST) + + const g:FOOS = 'gfoos' + assert_equal('gfoos', g:FOOS) + final g:FLIST = [2] + assert_equal([2], g:FLIST) + g:FLIST[0] = 22 + assert_equal([22], g:FLIST) + + def SetGlobalConst() + const g:globConst = 123 + enddef + SetGlobalConst() + assert_equal(123, g:globConst) + assert_true(islocked('g:globConst')) + + const w:FOOS = 'wfoos' + assert_equal('wfoos', w:FOOS) + final w:FLIST = [3] + assert_equal([3], w:FLIST) + w:FLIST[0] = 33 + assert_equal([33], w:FLIST) + + var s:other: number + other = 1234 + g:other_var = other + + var xyz: string # comment + + # type is inferred + var dict = {['a']: 222} + def GetDictVal(key: any) + g:dict_val = dict[key] + enddef + GetDictVal('a') + + final adict: dict<string> = {} + def ChangeAdict() + adict.foo = 'foo' + enddef + ChangeAdict() + END + v9.CheckScriptSuccess(lines) + assert_equal('', g:var_uninit) + assert_equal('text', g:var_test) + assert_equal('prefixed', g:var_prefixed) + assert_equal(1234, g:other_var) + assert_equal(222, g:dict_val) + + unlet g:var_uninit + unlet g:var_test + unlet g:var_prefixed + unlet g:other_var + unlet g:globConst + unlet g:FOOS + unlet g:FLIST + unlet w:FOOS + unlet w:FLIST +enddef + +def Test_create_list_after_const() + const a = 1 + g:ll = [] + assert_equal(0, islocked('g:ll')) + unlet g:ll +enddef + +def Test_var_declaration_fails() + var lines =<< trim END + vim9script + final var: string + END + v9.CheckScriptFailure(lines, 'E1125:') + + lines =<< trim END + vim9script + const g:constvar = 'string' + g:constvar = 'xx' + END + v9.CheckScriptFailure(lines, 'E741:') + unlet g:constvar + + lines =<< trim END + vim9script + var name = 'one' + lockvar name + def SetLocked() + name = 'two' + enddef + SetLocked() + END + v9.CheckScriptFailure(lines, 'E741: Value is locked: name', 1) + + lines =<< trim END + let s:legacy = 'one' + lockvar s:legacy + def SetLocked() + s:legacy = 'two' + enddef + call SetLocked() + END + v9.CheckScriptFailure(lines, 'E741: Value is locked: s:legacy', 1) + + lines =<< trim END + vim9script + def SetGlobalConst() + const g:globConst = 123 + enddef + SetGlobalConst() + g:globConst = 234 + END + v9.CheckScriptFailure(lines, 'E741: Value is locked: g:globConst', 6) + unlet g:globConst + + lines =<< trim END + vim9script + const cdict: dict<string> = {} + def Change() + cdict.foo = 'foo' + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E46:') + + lines =<< trim END + vim9script + final w:finalvar = [9] + w:finalvar = [8] + END + v9.CheckScriptFailure(lines, 'E1122:') + unlet w:finalvar + + lines =<< trim END + vim9script + const var: string + END + v9.CheckScriptFailure(lines, 'E1021:') + + lines =<< trim END + vim9script + var 9var: string + END + v9.CheckScriptFailure(lines, 'E488:') + + v9.CheckDefFailure(['var foo.bar = 2'], 'E1087:') + v9.CheckDefFailure(['var foo[3] = 2'], 'E1087:') + v9.CheckDefFailure(['const foo: number'], 'E1021:') + + lines =<< trim END + va foo = 123 + END + v9.CheckDefAndScriptFailure(lines, 'E1065:', 1) + + lines =<< trim END + var foo: func(number + END + v9.CheckDefAndScriptFailure(lines, 'E110:', 1) + + lines =<< trim END + var foo: func(number): func( + END + v9.CheckDefAndScriptFailure(lines, 'E110:', 1) + + for type in ['num_ber', + 'anys', 'ani', + 'bools', 'boel', + 'blobs', 'blub', + 'channels', 'channol', + 'dicts', 'duct', + 'floats', 'floot', + 'funcs', 'funk', + 'jobs', 'jop', + 'lists', 'last', + 'numbers', 'numbar', + 'strings', 'strung', + 'voids', 'viod'] + v9.CheckDefAndScriptFailure([$'var foo: {type}'], 'E1010:', 1) + endfor +enddef + +def Test_var_declaration_inferred() + # check that type is set on the list so that extend() fails + var lines =<< trim END + vim9script + def GetList(): list<number> + var l = [1, 2, 3] + return l + enddef + echo GetList()->extend(['x']) + END + v9.CheckScriptFailure(lines, 'E1013:', 6) + + lines =<< trim END + vim9script + def GetNr(): number + return 5 + enddef + def TestOne() + var some = [function('len'), GetNr] + g:res = typename(some) + enddef + TestOne() + assert_equal('list<func(): number>', g:res) + + def TestTwo() + var some = [function('len'), GetNr] + g:res = typename(some) + enddef + TestTwo() + assert_equal('list<func(): number>', g:res) + unlet g:res + + # FIXME: why is the type different? + var first = [function('len'), GetNr] + assert_equal('list<func(...): number>', typename(first)) + var second = [GetNr, function('len')] + assert_equal('list<func(...): number>', typename(second)) + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_script_local_in_legacy() + # OK to define script-local later but before compiling + var lines =<< trim END + def SetLater() + legvar = 'two' + enddef + let s:legvar = 'one' + defcompile + call SetLater() + call assert_equal('two', s:legvar) + END + v9.CheckScriptSuccess(lines) + + # OK to leave out s: prefix when script-local already defined + lines =<< trim END + let s:legvar = 'one' + def SetNoPrefix() + legvar = 'two' + enddef + call SetNoPrefix() + call assert_equal('two', s:legvar) + END + v9.CheckScriptSuccess(lines) + + # Not OK to leave out s: prefix when script-local defined after compiling + lines =<< trim END + def SetLaterNoPrefix() + legvar = 'two' + enddef + defcompile + let s:legvar = 'one' + END + v9.CheckScriptFailure(lines, 'E476:', 1) + + edit! Xslfile + lines =<< trim END + var edit: bool + legacy edit + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_var_type_check() + var lines =<< trim END + vim9script + var name: string + name = 1234 + END + v9.CheckScriptFailure(lines, 'E1012:') + + lines =<< trim END + vim9script + var name:string + END + v9.CheckScriptFailure(lines, 'E1069:') + + v9.CheckDefAndScriptFailure(['var n:number = 42'], 'E1069:') + + lines =<< trim END + vim9script + var name: asdf + END + v9.CheckScriptFailure(lines, 'E1010:') + + lines =<< trim END + vim9script + var l: list<number> + l = [] + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var d: dict<number> + d = {} + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var d = {a: 1, b: [2]} + def Func(b: bool) + var l: list<number> = b ? d.b : [3] + enddef + defcompile + END + v9.CheckScriptSuccess(lines) +enddef + +let g:dict_number = #{one: 1, two: 2} + +def Test_var_list_dict_type() + var ll: list<number> + ll = [1, 2, 2, 3, 3, 3]->uniq() + ll->assert_equal([1, 2, 3]) + + var dd: dict<number> + dd = g:dict_number + dd->assert_equal(g:dict_number) + + var lines =<< trim END + var ll: list<number> + ll = [1, 2, 3]->map('"one"') + END + v9.CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<string>') +enddef + +def Test_cannot_use_let() + v9.CheckDefAndScriptFailure(['let a = 34'], 'E1126:', 1) +enddef + +def Test_unlet() + g:somevar = 'yes' + assert_true(exists('g:somevar')) + unlet g:somevar + assert_false(exists('g:somevar')) + unlet! g:somevar + + # also works for script-local variable in legacy Vim script + s:somevar = 'legacy' + assert_true(exists('s:somevar')) + unlet s:somevar + assert_false(exists('s:somevar')) + unlet! s:somevar + + if 0 + unlet g:does_not_exist + endif + + v9.CheckDefExecFailure(['unlet v:notfound.key'], 'E1001:') + + v9.CheckDefExecFailure([ + 'var dd = 111', + 'unlet dd', + ], 'E1081:', 2) + + # dict unlet + var dd = {a: 1, b: 2, c: 3, 4: 4} + unlet dd['a'] + unlet dd.c + unlet dd[4] + assert_equal({b: 2}, dd) + + # null key works like empty string + dd = {'': 1, x: 9} + unlet dd[null_string] + assert_equal({x: 9}, dd) + + # list unlet + var ll = [1, 2, 3, 4] + unlet ll[1] + unlet ll[-1] + assert_equal([1, 3], ll) + + ll = [1, 2, 3, 4] + unlet ll[0 : 1] + assert_equal([3, 4], ll) + + ll = [1, 2, 3, 4] + unlet ll[2 : 8] + assert_equal([1, 2], ll) + + ll = [1, 2, 3, 4] + unlet ll[-2 : -1] + assert_equal([1, 2], ll) + + g:nrdict = {1: 1, 2: 2} + g:idx = 1 + unlet g:nrdict[g:idx] + assert_equal({2: 2}, g:nrdict) + unlet g:nrdict + unlet g:idx + + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'll[1 : 2] = 7', + ], 'E1012: Type mismatch; expected list<number> but got number', 2) + v9.CheckDefFailure([ + 'var dd = {a: 1}', + 'unlet dd["a" : "a"]', + ], 'E1166:', 2) + v9.CheckDefExecFailure([ + 'unlet g:adict[0 : 1]', + ], 'E1148:', 1) + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0:1]', + ], 'E1004:', 2) + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0 :1]', + ], 'E1004:', 2) + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0: 1]', + ], 'E1004:', 2) + + v9.CheckDefExecFailure([ + 'g:ll = [1, 2]', + 'g:idx = "x"', + 'unlet g:ll[g:idx]', + ], 'E1029: Expected number but got string', 3) + + v9.CheckDefExecFailure([ + 'g:ll = [1, 2, 3]', + 'g:idx = "x"', + 'unlet g:ll[g:idx : 2]', + ], 'E1029: Expected number but got string', 3) + + v9.CheckDefExecFailure([ + 'g:ll = [1, 2, 3]', + 'g:idx = "x"', + 'unlet g:ll[0 : g:idx]', + ], 'E1029: Expected number but got string', 3) + + # command recognized as assignment when skipping, should not give an error + v9.CheckScriptSuccess([ + 'vim9script', + 'for i in []', + " put =''", + 'endfor']) + + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll["x" : 1]', + ], 'E1012:', 2) + v9.CheckDefFailure([ + 'var ll = [1, 2]', + 'unlet ll[0 : "x"]', + ], 'E1012:', 2) + + # list of dict unlet + var dl = [{a: 1, b: 2}, {c: 3}] + unlet dl[0]['b'] + assert_equal([{a: 1}, {c: 3}], dl) + + v9.CheckDefExecFailure([ + 'var ll = test_null_list()', + 'unlet ll[0]', + ], 'E684:', 2) + v9.CheckDefExecFailure([ + 'var ll = [1]', + 'unlet ll[2]', + ], 'E684:', 2) + v9.CheckDefExecFailure([ + 'var ll = [1]', + 'unlet ll[g:astring]', + ], 'E1012:', 2) + v9.CheckDefExecFailure([ + 'var dd = test_null_dict()', + 'unlet dd["a"]', + ], 'E716:', 2) + v9.CheckDefExecFailure([ + 'var dd = {a: 1}', + 'unlet dd["b"]', + ], 'E716:', 2) + v9.CheckDefExecFailure([ + 'var dd = {a: 1}', + 'unlet dd[g:alist]', + ], 'E1105:', 2) + + v9.CheckDefExecFailure([ + 'g:dd = {"a": 1, 2: 2}', + 'unlet g:dd[0z11]', + ], 'E1029:', 2) + v9.CheckDefExecFailure([ + 'g:str = "a string"', + 'unlet g:str[0]', + ], 'E1148: Cannot index a string', 2) + + # can compile unlet before variable exists + g:someDict = {key: 'val'} + var k = 'key' + unlet g:someDict[k] + assert_equal({}, g:someDict) + unlet g:someDict + assert_false(exists('g:someDict')) + + v9.CheckScriptFailure([ + 'vim9script', + 'var svar = 123', + 'unlet svar', + ], 'E1081:') + v9.CheckScriptFailure([ + 'vim9script', + 'var svar = 123', + 'unlet s:svar', + ], 'E1268:') + v9.CheckScriptFailure([ + 'vim9script', + 'var svar = 123', + 'def Func()', + ' unlet svar', + 'enddef', + 'defcompile', + ], 'E1081:') + v9.CheckScriptFailure([ + 'vim9script', + 'var svar = 123', + 'func Func()', + ' unlet s:svar', + 'endfunc', + 'Func()', + ], 'E1081:') + v9.CheckScriptFailure([ + 'vim9script', + 'var svar = 123', + 'def Func()', + ' unlet s:svar', + 'enddef', + 'defcompile', + ], 'E1081:') + + v9.CheckScriptFailure([ + 'vim9script', + 'def Delcount(dict: dict<any>)', + ' unlet dict.count', + 'enddef', + 'Delcount(v:)', + ], 'E742:') + + v9.CheckScriptFailure([ + 'vim9script', + 'def DelChangedtick(dict: dict<any>)', + ' unlet dict.changedtick', + 'enddef', + 'DelChangedtick(b:)', + ], 'E795:') + + writefile(['vim9script', 'export var svar = 1234'], 'XunletExport.vim', 'D') + var lines =<< trim END + vim9script + import './XunletExport.vim' as exp + def UnletSvar() + unlet exp.svar + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E1260:', 1) + + $ENVVAR = 'foobar' + assert_equal('foobar', $ENVVAR) + unlet $ENVVAR + assert_equal('', $ENVVAR) +enddef + +def Test_expr_error_no_assign() + var lines =<< trim END + vim9script + var x = invalid + echo x + END + v9.CheckScriptFailureList(lines, ['E121:', 'E121:']) + + lines =<< trim END + vim9script + var x = 1 / 0 + echo x + END + v9.CheckScriptFailure(lines, 'E1154:') + + lines =<< trim END + vim9script + var x = 1 % 0 + echo x + END + v9.CheckScriptFailure(lines, 'E1154:') + + lines =<< trim END + var x: string 'string' + END + v9.CheckDefAndScriptFailure(lines, 'E488:') +enddef + + +def Test_assign_command_modifier() + var lines =<< trim END + var verbose = 0 + verbose = 1 + assert_equal(1, verbose) + silent verbose = 2 + assert_equal(2, verbose) + silent verbose += 2 + assert_equal(4, verbose) + silent verbose -= 1 + assert_equal(3, verbose) + + var topleft = {one: 1} + sandbox topleft.one = 3 + assert_equal({one: 3}, topleft) + leftabove topleft[' '] = 4 + assert_equal({one: 3, ' ': 4}, topleft) + + var x: number + var y: number + silent [x, y] = [1, 2] + assert_equal(1, x) + assert_equal(2, y) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_assign_alt_buf_register() + var lines =<< trim END + edit 'file_b1' + var b1 = bufnr() + edit 'file_b2' + var b2 = bufnr() + assert_equal(b1, bufnr('#')) + @# = b2 + assert_equal(b2, bufnr('#')) + END + v9.CheckDefAndScriptSuccess(lines) +enddef + +def Test_script_funcref_case() + var lines =<< trim END + var Len = (s: string): number => len(s) + 1 + assert_equal(5, Len('asdf')) + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var len = (s: string): number => len(s) + 1 + END + v9.CheckDefAndScriptFailure(lines, 'E704:') + + lines =<< trim END + vim9script + var Len = (s: string): number => len(s) + 2 + assert_equal(6, Len('asdf')) + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var len = (s: string): number => len(s) + 1 + END + v9.CheckScriptFailure(lines, 'E704:') +enddef + +def Test_script_funcref_runtime_type_check() + var lines =<< trim END + vim9script + def FuncWithNumberArg(n: number) + enddef + def Test() + var Ref: func(string) = function(FuncWithNumberArg) + enddef + defcompile + END + # OK at compile time + v9.CheckScriptSuccess(lines) + + # Type check fails at runtime + v9.CheckScriptFailure(lines + ['Test()'], 'E1012: Type mismatch; expected func(string) but got func(number)') +enddef + +def Test_inc_dec() + var lines =<< trim END + var nr = 7 + ++nr + assert_equal(8, nr) + --nr + assert_equal(7, nr) + ++nr | ++nr + assert_equal(9, nr) + ++nr # comment + assert_equal(10, nr) + + var ll = [1, 2] + --ll[0] + ++ll[1] + assert_equal([0, 3], ll) + + g:count = 1 + ++g:count + --g:count + assert_equal(1, g:count) + unlet g:count + END + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var nr = 7 + ++ nr + END + v9.CheckDefAndScriptFailure(lines, "E1202: No white space allowed after '++': ++ nr") +enddef + +def Test_abort_after_error() + # should abort after strpart() fails, not give another type error + var lines =<< trim END + vim9script + var x: string + x = strpart(1, 2) + END + writefile(lines, 'Xtestscript', 'D') + var expected = 'E1174: String required for argument 1' + assert_fails('so Xtestscript', [expected, expected], 3) +enddef + +def Test_using_s_var_in_function() + var lines =<< trim END + vim9script + var scriptlevel = 123 + def SomeFunc() + echo s:scriptlevel + enddef + SomeFunc() + END + v9.CheckScriptFailure(lines, 'E1268:') + + # OK in legacy script + lines =<< trim END + let s:scriptlevel = 123 + def s:SomeFunc() + echo s:scriptlevel + enddef + call s:SomeFunc() + END + v9.CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + var scriptlevel = 123 + def SomeFunc() + s:scriptlevel = 456 + enddef + SomeFunc() + END + v9.CheckScriptFailure(lines, 'E1268:') + + # OK in legacy script + lines =<< trim END + let s:scriptlevel = 123 + def s:SomeFunc() + s:scriptlevel = 456 + enddef + call s:SomeFunc() + call assert_equal(456, s:scriptlevel) + END + v9.CheckScriptSuccess(lines) +enddef + +let g:someVar = 'X' + +" Test for heredoc with Vim expressions. +" This messes up highlighting, keep it near the end. +def Test_heredoc_expr() + var lines =<< trim CODE + var s = "local" + var a1 = "1" + var a2 = "2" + var a3 = "3" + var a4 = "" + var code =<< trim eval END + var a = {5 + 10} + var b = {min([10, 6])} + {max([4, 6])} + var c = "{s}" + var d = x{a1}x{a2}x{a3}x{a4} + END + assert_equal(['var a = 15', 'var b = 6 + 6', 'var c = "local"', 'var d = x1x2x3x'], code) + CODE + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim CODE + var code =<< eval trim END + var s = "{$SOME_ENV_VAR}" + END + assert_equal(['var s = "somemore"'], code) + CODE + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim CODE + var code =<< eval END + var s = "{$SOME_ENV_VAR}" + END + assert_equal([' var s = "somemore"'], code) + CODE + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim CODE + var code =<< eval trim END + let a = {{abc}} + let b = {g:someVar} + let c = {{ + END + assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code) + CODE + v9.CheckDefAndScriptSuccess(lines) + + lines =<< trim LINES + var text =<< eval trim END + let b = { + END + LINES + v9.CheckDefAndScriptFailure(lines, "E1279: Missing '}'") + + lines =<< trim LINES + var text =<< eval trim END + let b = {abc + END + LINES + v9.CheckDefAndScriptFailure(lines, "E1279: Missing '}'") + + lines =<< trim LINES + var text =<< eval trim END + let b = {} + END + LINES + v9.CheckDefAndScriptFailure(lines, 'E15: Invalid expression: "}"') +enddef + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker |