1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local describe, it, before_each = t.describe, t.it, t.before_each
local eq = t.eq
local clear = n.clear
local command = n.command
local eval = n.eval
local api = n.api
local exec = n.exec
local exec_capture = n.exec_capture
local expect_exit = n.expect_exit
local source = n.source
local testprg = n.testprg
before_each(clear)
describe(':let', function()
it('correctly lists variables with curly-braces', function()
api.nvim_set_var('v', { 0 })
eq('v [0]', exec_capture('let {"v"}'))
end)
it('correctly lists variables with subscript', function()
api.nvim_set_var('v', { 0 })
eq('v[0] #0', exec_capture('let v[0]'))
eq('g:["v"][0] #0', exec_capture('let g:["v"][0]'))
eq('{"g:"}["v"][0] #0', exec_capture('let {"g:"}["v"][0]'))
end)
it(':unlet self-referencing node in a List graph #6070', function()
-- :unlet-ing a self-referencing List must not allow GC on indirectly
-- referenced in-scope Lists. Before #6070 this caused use-after-free.
expect_exit(
1000,
source,
[=[
let [l1, l2] = [[], []]
echo 'l1:' . id(l1)
echo 'l2:' . id(l2)
echo ''
let [l3, l4] = [[], []]
call add(l4, l4)
call add(l4, l3)
call add(l3, 1)
call add(l2, l2)
call add(l2, l1)
call add(l1, 1)
unlet l2
unlet l4
call garbagecollect(1)
call feedkeys(":\e:echo l1 l3\n:echo 42\n:cq\n", "t")
]=]
)
end)
it('multibyte env var #8398 #9267', function()
command("let $NVIM_TEST_LET = 'AìaB'")
eq('AìaB', eval('$NVIM_TEST_LET'))
command("let $NVIM_TEST_LET = 'AaあB'")
eq('AaあB', eval('$NVIM_TEST_LET'))
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
command("let $NVIM_TEST_LET = '" .. mbyte .. "'")
eq(mbyte, eval('$NVIM_TEST_LET'))
end)
it('multibyte env var to child process #8398 #9267', function()
local cmd_get_child_env = ("let g:env_from_child = system(['%s', 'NVIM_TEST_LET'])"):format(
testprg('printenv-test')
)
command("let $NVIM_TEST_LET = 'AìaB'")
command(cmd_get_child_env)
eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
command("let $NVIM_TEST_LET = 'AaあB'")
command(cmd_get_child_env)
eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
local mbyte = [[\p* .ม .ม .ม .ม่ .ม่ .ม่ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ ֹֻ ֹֻ ֹֻ .ֹֻ .ֹֻ .ֹֻ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹ ֹ ֹ .ֹ .ֹ .ֹ ֹֻ ֹֻ
.ֹֻ .ֹֻ .ֹֻ a a a ca ca ca à à à]]
command("let $NVIM_TEST_LET = '" .. mbyte .. "'")
command(cmd_get_child_env)
eq(eval('$NVIM_TEST_LET'), eval('g:env_from_child'))
end)
it('release of list assigned to l: variable does not trigger assertion #12387, #12430', function()
source([[
func! s:f()
let l:x = [1]
let g:x = l:
endfunc
for _ in range(2)
call s:f()
endfor
call garbagecollect()
call feedkeys('i', 't')
]])
eq(1, eval('1'))
end)
it('can apply operator to boolean option', function()
eq(true, api.nvim_get_option_value('equalalways', {}))
command('let &equalalways -= 1')
eq(false, api.nvim_get_option_value('equalalways', {}))
command('let &equalalways += 1')
eq(true, api.nvim_get_option_value('equalalways', {}))
command('let &equalalways *= 1')
eq(true, api.nvim_get_option_value('equalalways', {}))
command('let &equalalways /= 1')
eq(true, api.nvim_get_option_value('equalalways', {}))
command('let &equalalways %= 1')
eq(false, api.nvim_get_option_value('equalalways', {}))
end)
it('ignores legacy TTY assignments after validation', function()
for _, name in ipairs({ 't_xx', 't_Co', 'term', 'ttytype' }) do
local original = eval('&' .. name)
command('let &' .. name .. ' = "ignored"')
eq(original, eval('&' .. name))
command('let &' .. name .. ' .= "ignored"')
eq(original, eval('&' .. name))
end
eq('Vim(let):E730: Using a List as a String', t.pcall_err(command, 'let &t_xx = []'))
eq('Vim(let):E734: Wrong variable type for +=', t.pcall_err(command, 'let &t_xx += 1'))
end)
it('no crash: assigning bool/special to a string option gives E928', function()
for _, v in ipairs({ 'v:true', 'v:false', 'v:null' }) do
-- Regular string option.
eq('Vim(let):E928: String required', t.pcall_err(command, 'let &makeprg = ' .. v))
-- TTY option ("t_" pseudo-option).
eq('Vim(let):E928: String required', t.pcall_err(command, 'let &t_Co = ' .. v))
end
end)
it('no crash: assigning to local value of a global-local option', function()
-- Unset local reads as Unset, and -1 unsets it again, so curval/newval types may differ.
for _, o in ipairs({ 'autoread', 'autocomplete', 'fsync' }) do
command(('setglobal %s'):format(o)) -- global = on
command(('let &l:%s = v:false'):format(o)) -- unset local <- bool: sets local off
eq(false, api.nvim_get_option_value(o, {}))
command(('let &l:%s = -1'):format(o)) -- set local <- -1: unsets local, inheriting global
eq(true, api.nvim_get_option_value(o, {}))
end
end)
end)
describe(':let and :const', function()
it('have the same output when called without arguments', function()
eq(exec_capture('let'), exec_capture('const'))
end)
it('can be used in sandbox', function()
exec([[
func Func()
let l:foo = 'foo'
const l:bar = 'bar'
endfunc
sandbox call Func()
]])
end)
end)
|