# HG changeset patch # User Bram Moolenaar # Date 1430730627 -7200 # Node ID 1805e45ce4d67e0d326fbe952b87bd4086123844 # Parent 2af61e5ad85a4ebca12e2b0117486024f1862f6d patch 7.4.717 Problem: ":let list += list" can change a locked list. Solution: Check for the lock earlier. (Olaf Dabrunz) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -783,7 +783,7 @@ static char_u *find_name_end __ARGS((cha static char_u * make_expanded_name __ARGS((char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end)); static int eval_isnamec __ARGS((int c)); static int eval_isnamec1 __ARGS((int c)); -static int get_var_tv __ARGS((char_u *name, int len, typval_T *rettv, int verbose, int no_autoload)); +static int get_var_tv __ARGS((char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload)); static int handle_subscript __ARGS((char_u **arg, typval_T *rettv, int evaluate, int verbose)); static typval_T *alloc_tv __ARGS((void)); static typval_T *alloc_string_tv __ARGS((char_u *string)); @@ -2257,7 +2257,7 @@ list_arg_vars(eap, arg, first) { if (tofree != NULL) name = tofree; - if (get_var_tv(name, len, &tv, TRUE, FALSE) == FAIL) + if (get_var_tv(name, len, &tv, NULL, TRUE, FALSE) == FAIL) error = TRUE; else { @@ -2926,10 +2926,15 @@ set_var_lval(lp, endp, rettv, copy, op) typval_T tv; /* handle +=, -= and .= */ + di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, TRUE, FALSE) == OK) - { - if (tv_op(&tv, rettv, op) == OK) + &tv, &di, TRUE, FALSE) == OK) + { + if ((di == NULL + || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) + && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, + FALSE))) + && tv_op(&tv, rettv, op) == OK) set_var(lp->ll_name, &tv, FALSE); clear_tv(&tv); } @@ -5246,7 +5251,7 @@ eval7(arg, rettv, evaluate, want_string) } } else if (evaluate) - ret = get_var_tv(s, len, rettv, TRUE, FALSE); + ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else ret = OK; } @@ -10375,7 +10380,7 @@ f_exists(argvars, rettv) { if (tofree != NULL) name = tofree; - n = (get_var_tv(name, len, &tv, FALSE, TRUE) == OK); + n = (get_var_tv(name, len, &tv, NULL, FALSE, TRUE) == OK); if (n) { /* handle d.key, l[idx], f(expr) */ @@ -20646,10 +20651,11 @@ set_cmdarg(eap, oldarg) * Return OK or FAIL. */ static int -get_var_tv(name, len, rettv, verbose, no_autoload) +get_var_tv(name, len, rettv, dip, verbose, no_autoload) char_u *name; int len; /* length of "name" */ typval_T *rettv; /* NULL when only checking existence */ + dictitem_T **dip; /* non-NULL when typval's dict item is needed */ int verbose; /* may give error message */ int no_autoload; /* do not use script autoloading */ { @@ -20680,7 +20686,11 @@ get_var_tv(name, len, rettv, verbose, no { v = find_var(name, NULL, no_autoload); if (v != NULL) + { tv = &v->di_tv; + if (dip != NULL) + *dip = v; + } } if (tv == NULL) @@ -21474,8 +21484,8 @@ set_var(name, tv, copy) } /* - * Handle setting internal v: variables separately: we don't change - * the type. + * Handle setting internal v: variables separately where needed to + * prevent changing the type. */ if (ht == &vimvarht) { @@ -21490,10 +21500,9 @@ set_var(name, tv, copy) v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; } - } - else if (v->di_tv.v_type != VAR_NUMBER) - EMSG2(_(e_intern2), "set_var()"); - else + return; + } + else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); if (STRCMP(varname, "searchforward") == 0) @@ -21505,8 +21514,10 @@ set_var(name, tv, copy) redraw_all_later(SOME_VALID); } #endif - } - return; + return; + } + else if (v->di_tv.v_type != tv->v_type) + EMSG2(_(e_intern2), "set_var()"); } clear_tv(&v->di_tv); diff --git a/src/testdir/test55.in b/src/testdir/test55.in --- a/src/testdir/test55.in +++ b/src/testdir/test55.in @@ -442,6 +442,17 @@ let l = [0, 1, 2, 3] :unlockvar 1 b: :unlet! b:testvar :" +:$put ='No :let += of locked list variable:' +:let l = ['a', 'b', 3] +:lockvar 1 l +:try +: let l += ['x'] +: $put ='did :let +=' +:catch +: $put =v:exception[:14] +:endtry +:$put =string(l) +:" :unlet l :let l = [1, 2, 3, 4] :lockvar! l diff --git a/src/testdir/test55.ok b/src/testdir/test55.ok --- a/src/testdir/test55.ok +++ b/src/testdir/test55.ok @@ -144,6 +144,9 @@ No extend() of write-protected scope-lev Vim(put):E742: No :unlet of variable in locked scope: Vim(unlet):E741: +No :let += of locked list variable: +Vim(let):E741: +['a', 'b', 3] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 717, +/**/ 716, /**/ 715,