mirror of
https://github.com/vim/vim
synced 2025-07-16 01:01:58 +00:00
patch 9.1.0952: Vim9: missing type checking for any type assignment
Problem: Vim9: missing type checking for any type assignment (Ernie Rael) Solution: when assigning to a list item, if the type of the LHS item is any, then use the list item type (Yegappan Lakshmanan) fixes: #15208 closes: #16274 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
62e3014ab1
commit
92195ae72f
13
src/eval.c
13
src/eval.c
@ -1489,8 +1489,17 @@ get_lval_list(
|
||||
return FAIL;
|
||||
|
||||
if (lp->ll_valtype != NULL && !lp->ll_range)
|
||||
{
|
||||
// use the type of the member
|
||||
lp->ll_valtype = lp->ll_valtype->tt_member;
|
||||
if (lp->ll_valtype->tt_member != NULL)
|
||||
lp->ll_valtype = lp->ll_valtype->tt_member;
|
||||
else
|
||||
// If the LHS member type is not known (VAR_ANY), then get it from
|
||||
// the list item (after indexing)
|
||||
lp->ll_valtype = typval2type(&lp->ll_li->li_tv, get_copyID(),
|
||||
&lp->ll_type_list, TVTT_DO_MEMBER);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* May need to find the item or absolute index for the second
|
||||
@ -1994,6 +2003,7 @@ get_lval(
|
||||
|
||||
// Clear everything in "lp".
|
||||
CLEAR_POINTER(lp);
|
||||
ga_init2(&lp->ll_type_list, sizeof(type_T *), 10);
|
||||
|
||||
if (skip || (flags & GLV_COMPILING))
|
||||
{
|
||||
@ -2178,6 +2188,7 @@ clear_lval(lval_T *lp)
|
||||
{
|
||||
vim_free(lp->ll_exp_name);
|
||||
vim_free(lp->ll_newkey);
|
||||
clear_type_list(&lp->ll_type_list);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4692,6 +4692,7 @@ typedef struct lval_S
|
||||
int ll_oi; // The object/class member index
|
||||
int ll_is_root; // TRUE if ll_tv is the lval_root, like a
|
||||
// plain object/class. ll_tv is variable.
|
||||
garray_T ll_type_list; // list of pointers to allocated types
|
||||
} lval_T;
|
||||
|
||||
/**
|
||||
|
@ -3707,4 +3707,46 @@ def Test_override_script_local_func()
|
||||
v9.CheckScriptFailure(lines, 'E705: Variable name conflicts with existing function: MyFunc', 5)
|
||||
enddef
|
||||
|
||||
" Test for doing a type check at runtime for a list member type
|
||||
def Test_nested_type_check()
|
||||
var lines =<< trim END
|
||||
var d = {a: [10], b: [20]}
|
||||
var l = d->items()
|
||||
l[0][1][0] = 'abc'
|
||||
END
|
||||
v9.CheckSourceDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got string')
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
var d = {a: [10], b: [20]}
|
||||
var l = d->items()
|
||||
l[0][1][0] = 30
|
||||
assert_equal([['a', [30]], ['b', [20]]], l)
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Foo()
|
||||
var d = {a: [10], b: [20]}
|
||||
var l = d->items()
|
||||
l[0][1][0] = 30
|
||||
assert_equal([['a', [30]], ['b', [20]]], l)
|
||||
enddef
|
||||
Foo()
|
||||
END
|
||||
v9.CheckScriptSuccess(lines)
|
||||
|
||||
# Test for modifying a List item added using add() with a different type.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
var l: list<list<any>> = [['a']]
|
||||
var v = [[10]]
|
||||
l[0]->add(v)
|
||||
l[0][1][0] = [{x: 20}]
|
||||
END
|
||||
v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected list<number> but got list<dict<number>>', 6)
|
||||
enddef
|
||||
|
||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
952,
|
||||
/**/
|
||||
951,
|
||||
/**/
|
||||
|
@ -2215,6 +2215,25 @@ handle_debug(isn_T *iptr, ectx_T *ectx)
|
||||
vim_free(line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a runtime check of the RHS value against the LHS List member type.
|
||||
* This is used by the STOREINDEX instruction to perform a type check
|
||||
* at runtime if compile time type check cannot be performed (VAR_ANY).
|
||||
* Returns FAIL if there is a type mismatch.
|
||||
*/
|
||||
static int
|
||||
storeindex_check_list_member_type(
|
||||
list_T *lhs_list,
|
||||
typval_T *rhs_tv,
|
||||
ectx_T *ectx)
|
||||
{
|
||||
if (lhs_list->lv_type == NULL || lhs_list->lv_type->tt_member == NULL)
|
||||
return OK;
|
||||
|
||||
return check_typval_type(lhs_list->lv_type->tt_member, rhs_tv,
|
||||
ectx->ec_where);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store a value in a list, dict, blob or object variable.
|
||||
* Returns OK, FAIL or NOTDONE (uncatchable error).
|
||||
@ -2228,6 +2247,7 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
long lidx = 0;
|
||||
typval_T *tv_dest = STACK_TV_BOT(-1);
|
||||
int status = OK;
|
||||
int check_rhs_type = FALSE;
|
||||
|
||||
if (tv_idx->v_type == VAR_NUMBER)
|
||||
lidx = (long)tv_idx->vval.v_number;
|
||||
@ -2247,6 +2267,7 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
}
|
||||
else if (dest_type == VAR_ANY)
|
||||
{
|
||||
check_rhs_type = TRUE;
|
||||
dest_type = tv_dest->v_type;
|
||||
if (dest_type == VAR_DICT)
|
||||
status = do_2string(tv_idx, TRUE, FALSE);
|
||||
@ -2327,6 +2348,12 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
semsg(_(e_list_index_out_of_range_nr), lidx);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// Do a runtime type check for VAR_ANY
|
||||
if (check_rhs_type &&
|
||||
storeindex_check_list_member_type(list, tv, ectx) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (lidx < list->lv_len)
|
||||
{
|
||||
listitem_T *li = list_find(list, lidx);
|
||||
@ -6304,7 +6331,7 @@ call_def_function(
|
||||
else if (check_typval_arg_type(expected, tv,
|
||||
NULL, argv_idx + 1) == FAIL)
|
||||
goto failed_early;
|
||||
}
|
||||
}
|
||||
if (!done)
|
||||
copy_tv(tv, STACK_TV_BOT(0));
|
||||
}
|
||||
|
Reference in New Issue
Block a user