Skip to content

Commit 31b8160

Browse files
committed
patch 8.1.0888: the a: dict is not immutable as documented
Problem: The a: dict is not immutable as documented. Solution: Make the a:dict immutable, add a test. (Ozaki Kiichi, Yasuhiro Matsumoto, closes #3929)
1 parent 9474716 commit 31b8160

File tree

5 files changed

+210
-9
lines changed

5 files changed

+210
-9
lines changed

src/eval.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,14 +2092,15 @@ get_lval(
20922092

20932093
if (lp->ll_di == NULL)
20942094
{
2095-
/* Can't add "v:" variable. */
2096-
if (lp->ll_dict == &vimvardict)
2095+
// Can't add "v:" or "a:" variable.
2096+
if (lp->ll_dict == &vimvardict
2097+
|| &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
20972098
{
20982099
semsg(_(e_illvar), name);
20992100
return NULL;
21002101
}
21012102

2102-
/* Key does not exist in dict: may need to add it. */
2103+
// Key does not exist in dict: may need to add it.
21032104
if (*p == '[' || *p == '.' || unlet)
21042105
{
21052106
if (!quiet)
@@ -7919,14 +7920,14 @@ set_var(
79197920
}
79207921
else /* add a new variable */
79217922
{
7922-
/* Can't add "v:" variable. */
7923-
if (ht == &vimvarht)
7923+
// Can't add "v:" or "a:" variable.
7924+
if (ht == &vimvarht || ht == get_funccal_args_ht())
79247925
{
79257926
semsg(_(e_illvar), name);
79267927
return;
79277928
}
79287929

7929-
/* Make sure the variable name is valid. */
7930+
// Make sure the variable name is valid.
79307931
if (!valid_varname(varname))
79317932
return;
79327933

src/testdir/test_let.vim

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,124 @@ func Test_let()
2525
let s = "\na #1\nb #2"
2626
call assert_equal(s, out)
2727
endfunc
28+
29+
func s:set_arg1(a) abort
30+
let a:a = 1
31+
endfunction
32+
33+
func s:set_arg2(a) abort
34+
let a:b = 1
35+
endfunction
36+
37+
func s:set_arg3(a) abort
38+
let b = a:
39+
let b['a'] = 1
40+
endfunction
41+
42+
func s:set_arg4(a) abort
43+
let b = a:
44+
let b['a'] = 1
45+
endfunction
46+
47+
func s:set_arg5(a) abort
48+
let b = a:
49+
let b['a'][0] = 1
50+
endfunction
51+
52+
func s:set_arg6(a) abort
53+
let a:a[0] = 1
54+
endfunction
55+
56+
func s:set_arg7(a) abort
57+
call extend(a:, {'a': 1})
58+
endfunction
59+
60+
func s:set_arg8(a) abort
61+
call extend(a:, {'b': 1})
62+
endfunction
63+
64+
func s:set_arg9(a) abort
65+
let a:['b'] = 1
66+
endfunction
67+
68+
func s:set_arg10(a) abort
69+
let b = a:
70+
call extend(b, {'a': 1})
71+
endfunction
72+
73+
func s:set_arg11(a) abort
74+
let b = a:
75+
call extend(b, {'b': 1})
76+
endfunction
77+
78+
func s:set_arg12(a) abort
79+
let b = a:
80+
let b['b'] = 1
81+
endfunction
82+
83+
func Test_let_arg_fail()
84+
call assert_fails('call s:set_arg1(1)', 'E46:')
85+
call assert_fails('call s:set_arg2(1)', 'E461:')
86+
call assert_fails('call s:set_arg3(1)', 'E46:')
87+
call assert_fails('call s:set_arg4(1)', 'E46:')
88+
call assert_fails('call s:set_arg5(1)', 'E46:')
89+
call s:set_arg6([0])
90+
call assert_fails('call s:set_arg7(1)', 'E742:')
91+
call assert_fails('call s:set_arg8(1)', 'E742:')
92+
call assert_fails('call s:set_arg9(1)', 'E461:')
93+
call assert_fails('call s:set_arg10(1)', 'E742:')
94+
call assert_fails('call s:set_arg11(1)', 'E742:')
95+
call assert_fails('call s:set_arg12(1)', 'E461:')
96+
endfunction
97+
98+
func s:set_varg1(...) abort
99+
let a:000 = []
100+
endfunction
101+
102+
func s:set_varg2(...) abort
103+
let a:000[0] = 1
104+
endfunction
105+
106+
func s:set_varg3(...) abort
107+
let a:000 += [1]
108+
endfunction
109+
110+
func s:set_varg4(...) abort
111+
call add(a:000, 1)
112+
endfunction
113+
114+
func s:set_varg5(...) abort
115+
let a:000[0][0] = 1
116+
endfunction
117+
118+
func s:set_varg6(...) abort
119+
let b = a:000
120+
let b[0] = 1
121+
endfunction
122+
123+
func s:set_varg7(...) abort
124+
let b = a:000
125+
let b += [1]
126+
endfunction
127+
128+
func s:set_varg8(...) abort
129+
let b = a:000
130+
call add(b, 1)
131+
endfunction
132+
133+
func s:set_varg9(...) abort
134+
let b = a:000
135+
let b[0][0] = 1
136+
endfunction
137+
138+
func Test_let_varg_fail()
139+
call assert_fails('call s:set_varg1(1)', 'E46:')
140+
call assert_fails('call s:set_varg2(1)', 'E742:')
141+
call assert_fails('call s:set_varg3(1)', 'E46:')
142+
call assert_fails('call s:set_varg4(1)', 'E742:')
143+
call s:set_varg5([0])
144+
call assert_fails('call s:set_varg6(1)', 'E742:')
145+
" call assert_fails('call s:set_varg7(1)', 'E46:')
146+
call assert_fails('call s:set_varg8(1)', 'E742:')
147+
call s:set_varg9([0])
148+
endfunction

src/testdir/test_listdict.vim

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,17 +500,21 @@ endfunc
500500

501501
" No remove() of write-protected scope-level variable
502502
func Tfunc1(this_is_a_long_parameter_name)
503-
call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E795')
503+
call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742')
504504
endfunc
505505
func Test_dict_scope_var_remove()
506506
call Tfunc1('testval')
507507
endfunc
508508

509509
" No extend() of write-protected scope-level variable
510+
func Test_dict_scope_var_extend()
511+
call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742')
512+
endfunc
513+
510514
func Tfunc2(this_is_a_long_parameter_name)
511515
call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742')
512516
endfunc
513-
func Test_dict_scope_var_extend()
517+
func Test_dict_scope_var_extend_overwrite()
514518
call Tfunc2('testval')
515519
endfunc
516520

@@ -651,3 +655,75 @@ func Test_listdict_extend()
651655
call assert_fails("call extend(d, d, 'error')", 'E737:')
652656
call assert_equal({'a': {'b': 'B'}}, d)
653657
endfunc
658+
659+
func s:check_scope_dict(x, fixed)
660+
func s:gen_cmd(cmd, x)
661+
return substitute(a:cmd, '\<x\ze:', a:x, 'g')
662+
endfunc
663+
664+
let cmd = s:gen_cmd('let x:foo = 1', a:x)
665+
if a:fixed
666+
call assert_fails(cmd, 'E461')
667+
else
668+
exe cmd
669+
exe s:gen_cmd('call assert_equal(1, x:foo)', a:x)
670+
endif
671+
672+
let cmd = s:gen_cmd('let x:["bar"] = 2', a:x)
673+
if a:fixed
674+
call assert_fails(cmd, 'E461')
675+
else
676+
exe cmd
677+
exe s:gen_cmd('call assert_equal(2, x:bar)', a:x)
678+
endif
679+
680+
let cmd = s:gen_cmd('call extend(x:, {"baz": 3})', a:x)
681+
if a:fixed
682+
call assert_fails(cmd, 'E742')
683+
else
684+
exe cmd
685+
exe s:gen_cmd('call assert_equal(3, x:baz)', a:x)
686+
endif
687+
688+
if a:fixed
689+
if a:x ==# 'a'
690+
call assert_fails('unlet a:x', 'E795')
691+
call assert_fails('call remove(a:, "x")', 'E742')
692+
elseif a:x ==# 'v'
693+
call assert_fails('unlet v:count', 'E795')
694+
call assert_fails('call remove(v:, "count")', 'E742')
695+
endif
696+
else
697+
exe s:gen_cmd('unlet x:foo', a:x)
698+
exe s:gen_cmd('unlet x:bar', a:x)
699+
exe s:gen_cmd('call remove(x:, "baz")', a:x)
700+
endif
701+
702+
delfunc s:gen_cmd
703+
endfunc
704+
705+
func Test_scope_dict()
706+
" Test for g:
707+
call s:check_scope_dict('g', v:false)
708+
709+
" Test for s:
710+
call s:check_scope_dict('s', v:false)
711+
712+
" Test for l:
713+
call s:check_scope_dict('l', v:false)
714+
715+
" Test for a:
716+
call s:check_scope_dict('a', v:true)
717+
718+
" Test for b:
719+
call s:check_scope_dict('b', v:false)
720+
721+
" Test for w:
722+
call s:check_scope_dict('w', v:false)
723+
724+
" Test for t:
725+
call s:check_scope_dict('t', v:false)
726+
727+
" Test for v:
728+
call s:check_scope_dict('v', v:true)
729+
endfunc

src/userfunc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ call_user_func(
772772
v = &fc->fixvar[fixvar_idx++].var;
773773
name = v->di_key;
774774
STRCPY(name, "self");
775-
v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX;
775+
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
776776
hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v));
777777
v->di_tv.v_type = VAR_DICT;
778778
v->di_tv.v_lock = 0;
@@ -788,6 +788,7 @@ call_user_func(
788788
init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE);
789789
add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0",
790790
(varnumber_T)(argcount - fp->uf_args.ga_len));
791+
fc->l_avars.dv_lock = VAR_FIXED;
791792
/* Use "name" to avoid a warning from some compiler that checks the
792793
* destination size. */
793794
v = &fc->fixvar[fixvar_idx++].var;

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,8 @@ static char *(features[]) =
783783

784784
static int included_patches[] =
785785
{ /* Add new patch number below this line */
786+
/**/
787+
888,
786788
/**/
787789
887,
788790
/**/

0 commit comments

Comments
 (0)