Mercurial > vim
view src/testdir/test_listdict.vim @ 33815:08f9e1eac4cf v9.0.2123
patch 9.0.2123: Problem with initializing the length of range() lists
Commit: https://github.com/vim/vim/commit/df63da98d8dc284b1c76cfe1b17fa0acbd6094d8
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Nov 23 20:14:28 2023 +0100
patch 9.0.2123: Problem with initializing the length of range() lists
Problem: Problem with initializing the length of range() lists
Solution: Set length explicitly when it shouldn't contain any items
range() may cause a wrong calculation of list length, which may later
then cause a segfault in list_find(). This is usually not a problem,
because range_list_materialize() calculates the length, when it
materializes the list.
In addition, in list_find() when the length of the range was wrongly
initialized, it may seem to be valid, so the check for list index
out-of-bounds will not be true, because it is called before the list is
actually materialized. And so we may eventually try to access a null
pointer, causing a segfault.
So this patch does 3 things:
- In f_range(), when we know that the list should be empty, explicitly
set the list->lv_len value to zero. This should happen, when
start is larger than end (in case the stride is positive) or
end is larger than start when the stride is negative.
This should fix the underlying issue properly. However,
- as a safety measure, let's check that the requested index is not
out of range one more time, after the list has been materialized
and return NULL in case it suddenly is.
- add a few more tests to verify the behaviour.
fixes: #13557
closes: #13563
Co-authored-by: Tim Pope <tpope@github.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 23 Nov 2023 20:30:07 +0100 |
parents | 9dee279ce1c4 |
children | cd7acb9bc4fd |
line wrap: on
line source
" Tests for the List and Dict types scriptencoding utf-8 import './vim9.vim' as v9 func TearDown() " Run garbage collection after every test call test_garbagecollect_now() endfunc " Tests for List type " List creation func Test_list_create() " Creating List directly with different types let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] call assert_equal("[1, 'as''d', [1, 2, function('strlen')], {'a': 1}]", string(l)) call assert_equal({'a' : 1}, l[-1]) call assert_equal(1, l[-4]) let x = 10 try let x = l[-5] catch call assert_match('E684:', v:exception) endtry call assert_equal(10, x) endfunc " This was allowed in legacy Vim script let s:list_with_spaces = [1 , 2 , 3] " List slices func Test_list_slice() let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[:]) call assert_equal(['as''d', [1, 2, function('strlen')], {'a': 1}], l[1:]) call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2]) call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) call assert_equal([], l[8:-1]) call assert_equal([], l[0:-10]) " perform an operation on a list slice let l = [1, 2, 3] let l[:1] += [1, 2] let l[2:] -= [1] call assert_equal([2, 4, 2], l) let lines =<< trim END VAR l = [1, 2] call assert_equal([1, 2], l[:]) call assert_equal([2], l[-1 : -1]) call assert_equal([1, 2], l[-2 : -1]) END call v9.CheckLegacyAndVim9Success(lines) let l = [1, 2] call assert_equal([], l[-3 : -1]) let lines =<< trim END var l = [1, 2] assert_equal([1, 2], l[-3 : -1]) END call v9.CheckDefAndScriptSuccess(lines) endfunc " List identity func Test_list_identity() let lines =<< trim END VAR l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] VAR ll = l VAR lx = copy(l) call assert_true(l == ll) call assert_false(l isnot ll) call assert_true(l is ll) call assert_true(l == lx) call assert_false(l is lx) call assert_true(l isnot lx) END call v9.CheckLegacyAndVim9Success(lines) endfunc " removing items with :unlet func Test_list_unlet() let lines =<< trim END VAR l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] unlet l[2] call assert_equal([1, 'as''d', {'a': 1}], l) LET l = range(8) unlet l[: 3] unlet l[1 :] call assert_equal([4], l) #" removing items out of range: silently skip items that don't exist LET l = [0, 1, 2, 3] unlet l[2 : 2] call assert_equal([0, 1, 3], l) LET l = [0, 1, 2, 3] unlet l[2 : 3] call assert_equal([0, 1], l) LET l = [0, 1, 2, 3] unlet l[2 : 4] call assert_equal([0, 1], l) LET l = [0, 1, 2, 3] unlet l[2 : 5] call assert_equal([0, 1], l) LET l = [0, 1, 2, 3] unlet l[-2 : 2] call assert_equal([0, 1, 3], l) LET l = [0, 1, 2, 3] unlet l[-3 : 2] call assert_equal([0, 3], l) LET l = [0, 1, 2, 3] unlet l[-4 : 2] call assert_equal([3], l) LET l = [0, 1, 2, 3] unlet l[-5 : 2] call assert_equal([3], l) LET l = [0, 1, 2, 3] unlet l[-6 : 2] call assert_equal([3], l) END call v9.CheckLegacyAndVim9Success(lines) let l = [0, 1, 2, 3] unlet l[2:2] call assert_equal([0, 1, 3], l) let l = [0, 1, 2, 3] unlet l[2:3] call assert_equal([0, 1], l) let lines =<< trim END VAR l = [0, 1, 2, 3] unlet l[2 : 1] END call v9.CheckLegacyAndVim9Failure(lines, 'E684:') let lines =<< trim END VAR l = [0, 1, 2, 3] unlet l[-1 : 2] END call v9.CheckLegacyAndVim9Failure(lines, 'E684:') endfunc " assignment to a list func Test_list_assign() let lines =<< trim END VAR l = [0, 1, 2, 3] VAR va = 0 VAR vb = 0 LET [va, vb] = l[2 : 3] call assert_equal([2, 3], [va, vb]) END call v9.CheckLegacyAndVim9Success(lines) let lines =<< trim END let l = [0, 1, 2, 3] let [va, vb] = l END call v9.CheckScriptFailure(lines, 'E687:') let lines =<< trim END var l = [0, 1, 2, 3] var va = 0 var vb = 0 [va, vb] = l END call v9.CheckScriptFailure(['vim9script'] + lines, 'E687:') call v9.CheckDefExecFailure(lines, 'E1093: Expected 2 items but got 4') let lines =<< trim END let l = [0, 1, 2, 3] let [va, vb] = l[1:1] END call v9.CheckScriptFailure(lines, 'E688:') let lines =<< trim END var l = [0, 1, 2, 3] var va = 0 var vb = 0 [va, vb] = l[1 : 1] END call v9.CheckScriptFailure(['vim9script'] + lines, 'E688:') call v9.CheckDefExecFailure(lines, 'E1093: Expected 2 items but got 1') endfunc " test for range assign func Test_list_range_assign() let lines =<< trim END VAR l = [0] LET l[:] = [1, 2] call assert_equal([1, 2], l) LET l[-4 : -1] = [5, 6] call assert_equal([5, 6], l) END call v9.CheckLegacyAndVim9Success(lines) let lines =<< trim END var l = [7] l[:] = ['text'] END call v9.CheckDefAndScriptFailure(lines, 'E1012:', 2) endfunc func Test_list_items() let r = [] let l = ['a', 'b', 'c'] for [idx, val] in items(l) call extend(r, [[idx, val]]) endfor call assert_equal([[0, 'a'], [1, 'b'], [2, 'c']], r) call assert_fails('call items(3)', 'E1225:') endfunc func Test_string_items() let r = [] let s = 'ábツ' for [idx, val] in items(s) call extend(r, [[idx, val]]) endfor call assert_equal([[0, 'á'], [1, 'b'], [2, 'ツ']], r) endfunc " Test removing items in list func Test_list_func_remove() let lines =<< trim END #" Test removing 1 element VAR l = [1, 2, 3, 4] call assert_equal(1, remove(l, 0)) call assert_equal([2, 3, 4], l) LET l = [1, 2, 3, 4] call assert_equal(2, remove(l, 1)) call assert_equal([1, 3, 4], l) LET l = [1, 2, 3, 4] call assert_equal(4, remove(l, -1)) call assert_equal([1, 2, 3], l) #" Test removing range of element(s) LET l = [1, 2, 3, 4] call assert_equal([3], remove(l, 2, 2)) call assert_equal([1, 2, 4], l) LET l = [1, 2, 3, 4] call assert_equal([2, 3], remove(l, 1, 2)) call assert_equal([1, 4], l) LET l = [1, 2, 3, 4] call assert_equal([2, 3], remove(l, -3, -2)) call assert_equal([1, 4], l) END call v9.CheckLegacyAndVim9Success(lines) " Test invalid cases let l = [1, 2, 3, 4] call assert_fails("call remove(l, 5)", 'E684:') call assert_fails("call remove(l, 1, 5)", 'E684:') call assert_fails("call remove(l, 3, 2)", 'E16:') call assert_fails("call remove(1, 0)", 'E896:') call assert_fails("call remove(l, l)", 'E745:') endfunc " List add() function func Test_list_add() let lines =<< trim END VAR l = [] call add(l, 1) call add(l, [2, 3]) call add(l, []) call add(l, test_null_list()) call add(l, {'k': 3}) call add(l, {}) call add(l, test_null_dict()) call assert_equal([1, [2, 3], [], [], {'k': 3}, {}, {}], l) END call v9.CheckLegacyAndVim9Success(lines) " weird legacy behavior call assert_equal(1, add(test_null_list(), 4)) endfunc " Tests for Dictionary type func Test_dict() " Creating Dictionary directly with different types let lines =<< trim END VAR d = {'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}, } call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d)) call assert_equal('asd', d.1) call assert_equal(['-1', '1', 'b'], sort(keys(d))) call assert_equal(['asd', [1, 2, function('strlen')], {'a': 1}], values(d)) call extend(d, {3: 33, 1: 99}) call extend(d, {'b': 'bbb', 'c': 'ccc'}, "keep") call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d) END call v9.CheckLegacyAndVim9Success(lines) let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d)) let v = [] for [key, val] in items(d) call extend(v, [key, val]) unlet key val endfor call assert_equal(['1','asd','b',[1, 2, function('strlen')],'-1',{'a': 1}], v) call extend(d, {3: 33, 1: 99}) call assert_fails("call extend(d, {3:333,4:444}, 'error')", 'E737:') " duplicate key call assert_fails("let d = {'k' : 10, 'k' : 20}", 'E721:') " missing comma call assert_fails("let d = {'k' : 10 'k' : 20}", 'E722:') " missing curly brace call assert_fails("let d = {'k' : 10,", 'E723:') " invalid key call assert_fails('let d = #{++ : 10}', 'E15:') " wrong type for key call assert_fails('let d={[] : 10}', 'E730:') " undefined variable as value call assert_fails("let d={'k' : i}", 'E121:') " allow key starting with number at the start, not a curly expression call assert_equal({'1foo': 77}, #{1foo: 77}) " #{expr} is not a curly expression let x = 'x' call assert_equal(#{g: x}, #{g:x}) endfunc " This was allowed in legacy Vim script let s:dict_with_spaces = {'one' : 1 , 'two' : 2 , 'three' : 3} let s:dict_with_spaces_lit = #{one : 1 , two : 2 , three : 3} " Dictionary identity func Test_dict_identity() let lines =<< trim END VAR d = {'1': 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1}, } VAR dd = d VAR dx = copy(d) call assert_true(d == dd) call assert_false(d isnot dd) call assert_true(d is dd) call assert_true(d == dx) call assert_false(d is dx) call assert_true(d isnot dx) END call v9.CheckLegacyAndVim9Success(lines) endfunc " removing items with :unlet func Test_dict_unlet() let lines =<< trim END VAR d = {'b': 'bbb', '1': 99, '3': 33, '-1': {'a': 1}} unlet d.b unlet d[-1] call assert_equal({'1': 99, '3': 33}, d) END call v9.CheckLegacyAndVim9Success(lines) endfunc " manipulating a big Dictionary (hashtable.c has a border of 1000 entries) func Test_dict_big() let d = {} for i in range(1500) let d[i] = 3000 - i endfor call assert_equal([3000, 2900, 2001, 1600, 1501], [d[0], d[100], d[999], d[1400], d[1499]]) let str = '' try let n = d[1500] catch let str = substitute(v:exception, '\v(.{14}).*( "\d{4}").*', '\1\2', '') endtry call assert_equal('Vim(let):E716: "1500"', str) " lookup each item for i in range(1500) call assert_equal(3000 - i, d[i]) endfor let i += 1 " delete even items while i >= 2 let i -= 2 unlet d[i] endwhile call assert_equal('NONE', get(d, 1500 - 100, 'NONE')) call assert_equal(2999, d[1]) " delete odd items, checking value, one intentionally wrong let d[33] = 999 let i = 1 while i < 1500 if i != 33 call assert_equal(3000 - i, d[i]) else call assert_equal(999, d[i]) endif unlet d[i] let i += 2 endwhile call assert_equal({}, d) unlet d endfunc " Dictionary function func Test_dict_func() let d = {} func d.func(a) dict return a:a . len(self.data) endfunc let d.data = [1,2,3] call assert_equal('len: 3', d.func('len: ')) let x = d.func('again: ') call assert_equal('again: 3', x) let Fn = d.func call assert_equal('xxx3', Fn('xxx')) endfunc func Test_dict_assign() let d = {} let d.1 = 1 let d._ = 2 call assert_equal({'1': 1, '_': 2}, d) let lines =<< trim END VAR d = {} LET d.a = 1 LET d._ = 2 call assert_equal({'a': 1, '_': 2}, d) END call v9.CheckLegacyAndVim9Success(lines) let lines =<< trim END let n = 0 let n.key = 3 END call v9.CheckScriptFailure(lines, 'E1203: Dot not allowed after a number: n.key = 3') let lines =<< trim END vim9script var n = 0 n.key = 3 END call v9.CheckScriptFailure(lines, 'E1203: Dot not allowed after a number: n.key = 3') let lines =<< trim END var n = 0 n.key = 3 END call v9.CheckDefFailure(lines, 'E1141:') endfunc " Function in script-local List or Dict func Test_script_local_dict_func() let g:dict = {} function g:dict.func() dict return 'g:dict.func' . self.foo[1] . self.foo[0]('asdf') endfunc let g:dict.foo = ['-', 2, 3] call insert(g:dict.foo, function('strlen')) call assert_equal('g:dict.func-4', g:dict.func()) unlet g:dict endfunc " Test removing items in a dictionary func Test_dict_func_remove() let lines =<< trim END VAR d = {1: 'a', 2: 'b', 3: 'c'} call assert_equal('b', remove(d, 2)) call assert_equal({1: 'a', 3: 'c'}, d) END call v9.CheckLegacyAndVim9Success(lines) let lines =<< trim END VAR d = {1: 'a', 3: 'c'} call remove(d, 1, 2) END call v9.CheckLegacyAndVim9Failure(lines, 'E118:') let lines =<< trim END VAR d = {1: 'a', 3: 'c'} call remove(d, 'a') END call v9.CheckLegacyAndVim9Failure(lines, 'E716:') let lines =<< trim END let d = {'a-b': 55} echo d.a-b END call v9.CheckScriptFailure(lines, 'E716: Key not present in Dictionary: "a"') let lines =<< trim END vim9script var d = {'a-b': 55} echo d.a-b END call v9.CheckScriptFailure(lines, 'E716: Key not present in Dictionary: "a"') let lines =<< trim END var d = {'a-b': 55} echo d.a-b END call v9.CheckDefFailure(lines, 'E1004: White space required before and after ''-''') let lines =<< trim END let d = {1: 'a', 3: 'c'} call remove(d, []) END call v9.CheckScriptFailure(lines, 'E730:') let lines =<< trim END vim9script var d = {1: 'a', 3: 'c'} call remove(d, []) END call v9.CheckScriptFailure(lines, 'E1220: String or Number required for argument 2') let lines =<< trim END var d = {1: 'a', 3: 'c'} call remove(d, []) END call v9.CheckDefExecFailure(lines, 'E1013: Argument 2: type mismatch, expected string but got list<unknown>') endfunc " Nasty: remove func from Dict that's being called (works) func Test_dict_func_remove_in_use() let d = {1:1} func d.func(a) return "a:" . a:a endfunc let expected = 'a:' . string(get(d, 'func')) call assert_equal(expected, d.func(string(remove(d, 'func')))) " similar, in a way it also works in Vim9 let lines =<< trim END VAR d = {1: 1, 2: 'x'} func GetArg(a) return "a:" .. a:a endfunc LET d.func = function('GetArg') VAR expected = 'a:' .. string(get(d, 'func')) call assert_equal(expected, d.func(string(remove(d, 'func')))) END call v9.CheckTransLegacySuccess(lines) call v9.CheckTransVim9Success(lines) endfunc func Test_dict_literal_keys() call assert_equal({'one': 1, 'two2': 2, '3three': 3, '44': 4}, #{one: 1, two2: 2, 3three: 3, 44: 4},) " why *{} cannot be used for a literal dictionary let blue = 'blue' call assert_equal('6', trim(execute('echo 2 *{blue: 3}.blue'))) endfunc " Nasty: deepcopy() dict that refers to itself (fails when noref used) func Test_dict_deepcopy() let lines =<< trim END VAR d = {1: 1, 2: '2'} VAR l = [4, d, 6] LET d[3] = l VAR dc = deepcopy(d) call deepcopy(d, 1) END call v9.CheckLegacyAndVim9Failure(lines, 'E698:') let lines =<< trim END VAR d = {1: 1, 2: '2'} VAR l = [4, d, 6] LET d[3] = l VAR l2 = [0, l, l, 3] LET l[1] = l2 VAR l3 = deepcopy(l2) call assert_true(l3[1] is l3[2]) END call v9.CheckLegacyAndVim9Success(lines) call assert_fails("call deepcopy([1, 2], 2)", 'E1212:') endfunc " Locked variables func Test_list_locked_var() " Not tested with :def function, local vars cannot be locked. let lines =<< trim END VAR expected = [ \ [['1000-000', 'ppppppF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppppppF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1100-100', 'ppFppFF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1110-110', 'pFFpFFF'], \ ['0010-010', 'pFppFpp'], \ ['0000-000', 'ppppppp']], \ [['1111-111', 'FFFFFFF'], \ ['0011-011', 'FFpFFpp'], \ ['0000-000', 'ppppppp']] \ ] for depth in range(5) for u in range(3) VAR l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] exe "lockvar " .. depth .. " l" if u == 1 exe "unlockvar l" elseif u == 2 exe "unlockvar " .. depth .. " l" endif VAR ps = islocked("l") .. islocked("l[1]") .. islocked("l[1][1]") .. islocked("l[1][1][0]") .. '-' .. islocked("l[2]") .. islocked("l[2]['6']") .. islocked("l[2]['6'][7]") call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) LET ps = '' try LET l[1][1][0] = 99 LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l[1][1] = [99] LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l[1] = [99] LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l[2]['6'][7] = 99 LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l[2][6] = {99: 99} LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l[2] = {99: 99} LET ps ..= 'p' catch LET ps ..= 'F' endtry try LET l = [99] LET ps ..= 'p' catch LET ps ..= 'F' endtry call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth) unlock! l endfor endfor END call v9.CheckTransLegacySuccess(lines) call v9.CheckTransVim9Success(lines) call assert_fails("let x=islocked('a b')", 'E488:') let mylist = [1, 2, 3] call assert_fails("let x = islocked('mylist[1:2]')", 'E786:') let mydict = {'k' : 'v'} call assert_fails("let x = islocked('mydict.a')", 'E716:') endfunc " Unletting locked variables func Test_list_locked_var_unlet() " Not tested with Vim9: script and local variables cannot be unlocked let expected = [ \ [['1000-000', 'ppppppp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppFppFp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1100-100', 'pFFpFFp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1110-110', 'FFFFFFp'], \ ['0010-010', 'FppFppp'], \ ['0000-000', 'ppppppp']], \ [['1111-111', 'FFFFFFp'], \ ['0011-011', 'FppFppp'], \ ['0000-000', 'ppppppp']] \ ] for depth in range(5) for u in range(3) unlet! l let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] exe "lockvar " . depth . " l" if u == 1 exe "unlockvar l" elseif u == 2 exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try unlet l[2]['6'][7] let ps .= 'p' catch let ps .= 'F' endtry try unlet l[2][6] let ps .= 'p' catch let ps .= 'F' endtry try unlet l[2] let ps .= 'p' catch let ps .= 'F' endtry try unlet l[1][1][0] let ps .= 'p' catch let ps .= 'F' endtry try unlet l[1][1] let ps .= 'p' catch let ps .= 'F' endtry try unlet l[1] let ps .= 'p' catch let ps .= 'F' endtry try unlet l let ps .= 'p' catch let ps .= 'F' endtry call assert_equal(expected[depth][u][1], ps) endfor endfor " Deleting a list range with locked items works, but changing the items " fails. let l = [1, 2, 3, 4] lockvar l[1:2] call assert_fails('let l[1:2] = [8, 9]', 'E741:') unlet l[1:2] call assert_equal([1, 4], l) unlet l endfunc " Locked variables and :unlet or list / dict functions " No :unlet after lock on dict: func Test_dict_lock_unlet() let d = {'a': 99, 'b': 100} lockvar 1 d call assert_fails('unlet d.a', 'E741:') endfunc " unlet after lock on dict item func Test_dict_item_lock_unlet() let lines =<< trim END VAR d = {'a': 99, 'b': 100} lockvar d.a unlet d.a call assert_equal({'b': 100}, d) END call v9.CheckLegacyAndVim9Success(lines) endfunc " filter() after lock on dict item func Test_dict_lock_filter() let lines =<< trim END VAR d = {'a': 99, 'b': 100} lockvar d.a call filter(d, 'v:key != "a"') call assert_equal({'b': 100}, d) END call v9.CheckLegacyAndVim9Success(lines) endfunc " map() after lock on dict func Test_dict_lock_map() let lines =<< trim END VAR d = {'a': 99, 'b': 100} lockvar 1 d call map(d, 'v:val + 200') call assert_equal({'a': 299, 'b': 300}, d) END " This won't work in a :def function call v9.CheckTransLegacySuccess(lines) call v9.CheckTransVim9Success(lines) " For a :def function use a global dict. let lines =<< trim END let g:thedict = {'a': 77, 'b': 88} lockvar 1 g:thedict def Delkey() unlet g:thedict.a enddef call Delkey() END call v9.CheckScriptFailure(lines, 'E741:') endfunc " Lock one item in a list func Test_list_item_lock_map() let lines =<< trim END VAR l = [99, 100, 101] lockvar l[1] call assert_fails("echo map(l, 'v:val + 200')", 'E741:') call assert_equal([299, 100, 101], l) END " This won't work in a :def function call v9.CheckTransLegacySuccess(lines) call v9.CheckTransVim9Success(lines) endfunc " Lock one item in a dict func Test_dict_item_lock_map() let lines =<< trim END VAR d = {'a': 99, 'b': 100, 'c': 101} lockvar d.b call assert_fails("echo map(d, 'v:val + 200')", 'E741:') call assert_equal({'a': 299, 'b': 100, 'c': 101}, d) END " This won't work in a :def function call v9.CheckTransLegacySuccess(lines) call v9.CheckTransVim9Success(lines) endfunc " No extend() after lock on dict item func Test_dict_lock_extend() let d = {'a': 99, 'b': 100} lockvar d.a call assert_fails("call extend(d, {'a' : 123})", 'E741:') call assert_equal({'a': 99, 'b': 100}, d) endfunc " Cannot use += with a locked dict func Test_dict_lock_operator() let d = {} lockvar d call assert_fails("let d += {'k' : 10}", 'E741:') unlockvar d endfunc " No remove() of write-protected scope-level variable func Tfunc1(this_is_a_long_parameter_name) call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742:') endfunc func Test_dict_scope_var_remove() call Tfunc1('testval') endfunc " No extend() of write-protected scope-level variable func Test_dict_scope_var_extend() call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742:') endfunc func Tfunc2(this_is_a_long_parameter_name) call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742:') endfunc func Test_dict_scope_var_extend_overwrite() call Tfunc2('testval') endfunc " No :unlet of variable in locked scope func Test_lock_var_unlet() let b:testvar = 123 lockvar 1 b: call assert_fails('unlet b:testvar', 'E741:') unlockvar 1 b: unlet! b:testvar endfunc " No :let += of locked list variable func Test_let_lock_list() let l = ['a', 'b', 3] lockvar 1 l call assert_fails("let l += ['x']", 'E741:') call assert_equal(['a', 'b', 3], l) unlet l let l = [1, 2, 3, 4] lockvar! l call assert_equal([1, 2, 3, 4], l) unlockvar l[1] call assert_fails('unlet l[0:1]', 'E741:') call assert_equal([1, 2, 3, 4], l) call assert_fails('unlet l[1:2]', 'E741:') call assert_equal([1, 2, 3, 4], l) unlockvar l[1] call assert_fails('let l[0:1] = [0, 1]', 'E741:') call assert_equal([1, 2, 3, 4], l) call assert_fails('let l[1:2] = [0, 1]', 'E741:') call assert_equal([1, 2, 3, 4], l) unlet l let lines =<< trim END def TryUnletListItem(l: list<any>) unlet l[0] enddef let l = [1, 2, 3, 4] lockvar! l call TryUnletListItem(l) END call v9.CheckScriptFailure(lines, 'E741:') unlet g:l endfunc " Locking part of the list func Test_let_lock_list_items() let l = [1, 2, 3, 4] lockvar l[2:] call assert_equal(0, islocked('l[0]')) call assert_equal(1, islocked('l[2]')) call assert_equal(1, islocked('l[3]')) call assert_fails('let l[2] = 10', 'E741:') call assert_fails('let l[3] = 20', 'E741:') unlet l endfunc " lockvar/islocked() triggering script autoloading func Test_lockvar_script_autoload() let old_rtp = &rtp set rtp+=./sautest lockvar g:footest#x unlockvar g:footest#x call assert_equal(-1, 'g:footest#x'->islocked()) call assert_equal(0, exists('g:footest#x')) call assert_equal(1, g:footest#x) let &rtp = old_rtp endfunc " a:000 function argument test func s:arg_list_test(...) call assert_fails('let a:000 = [1, 2]', 'E46:') call assert_fails('let a:000[0] = 9', 'E742:') call assert_fails('let a:000[2] = [9, 10]', 'E742:') call assert_fails('let a:000[3] = {9 : 10}', 'E742:') " now the tests that should pass let a:000[2][1] = 9 call extend(a:000[2], [5, 6]) let a:000[3][5] = 8 let a:000[3]['a'] = 12 call assert_equal([1, 2, [3, 9, 5, 6], {'a': 12, '5': 8}], a:000) endfunc func Test_func_arg_list() call s:arg_list_test(1, 2, [3, 4], {5: 6}) endfunc " Tests for reverse(), sort(), uniq() func Test_reverse_sort_uniq() let lines =<< trim END VAR l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l))) call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l)) call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l))) call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) LET l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) LET l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) END call v9.CheckLegacyAndVim9Success(lines) call assert_fails('call reverse({})', 'E1252:') call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:') call assert_fails("call sort([1, 2], function('min'), 1)", "E1206:") call assert_fails("call sort([1, 2], function('invalid_func'))", "E700:") call assert_fails("call sort([1, 2], function('min'))", "E118:") let lines =<< trim END call sort(['a', 'b'], 0) END call v9.CheckDefAndScriptFailure(lines, 'E1256: String or function required for argument 2') let lines =<< trim END call sort(['a', 'b'], 1) END call v9.CheckDefAndScriptFailure(lines, 'E1256: String or function required for argument 2') endfunc " reduce a list, blob or string func Test_reduce() let lines =<< trim END call assert_equal(1, reduce([], LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(10, reduce([1, 3, 5], LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], LSTART acc, val LMIDDLE 2 * acc + val LEND, 1)) call assert_equal('a x y z', ['x', 'y', 'z']->reduce(LSTART acc, val LMIDDLE acc .. ' ' .. val LEND, 'a')) call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0])) VAR l = ['x', 'y', 'z'] call assert_equal(42, reduce(l, function('get'), {'x': {'y': {'z': 42 } } })) call assert_equal(['x', 'y', 'z'], l) call assert_equal(1, reduce([1], LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal('x y z', reduce(['x', 'y', 'z'], LSTART acc, val LMIDDLE acc .. ' ' .. val LEND)) call assert_equal(120, range(1, 5)->reduce(LSTART acc, val LMIDDLE acc * val LEND)) call assert_equal(0, range(1)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(1, range(2)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(3, range(3)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(6, range(4)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(10, range(5)->reduce(LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(1, reduce(0z, LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE acc + val LEND, 1)) call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce(LSTART acc, val LMIDDLE 2 * acc + val LEND, 1)) call assert_equal(0xff, reduce(0zff, LSTART acc, val LMIDDLE acc + val LEND)) call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE 2 * acc + val LEND)) call assert_equal('x,y,z', 'xyz'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal('', ''->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND, '')) call assert_equal('あ,い,う,え,お,😊,💕', 'あいうえお😊💕'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal('😊,あ,い,う,え,お,💕', 'あいうえお💕'->reduce(LSTART acc, val LMIDDLE acc .. ',' .. val LEND, '😊')) call assert_equal('ऊ,ॠ,ॡ', reduce('ऊॠॡ', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal('c,à,t', reduce('càt', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal('Å,s,t,r,ö,m', reduce('Åström', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal('Å,s,t,r,ö,m', reduce('Åström', LSTART acc, val LMIDDLE acc .. ',' .. val LEND)) call assert_equal(',a,b,c', reduce('abc', LSTART acc, val LMIDDLE acc .. ',' .. val LEND, test_null_string())) call assert_equal(0x7d, reduce([0x30, 0x25, 0x08, 0x61], 'or')) call assert_equal(0x7d, reduce(0z30250861, 'or')) call assert_equal('β', reduce('ββββ', 'matchstr')) END call v9.CheckLegacyAndVim9Success(lines) call assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {})) vim9 assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce((acc, val) => extend(acc, {[val]: 1 }), {})) call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') call assert_fails("call reduce(range(0), { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') call assert_fails("call reduce(test_null_blob(), { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') call assert_fails("call reduce('', { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value') call assert_fails("call reduce(test_null_string(), { acc, val -> acc + val })", 'E998: Reduce of an empty String with no initial value') call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E1098:') call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E1098:') call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:') call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E1210:') call assert_fails("vim9 reduce(0, (acc, val) => (acc .. val), '')", 'E1252:') call assert_fails("vim9 reduce({}, (acc, val) => (acc .. val), '')", 'E1252:') call assert_fails("vim9 reduce(0.1, (acc, val) => (acc .. val), '')", 'E1252:') call assert_fails("vim9 reduce(function('tr'), (acc, val) => (acc .. val), '')", 'E1252:') call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E1174:') call assert_fails("call reduce('', { acc, val -> acc + val }, {})", 'E1174:') call assert_fails("call reduce('', { acc, val -> acc + val }, 0.1)", 'E1174:') call assert_fails("call reduce('', { acc, val -> acc + val }, function('tr'))", 'E1174:') call assert_fails("call reduce('abc', { a, v -> a10}, '')", 'E121:') call assert_fails("call reduce(0z0102, { a, v -> a10}, 1)", 'E121:') call assert_fails("call reduce([1, 2], { a, v -> a10}, '')", 'E121:') let g:lut = [1, 2, 3, 4] func EvilRemove() call remove(g:lut, 1) return 1 endfunc call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:') unlet g:lut delfunc EvilRemove call assert_equal(42, reduce(test_null_list(), function('add'), 42)) call assert_equal(42, reduce(test_null_blob(), function('add'), 42)) " should not crash call assert_fails('echo reduce([1], test_null_function())', 'E1132:') call assert_fails('echo reduce([1], test_null_partial())', 'E1132:') endfunc " splitting a string to a List using split() func Test_str_split() let lines =<< trim END call assert_equal(['aa', 'bb'], split(' aa bb ')) call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) call assert_equal(['', 'aa', 'bb', ''], split(' aa bb ', '\W\+', 1)) call assert_equal(['', '', 'aa', '', 'bb', '', ''], split(' aa bb ', '\W', 1)) call assert_equal(['aa', '', 'bb'], split(':aa::bb:', ':', 0)) call assert_equal(['', 'aa', '', 'bb', ''], split(':aa::bb:', ':', 1)) call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1)) call assert_equal(['a', 'b', 'c'], split('abc', '\zs')) call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1)) call assert_equal(['abc'], split('abc', '\\%(')) END call v9.CheckLegacyAndVim9Success(lines) call assert_fails("call split('abc', [])", 'E730:') call assert_fails("call split('abc', 'b', [])", 'E745:') endfunc " compare recursively linked list and dict func Test_listdict_compare() let lines =<< trim END VAR l = [1, 2, 3, '4'] VAR d = {'1': 1, '2': l, '3': 3} LET l[1] = d call assert_true(l == l) call assert_true(d == d) call assert_false(l != deepcopy(l)) call assert_false(d != deepcopy(d)) END call v9.CheckLegacyAndVim9Success(lines) " comparison errors call assert_fails('echo [1, 2] =~ {}', 'E691:') call assert_fails('echo [1, 2] =~ [1, 2]', 'E692:') call assert_fails('echo {} =~ 5', 'E735:') call assert_fails('echo {} =~ {}', 'E736:') endfunc " compare complex recursively linked list and dict func Test_listdict_compare_complex() let lines =<< trim END VAR l = [] call add(l, l) VAR dict4 = {"l": l} call add(dict4.l, dict4) VAR lcopy = deepcopy(l) VAR dict4copy = deepcopy(dict4) call assert_true(l == lcopy) call assert_true(dict4 == dict4copy) END call v9.CheckLegacyAndVim9Success(lines) endfunc " Test for extending lists and dictionaries func Test_listdict_extend() " Test extend() with lists " Pass the same List to extend() let lines =<< trim END VAR l = [1, 2, 3] call assert_equal([1, 2, 3, 1, 2, 3], extend(l, l)) call assert_equal([1, 2, 3, 1, 2, 3], l) LET l = [1, 2, 3] call assert_equal([1, 2, 3, 4, 5, 6], extend(l, [4, 5, 6])) call assert_equal([1, 2, 3, 4, 5, 6], l) LET l = [1, 2, 3] call extend(l, [4, 5, 6], 0) call assert_equal([4, 5, 6, 1, 2, 3], l) LET l = [1, 2, 3] call extend(l, [4, 5, 6], 1) call assert_equal([1, 4, 5, 6, 2, 3], l) LET l = [1, 2, 3] call extend(l, [4, 5, 6], 3) call assert_equal([1, 2, 3, 4, 5, 6], l) LET l = [1, 2, 3] call extend(l, [4, 5, 6], -1) call assert_equal([1, 2, 4, 5, 6, 3], l) LET l = [1, 2, 3] call extend(l, [4, 5, 6], -3) call assert_equal([4, 5, 6, 1, 2, 3], l) END call v9.CheckLegacyAndVim9Success(lines) let l = [1, 2, 3] call assert_fails("call extend(l, [4, 5, 6], 4)", 'E684:') call assert_fails("call extend(l, [4, 5, 6], -4)", 'E684:') call assert_fails("call extend(l, [4, 5, 6], 1.2)", 'E805:') " Test extend() with dictionaries. " Pass the same Dict to extend() let lines =<< trim END VAR d = {'a': {'b': 'B'}, 'x': 9} call extend(d, d) call assert_equal({'a': {'b': 'B'}, 'x': 9}, d) LET d = {'a': 'A', 'b': 9} call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, extend(d, {'b': 0, 'c': 'C'})) call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) LET d = {'a': 'A', 'b': 9} call extend(d, {'a': 'A', 'b': 0, 'c': 'C'}, "force") call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) LET d = {'a': 'A', 'b': 9} call extend(d, {'b': 0, 'c': 'C'}, "keep") call assert_equal({'a': 'A', 'b': 9, 'c': 'C'}, d) END call v9.CheckLegacyAndVim9Success(lines) let d = {'a': 'A', 'b': 'B'} call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'error')", 'E737:') call assert_fails("call extend(d, {'b': 0}, [])", 'E730:') call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'xxx')", 'E475:') call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 1.2)", 'E475:') call assert_equal({'a': 'A', 'b': 'B'}, d) call assert_fails("call extend([1, 2], 1)", 'E712:') call assert_fails("call extend([1, 2], {})", 'E712:') " Extend g: dictionary with an invalid variable name call assert_fails("call extend(g:, {'-!' : 10})", 'E461:') " Extend a list with itself. let lines =<< trim END VAR l = [1, 5, 7] call extend(l, l, 0) call assert_equal([1, 5, 7, 1, 5, 7], l) LET l = [1, 5, 7] call extend(l, l, 1) call assert_equal([1, 1, 5, 7, 5, 7], l) LET l = [1, 5, 7] call extend(l, l, 2) call assert_equal([1, 5, 1, 5, 7, 7], l) LET l = [1, 5, 7] call extend(l, l, 3) call assert_equal([1, 5, 7, 1, 5, 7], l) END call v9.CheckLegacyAndVim9Success(lines) endfunc func Test_listdict_extendnew() " Test extendnew() with lists let l = [1, 2, 3] call assert_equal([1, 2, 3, 4, 5], extendnew(l, [4, 5])) call assert_equal([1, 2, 3], l) lockvar l call assert_equal([1, 2, 3, 4, 5], extendnew(l, [4, 5])) " Test extendnew() with dictionaries. let d = {'a': {'b': 'B'}} call assert_equal({'a': {'b': 'B'}, 'c': 'cc'}, extendnew(d, {'c': 'cc'})) call assert_equal({'a': {'b': 'B'}}, d) lockvar d call assert_equal({'a': {'b': 'B'}, 'c': 'cc'}, extendnew(d, {'c': 'cc'})) endfunc func s:check_scope_dict(x, fixed) func s:gen_cmd(cmd, x) return substitute(a:cmd, '\<x\ze:', a:x, 'g') endfunc let cmd = s:gen_cmd('let x:foo = 1', a:x) if a:fixed call assert_fails(cmd, 'E461:') else exe cmd exe s:gen_cmd('call assert_equal(1, x:foo)', a:x) endif let cmd = s:gen_cmd('let x:["bar"] = 2', a:x) if a:fixed call assert_fails(cmd, 'E461:') else exe cmd exe s:gen_cmd('call assert_equal(2, x:bar)', a:x) endif let cmd = s:gen_cmd('call extend(x:, {"baz": 3})', a:x) if a:fixed call assert_fails(cmd, 'E742:') else exe cmd exe s:gen_cmd('call assert_equal(3, x:baz)', a:x) endif if a:fixed if a:x ==# 'a' call assert_fails('unlet a:x', 'E795:') call assert_fails('call remove(a:, "x")', 'E742:') elseif a:x ==# 'v' call assert_fails('unlet v:count', 'E795:') call assert_fails('call remove(v:, "count")', 'E742:') endif else exe s:gen_cmd('unlet x:foo', a:x) exe s:gen_cmd('unlet x:bar', a:x) exe s:gen_cmd('call remove(x:, "baz")', a:x) endif delfunc s:gen_cmd endfunc func Test_scope_dict() " Test for g: call s:check_scope_dict('g', v:false) " Test for s: call s:check_scope_dict('s', v:false) " Test for l: call s:check_scope_dict('l', v:false) " Test for a: call s:check_scope_dict('a', v:true) " Test for b: call s:check_scope_dict('b', v:false) " Test for w: call s:check_scope_dict('w', v:false) " Test for t: call s:check_scope_dict('t', v:false) " Test for v: call s:check_scope_dict('v', v:true) endfunc " Test for deep nesting of lists (> 100) func Test_deep_nested_list() let deep_list = [] let l = deep_list for i in range(102) let newlist = [] call add(l, newlist) let l = newlist endfor call add(l, 102) call assert_fails('let m = deepcopy(deep_list)', 'E698:') call assert_fails('lockvar 110 deep_list', 'E743:') call assert_fails('unlockvar 110 deep_list', 'E743:') call assert_fails('let x = execute("echo deep_list")', 'E724:') call test_garbagecollect_now() unlet deep_list endfunc " Test for deep nesting of dicts (> 100) func Test_deep_nested_dict() let deep_dict = {} let d = deep_dict for i in range(102) let newdict = {} let d.k = newdict let d = newdict endfor let d.k = 'v' call assert_fails('let m = deepcopy(deep_dict)', 'E698:') call assert_fails('lockvar 110 deep_dict', 'E743:') call assert_fails('unlockvar 110 deep_dict', 'E743:') call assert_fails('let x = execute("echo deep_dict")', 'E724:') call test_garbagecollect_now() unlet deep_dict endfunc " List and dict indexing tests func Test_listdict_index() call v9.CheckLegacyAndVim9Failure(['echo function("min")[0]'], 'E695:') call v9.CheckLegacyAndVim9Failure(['echo v:true[0]'], 'E909:') call v9.CheckLegacyAndVim9Failure(['echo v:null[0]'], 'E909:') call v9.CheckLegacyAndVim9Failure(['VAR d = {"k": 10}', 'echo d.'], ['E15:', 'E1127:', 'E15:']) call v9.CheckLegacyAndVim9Failure(['VAR d = {"k": 10}', 'echo d[1 : 2]'], 'E719:') call assert_fails("let v = [4, 6][{-> 1}]", 'E729:') call v9.CheckDefAndScriptFailure(['var v = [4, 6][() => 1]'], ['E1012', 'E703:']) call v9.CheckLegacyAndVim9Failure(['VAR v = range(5)[2 : []]'], ['E730:', 'E1012:', 'E730:']) call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:']) call v9.CheckDefAndScriptFailure(['var v = range(5)[2 : () => 2(]'], 'E15:') call v9.CheckLegacyAndVim9Failure(['VAR v = range(5)[2 : 3'], ['E111:', 'E1097:', 'E111:']) call v9.CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, 10)'], 'E684:') call v9.CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, -10)'], 'E684:') call v9.CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, [])'], ['E745:', 'E1013:', 'E1210:']) call v9.CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[i] = 3'], ['E121:', 'E1001:', 'E121:']) call v9.CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[1.1] = 4'], ['E805:', 'E1012:', 'E805:']) call v9.CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[: i] = [4, 5]'], ['E121:', 'E1001:', 'E121:']) call v9.CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[: 3.2] = [4, 5]'], ['E805:', 'E1012:', 'E805:']) call v9.CheckLegacyAndVim9Failure(['VAR t = test_unknown()', 'echo t[0]'], ['E340:', 'E909:', 'E340:', 'E685:']) endfunc " Test for a null list func Test_null_list() let lines =<< trim END VAR l = test_null_list() call assert_equal('', join(test_null_list())) call assert_equal('', join(l)) call assert_equal(0, len(l)) call assert_equal(1, empty(l)) call assert_equal([], split(test_null_string())) call assert_equal([], l[ : 2]) call assert_true([] == l) call assert_equal('[]', string(l)) call assert_equal([], sort(test_null_list())) call assert_equal([], sort(l)) call assert_equal([], uniq(test_null_list())) call assert_equal([], uniq(l)) VAR k = [] + l call assert_equal([], k) LET k = l + [] call assert_equal([], k) call assert_equal(0, len(copy(l))) call assert_equal(0, count(l, 5)) call assert_equal([], deepcopy(l)) call assert_equal(5, get(l, 2, 5)) call assert_equal(-1, index(l, 2, 5)) call assert_equal(0, min(l)) call assert_equal(0, max(l)) call assert_equal(0, remove(test_null_list(), 0, 2)) call assert_equal([], repeat(l, 2)) call assert_equal([], reverse(test_null_list())) call assert_equal([], reverse(l)) call assert_equal([], sort(test_null_list())) call assert_equal([], sort(l)) call assert_equal('[]', string(l)) END call v9.CheckLegacyAndVim9Success(lines) let l = test_null_list() call assert_equal([], extend(l, l, 0)) call assert_equal(0, insert(test_null_list(), 2, -1)) call assert_fails('let s = join([1, 2], [])', 'E730:') call assert_fails('call remove(l, 0, 2)', 'E684:') call assert_fails('call insert(l, 2, -1)', 'E684:') call assert_fails('call extend(test_null_list(), test_null_list())', 'E1134:') lockvar l call assert_equal(1, islocked('l')) unlockvar l endfunc " Test for a null dict func Test_null_dict() let lines =<< trim END call assert_equal(test_null_dict(), test_null_dict()) VAR d = test_null_dict() call assert_equal({}, d) call assert_equal(0, len(d)) call assert_equal(1, empty(d)) call assert_equal([], items(test_null_dict())) call assert_equal([], items(d)) call assert_equal([], keys(test_null_dict())) call assert_equal([], keys(d)) call assert_equal([], values(test_null_dict())) call assert_equal([], values(d)) call assert_false(has_key(d, 'k')) call assert_equal('{}', string(d)) call assert_equal({}, {}) call assert_equal(0, len(copy(d))) call assert_equal(0, count(d, 'k')) call assert_equal({}, deepcopy(d)) call assert_equal(20, get(d, 'k', 20)) call assert_equal(0, min(d)) call assert_equal(0, max(d)) call assert_equal(0, remove(test_null_dict(), 'k')) call assert_equal('{}', string(d)) END call v9.CheckLegacyAndVim9Success(lines) let d = test_null_dict() call assert_equal({}, extend(d, d, 'keep')) call assert_fails("call remove(d, 'k')", 'E716:') call assert_fails('let x = d[10]', 'E716:') call assert_fails('call extend(test_null_dict(), test_null_dict())', 'E1133:') lockvar d call assert_equal(1, islocked('d')) unlockvar d endfunc " Test for the indexof() function func Test_indexof() let l = [#{color: 'red'}, #{color: 'blue'}, #{color: 'green'}] call assert_equal(0, indexof(l, {i, v -> v.color == 'red'})) call assert_equal(2, indexof(l, {i, v -> v.color == 'green'})) call assert_equal(-1, indexof(l, {i, v -> v.color == 'grey'})) call assert_equal(1, indexof(l, "v:val.color == 'blue'")) call assert_equal(-1, indexof(l, "v:val.color == 'cyan'")) let l = [#{n: 10}, #{n: 10}, #{n: 20}] call assert_equal(0, indexof(l, "v:val.n == 10", #{startidx: 0})) call assert_equal(1, indexof(l, "v:val.n == 10", #{startidx: -2})) call assert_equal(-1, indexof(l, "v:val.n == 10", #{startidx: 4})) call assert_equal(-1, indexof(l, "v:val.n == 10", #{startidx: -4})) call assert_equal(0, indexof(l, "v:val.n == 10", test_null_dict())) let s = ["a", "b", "c"] call assert_equal(2, indexof(s, {_, v -> v == 'c'})) call assert_equal(-1, indexof(s, {_, v -> v == 'd'})) call assert_equal(-1, indexof(s, {_, v -> "v == 'd'"})) call assert_equal(-1, indexof([], {i, v -> v == 'a'})) call assert_equal(-1, indexof([1, 2, 3], {_, v -> "v == 2"})) call assert_equal(-1, indexof(test_null_list(), {i, v -> v == 'a'})) call assert_equal(-1, indexof(l, test_null_string())) call assert_equal(-1, indexof(l, test_null_function())) " failure cases call assert_fails('let i = indexof(l, "v:val == ''cyan''")', 'E735:') call assert_fails('let i = indexof(l, "color == ''cyan''")', 'E121:') call assert_fails('let i = indexof(l, {})', 'E1256:') call assert_fails('let i = indexof({}, "v:val == 2")', 'E1226:') call assert_fails('let i = indexof([], "v:val == 2", [])', 'E1206:') func TestIdx(k, v) return a:v.n == 20 endfunc call assert_equal(2, indexof(l, function("TestIdx"))) delfunc TestIdx func TestIdx(k, v) return {} endfunc call assert_fails('let i = indexof(l, function("TestIdx"))', 'E728:') delfunc TestIdx func TestIdx(k, v) throw "IdxError" endfunc call assert_fails('let i = indexof(l, function("TestIdx"))', 'E605:') delfunc TestIdx endfunc " vim: shiftwidth=2 sts=2 expandtab