]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0952: Vim9: missing type checking for any type assignment v9.1.0952
authorYegappan Lakshmanan <yegappan@yahoo.com>
Sun, 22 Dec 2024 13:44:35 +0000 (14:44 +0100)
committerChristian Brabandt <cb@256bit.org>
Sun, 22 Dec 2024 13:44:35 +0000 (14:44 +0100)
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>
src/eval.c
src/structs.h
src/testdir/test_vim9_assign.vim
src/version.c
src/vim9execute.c

index 6ce59188513aed745bdbc598218cfee87998a310..f4f8c058db3aefea2770029c31a0287702214b07 100644 (file)
@@ -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);
 }
 
 /*
index dcb8978fed53e73432f068933b0e53fb705cc828..d6d4a0fef715a4b2c8813e1fb5c308a89bab91e0 100644 (file)
@@ -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;
 
 /**
index 1848c016c16664989bc3649c42e92078ee85acaa..616e224915c625feb0fe0736bb535d8d89c66f9e 100644 (file)
@@ -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
index f34048e91cd232351b91525596151d63c423732c..dc7a029f4bca94a6161c2e66c55c9792f7c4a436 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    952,
 /**/
     951,
 /**/
index 7ab6813a6955e08b82acc66716ae2b969bdce3d9..f20ff4064721f828246497b05a7c568e63e3307f 100644 (file)
@@ -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));
        }