Skip to content

Commit 5b5ae29

Browse files
committed
patch 8.2.2533: Vim9: cannot use a range with :unlet
Problem: Vim9: cannot use a range with :unlet. Solution: Implement ISN_UNLETRANGE.
1 parent ada1d87 commit 5b5ae29

11 files changed

Lines changed: 231 additions & 38 deletions

File tree

src/errors.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,7 @@ EXTERN char e_variable_nr_type_mismatch_expected_str_but_got_str[]
365365
INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s"));
366366
EXTERN char e_vim9cmd_must_be_followed_by_command[]
367367
INIT(= N_("E1164: vim9cmd must be followed by a command"));
368+
EXTERN char e_cannot_use_range_with_assignment_str[]
369+
INIT(= N_("E1165: Cannot use a range with an assignment: %s"));
370+
EXTERN char e_cannot_use_range_with_dictionary[]
371+
INIT(= N_("E1166: Cannot use a range with a dictionary"));

src/eval.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,15 +1213,7 @@ get_lval(
12131213

12141214
lp->ll_dict = NULL;
12151215
lp->ll_list = lp->ll_tv->vval.v_list;
1216-
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
1217-
if (lp->ll_li == NULL)
1218-
{
1219-
if (lp->ll_n1 < 0)
1220-
{
1221-
lp->ll_n1 = 0;
1222-
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
1223-
}
1224-
}
1216+
lp->ll_li = list_find_index(lp->ll_list, &lp->ll_n1);
12251217
if (lp->ll_li == NULL)
12261218
{
12271219
clear_tv(&var2);

src/evalvars.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,27 +1656,9 @@ do_unlet_var(
16561656
return FAIL;
16571657
else if (lp->ll_range)
16581658
{
1659-
listitem_T *li;
1660-
listitem_T *ll_li = lp->ll_li;
1661-
int ll_n1 = lp->ll_n1;
1662-
1663-
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1))
1664-
{
1665-
li = ll_li->li_next;
1666-
if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE))
1667-
return FAIL;
1668-
ll_li = li;
1669-
++ll_n1;
1670-
}
1671-
1672-
// Delete a range of List items.
1673-
while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1))
1674-
{
1675-
li = lp->ll_li->li_next;
1676-
listitem_remove(lp->ll_list, lp->ll_li);
1677-
lp->ll_li = li;
1678-
++lp->ll_n1;
1679-
}
1659+
if (list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_name, lp->ll_n1,
1660+
!lp->ll_empty2, lp->ll_n2) == FAIL)
1661+
return FAIL;
16801662
}
16811663
else
16821664
{
@@ -1691,6 +1673,43 @@ do_unlet_var(
16911673
return ret;
16921674
}
16931675

1676+
/*
1677+
* Unlet one item or a range of items from a list.
1678+
* Return OK or FAIL.
1679+
*/
1680+
int
1681+
list_unlet_range(
1682+
list_T *l,
1683+
listitem_T *li_first,
1684+
char_u *name,
1685+
long n1_arg,
1686+
int has_n2,
1687+
long n2)
1688+
{
1689+
listitem_T *li = li_first;
1690+
int n1 = n1_arg;
1691+
1692+
while (li != NULL && (!has_n2 || n2 >= n1))
1693+
{
1694+
if (value_check_lock(li->li_tv.v_lock, name, FALSE))
1695+
return FAIL;
1696+
li = li->li_next;
1697+
++n1;
1698+
}
1699+
1700+
// Delete a range of List items.
1701+
li = li_first;
1702+
n1 = n1_arg;
1703+
while (li != NULL && (!has_n2 || n2 >= n1))
1704+
{
1705+
listitem_T *next = li->li_next;
1706+
1707+
listitem_remove(l, li);
1708+
li = next;
1709+
++n1;
1710+
}
1711+
return OK;
1712+
}
16941713
/*
16951714
* "unlet" a variable. Return OK if it existed, FAIL if not.
16961715
* When "forceit" is TRUE don't complain if the variable doesn't exist.

src/list.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,26 @@ list_find_str(list_T *l, long idx)
530530
return tv_get_string(&li->li_tv);
531531
}
532532

533+
/*
534+
* Like list_find() but when a negative index is used that is not found use
535+
* zero and set "idx" to zero. Used for first index of a range.
536+
*/
537+
listitem_T *
538+
list_find_index(list_T *l, long *idx)
539+
{
540+
listitem_T *li = list_find(l, *idx);
541+
542+
if (li == NULL)
543+
{
544+
if (*idx < 0)
545+
{
546+
*idx = 0;
547+
li = list_find(l, *idx);
548+
}
549+
}
550+
return li;
551+
}
552+
533553
/*
534554
* Locate "item" list "l" and return its index.
535555
* Returns -1 when "item" is not in the list.

src/proto/evalvars.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void list_hashtable_vars(hashtab_T *ht, char *prefix, int empty, int *first);
2323
void ex_unlet(exarg_T *eap);
2424
void ex_lockvar(exarg_T *eap);
2525
void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int (*callback)(lval_T *, char_u *, exarg_T *, int, void *), void *cookie);
26+
int list_unlet_range(list_T *l, listitem_T *li_first, char_u *name, long n1_arg, int has_n2, long n2);
2627
int do_unlet(char_u *name, int forceit);
2728
void item_lock(typval_T *tv, int deep, int lock, int check_refcount);
2829
void del_menutrans_vars(void);

src/proto/list.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ int list_equal(list_T *l1, list_T *l2, int ic, int recursive);
2020
listitem_T *list_find(list_T *l, long n);
2121
long list_find_nr(list_T *l, long idx, int *errorp);
2222
char_u *list_find_str(list_T *l, long idx);
23+
listitem_T *list_find_index(list_T *l, long *idx);
2324
long list_idx_of_item(list_T *l, listitem_T *item);
2425
void list_append(list_T *l, listitem_T *item);
2526
int list_append_tv(list_T *l, typval_T *tv);

src/testdir/test_vim9_assign.vim

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ let g:existing = 'yes'
99
let g:inc_counter = 1
1010
let $SOME_ENV_VAR = 'some'
1111
let g:alist = [7]
12+
let g:adict = #{a: 1}
1213
let g:astring = 'text'
1314

1415
def Test_assignment_bool()
@@ -1414,6 +1415,51 @@ def Test_unlet()
14141415
unlet ll[-1]
14151416
assert_equal([1, 3], ll)
14161417

1418+
ll = [1, 2, 3, 4]
1419+
unlet ll[0 : 1]
1420+
assert_equal([3, 4], ll)
1421+
1422+
ll = [1, 2, 3, 4]
1423+
unlet ll[2 : 8]
1424+
assert_equal([1, 2], ll)
1425+
1426+
ll = [1, 2, 3, 4]
1427+
unlet ll[-2 : -1]
1428+
assert_equal([1, 2], ll)
1429+
1430+
CheckDefFailure([
1431+
'var ll = [1, 2]',
1432+
'll[1 : 2] = 7',
1433+
], 'E1165:', 2)
1434+
CheckDefFailure([
1435+
'var dd = {a: 1}',
1436+
'unlet dd["a" : "a"]',
1437+
], 'E1166:', 2)
1438+
CheckDefExecFailure([
1439+
'unlet g:adict[0 : 1]',
1440+
], 'E1148:', 1)
1441+
CheckDefFailure([
1442+
'var ll = [1, 2]',
1443+
'unlet ll[0:1]',
1444+
], 'E1004:', 2)
1445+
CheckDefFailure([
1446+
'var ll = [1, 2]',
1447+
'unlet ll[0 :1]',
1448+
], 'E1004:', 2)
1449+
CheckDefFailure([
1450+
'var ll = [1, 2]',
1451+
'unlet ll[0: 1]',
1452+
], 'E1004:', 2)
1453+
1454+
CheckDefFailure([
1455+
'var ll = [1, 2]',
1456+
'unlet ll["x" : 1]',
1457+
], 'E1012:', 2)
1458+
CheckDefFailure([
1459+
'var ll = [1, 2]',
1460+
'unlet ll[0 : "x"]',
1461+
], 'E1012:', 2)
1462+
14171463
# list of dict unlet
14181464
var dl = [{a: 1, b: 2}, {c: 3}]
14191465
unlet dl[0]['b']

src/version.c

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

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2533,
753755
/**/
754756
2532,
755757
/**/

src/vim9.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ typedef enum {
6161
ISN_UNLET, // unlet variable isn_arg.unlet.ul_name
6262
ISN_UNLETENV, // unlet environment variable isn_arg.unlet.ul_name
6363
ISN_UNLETINDEX, // unlet item of list or dict
64+
ISN_UNLETRANGE, // unlet items of list
6465

6566
ISN_LOCKCONST, // lock constant value
6667

src/vim9compile.c

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5865,13 +5865,35 @@ compile_assign_unlet(
58655865
vartype_T dest_type;
58665866
size_t varlen = lhs->lhs_varlen;
58675867
garray_T *stack = &cctx->ctx_type_stack;
5868+
int range = FALSE;
58685869

58695870
// Compile the "idx" in "var[idx]" or "key" in "var.key".
58705871
p = var_start + varlen;
58715872
if (*p == '[')
58725873
{
58735874
p = skipwhite(p + 1);
58745875
r = compile_expr0(&p, cctx);
5876+
5877+
if (r == OK && *skipwhite(p) == ':')
5878+
{
5879+
// unlet var[idx : idx]
5880+
if (is_assign)
5881+
{
5882+
semsg(_(e_cannot_use_range_with_assignment_str), p);
5883+
return FAIL;
5884+
}
5885+
range = TRUE;
5886+
p = skipwhite(p);
5887+
if (!IS_WHITE_OR_NUL(p[-1]) || !IS_WHITE_OR_NUL(p[1]))
5888+
{
5889+
semsg(_(e_white_space_required_before_and_after_str_at_str),
5890+
":", p);
5891+
return FAIL;
5892+
}
5893+
p = skipwhite(p + 1);
5894+
r = compile_expr0(&p, cctx);
5895+
}
5896+
58755897
if (r == OK && *skipwhite(p) != ']')
58765898
{
58775899
// this should not happen
@@ -5897,17 +5919,29 @@ compile_assign_unlet(
58975919
else
58985920
{
58995921
dest_type = lhs->lhs_type->tt_type;
5922+
if (dest_type == VAR_DICT && range)
5923+
{
5924+
emsg(e_cannot_use_range_with_dictionary);
5925+
return FAIL;
5926+
}
59005927
if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL)
59015928
return FAIL;
5902-
if (dest_type == VAR_LIST
5903-
&& need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
5929+
if (dest_type == VAR_LIST)
5930+
{
5931+
if (range
5932+
&& need_type(((type_T **)stack->ga_data)[stack->ga_len - 2],
59045933
&t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
5905-
return FAIL;
5934+
return FAIL;
5935+
if (need_type(((type_T **)stack->ga_data)[stack->ga_len - 1],
5936+
&t_number, -1, 0, cctx, FALSE, FALSE) == FAIL)
5937+
return FAIL;
5938+
}
59065939
}
59075940

59085941
// Load the dict or list. On the stack we then have:
59095942
// - value (for assignment, not for :unlet)
59105943
// - index
5944+
// - for [a : b] second index
59115945
// - variable
59125946
if (lhs->lhs_dest == dest_expr)
59135947
{
@@ -5946,6 +5980,11 @@ compile_assign_unlet(
59465980
return FAIL;
59475981
isn->isn_arg.vartype = dest_type;
59485982
}
5983+
else if (range)
5984+
{
5985+
if (generate_instr_drop(cctx, ISN_UNLETRANGE, 3) == NULL)
5986+
return FAIL;
5987+
}
59495988
else
59505989
{
59515990
if (generate_instr_drop(cctx, ISN_UNLETINDEX, 2) == NULL)
@@ -8907,6 +8946,7 @@ delete_instr(isn_T *isn)
89078946
case ISN_TRY:
89088947
case ISN_TRYCONT:
89098948
case ISN_UNLETINDEX:
8949+
case ISN_UNLETRANGE:
89108950
case ISN_UNPACK:
89118951
// nothing allocated
89128952
break;

0 commit comments

Comments
 (0)