changeset 23917:4b417b776b95 v8.2.2501

patch 8.2.2501: not always clear where an error is reported Commit: https://github.com/vim/vim/commit/f785aa1354208f6b644e891aa01f8f86d947af7e Author: Bram Moolenaar <Bram@vim.org> Date: Thu Feb 11 21:19:34 2021 +0100 patch 8.2.2501: not always clear where an error is reported Problem: Not always clear where an error is reported. Solution: Add the where_T structure and pass it around. (closes https://github.com/vim/vim/issues/7796)
author Bram Moolenaar <Bram@vim.org>
date Thu, 11 Feb 2021 21:30:04 +0100
parents cdb1a04f8189
children 331252d0c5e6
files src/dict.c src/errors.h src/eval.c src/evalvars.c src/list.c src/proto/eval.pro src/proto/evalvars.pro src/proto/vim9script.pro src/proto/vim9type.pro src/structs.h src/testdir/test_vim9_assign.vim src/version.c src/vim9compile.c src/vim9execute.c src/vim9script.c src/vim9type.c
diffstat 16 files changed, 146 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/src/dict.c
+++ b/src/dict.c
@@ -1089,7 +1089,8 @@ dict_extend(dict_T *d1, dict_T *d2, char
 	    }
 
 	    if (type != NULL
-		     && check_typval_type(type, &HI2DI(hi2)->di_tv, 0) == FAIL)
+		     && check_typval_arg_type(type, &HI2DI(hi2)->di_tv, 0)
+								       == FAIL)
 		break;
 
 	    if (di1 == NULL)
--- a/src/errors.h
+++ b/src/errors.h
@@ -361,3 +361,5 @@ EXTERN char e_cannot_json_encode_str[]
 	INIT(= N_("E1161: Cannot json encode a %s"));
 EXTERN char e_register_name_must_be_one_char_str[]
 	INIT(= N_("E1162: Register name must be one character: %s"));
+EXTERN char e_variable_nr_type_mismatch_expected_str_but_got_str[]
+	INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s"));
--- a/src/eval.c
+++ b/src/eval.c
@@ -1299,8 +1299,9 @@ set_var_lval(
     char_u	*endp,
     typval_T	*rettv,
     int		copy,
-    int		flags,    // ASSIGN_CONST, ASSIGN_NO_DECL
-    char_u	*op)
+    int		flags,	    // ASSIGN_CONST, ASSIGN_NO_DECL
+    char_u	*op,
+    int		var_idx)    // index for "let [a, b] = list"
 {
     int		cc;
     listitem_T	*ri;
@@ -1390,9 +1391,10 @@ set_var_lval(
 	else
 	{
 	    if (lp->ll_type != NULL
-			   && check_typval_type(lp->ll_type, rettv, 0) == FAIL)
+		       && check_typval_arg_type(lp->ll_type, rettv, 0) == FAIL)
 		return;
-	    set_var_const(lp->ll_name, lp->ll_type, rettv, copy, flags);
+	    set_var_const(lp->ll_name, lp->ll_type, rettv, copy,
+							       flags, var_idx);
 	}
 	*endp = cc;
     }
@@ -1471,7 +1473,7 @@ set_var_lval(
 	}
 
 	if (lp->ll_valtype != NULL
-			&& check_typval_type(lp->ll_valtype, rettv, 0) == FAIL)
+		    && check_typval_arg_type(lp->ll_valtype, rettv, 0) == FAIL)
 	    return;
 
 	if (lp->ll_newkey != NULL)
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -173,7 +173,7 @@ static void list_buf_vars(int *first);
 static void list_win_vars(int *first);
 static void list_tab_vars(int *first);
 static char_u *list_arg_vars(exarg_T *eap, char_u *arg, int *first);
-static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op);
+static char_u *ex_let_one(char_u *arg, typval_T *tv, int copy, int flags, char_u *endchars, char_u *op, int var_idx);
 static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
 static int do_lock_var(lval_T *lp, char_u *name_end, exarg_T *eap, int deep, void *cookie);
 static void list_one_var(dictitem_T *v, char *prefix, int *first);
@@ -929,13 +929,14 @@ ex_let_vars(
     char_u	*arg = arg_start;
     list_T	*l;
     int		i;
+    int		var_idx = 0;
     listitem_T	*item;
     typval_T	ltv;
 
     if (*arg != '[')
     {
 	// ":let var = expr" or ":for var in list"
-	if (ex_let_one(arg, tv, copy, flags, op, op) == NULL)
+	if (ex_let_one(arg, tv, copy, flags, op, op, var_idx) == NULL)
 	    return FAIL;
 	return OK;
     }
@@ -964,7 +965,9 @@ ex_let_vars(
     while (*arg != ']')
     {
 	arg = skipwhite(arg + 1);
-	arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]", op);
+	++var_idx;
+	arg = ex_let_one(arg, &item->li_tv, TRUE, flags, (char_u *)",;]",
+								  op, var_idx);
 	item = item->li_next;
 	if (arg == NULL)
 	    return FAIL;
@@ -987,9 +990,10 @@ ex_let_vars(
 	    ltv.v_lock = 0;
 	    ltv.vval.v_list = l;
 	    l->lv_refcount = 1;
+	    ++var_idx;
 
 	    arg = ex_let_one(skipwhite(arg + 1), &ltv, FALSE, flags,
-							    (char_u *)"]", op);
+						   (char_u *)"]", op, var_idx);
 	    clear_tv(&ltv);
 	    if (arg == NULL)
 		return FAIL;
@@ -1284,7 +1288,8 @@ ex_let_one(
     int		copy,		// copy value from "tv"
     int		flags,		// ASSIGN_CONST, ASSIGN_FINAL, etc.
     char_u	*endchars,	// valid chars after variable name  or NULL
-    char_u	*op)		// "+", "-", "."  or NULL
+    char_u	*op,		// "+", "-", "."  or NULL
+    int		var_idx)	// variable index for "let [a, b] = list"
 {
     int		c1;
     char_u	*name;
@@ -1508,7 +1513,7 @@ ex_let_one(
 		emsg(_(e_letunexp));
 	    else
 	    {
-		set_var_lval(&lv, p, tv, copy, flags, op);
+		set_var_lval(&lv, p, tv, copy, flags, op, var_idx);
 		arg_end = p;
 	    }
 	}
@@ -3075,7 +3080,7 @@ set_var(
     typval_T	*tv,
     int		copy)	    // make copy of value in "tv"
 {
-    set_var_const(name, NULL, tv, copy, ASSIGN_DECL);
+    set_var_const(name, NULL, tv, copy, ASSIGN_DECL, 0);
 }
 
 /*
@@ -3089,7 +3094,8 @@ set_var_const(
     type_T	*type,
     typval_T	*tv_arg,
     int		copy,	    // make copy of value in "tv"
-    int		flags)	    // ASSIGN_CONST, ASSIGN_FINAL, etc.
+    int		flags,	    // ASSIGN_CONST, ASSIGN_FINAL, etc.
+    int		var_idx)    // index for ":let [a, b] = list"
 {
     typval_T	*tv = tv_arg;
     typval_T	bool_tv;
@@ -3148,6 +3154,8 @@ set_var_const(
 
 	    if (is_script_local && vim9script)
 	    {
+		where_T where;
+
 		if ((flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0)
 		{
 		    semsg(_(e_redefining_script_item_str), name);
@@ -3155,7 +3163,9 @@ set_var_const(
 		}
 
 		// check the type and adjust to bool if needed
-		if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
+		where.wt_index = var_idx;
+		where.wt_variable = TRUE;
+		if (check_script_var_type(&di->di_tv, tv, name, where) == FAIL)
 		    goto failed;
 	    }
 
@@ -3719,10 +3729,10 @@ var_redir_start(char_u *name, int append
     tv.vval.v_string = (char_u *)"";
     if (append)
 	set_var_lval(redir_lval, redir_endp, &tv, TRUE,
-						ASSIGN_NO_DECL, (char_u *)".");
+					     ASSIGN_NO_DECL, (char_u *)".", 0);
     else
 	set_var_lval(redir_lval, redir_endp, &tv, TRUE,
-						ASSIGN_NO_DECL, (char_u *)"=");
+					     ASSIGN_NO_DECL, (char_u *)"=", 0);
     clear_lval(redir_lval);
     if (called_emsg > called_emsg_before)
     {
@@ -3794,7 +3804,7 @@ var_redir_stop(void)
 					FALSE, FALSE, 0, FNE_CHECK_START);
 	    if (redir_endp != NULL && redir_lval->ll_name != NULL)
 		set_var_lval(redir_lval, redir_endp, &tv, FALSE, 0,
-								(char_u *)".");
+							     (char_u *)".", 0);
 	    clear_lval(redir_lval);
 	}
 
--- a/src/list.c
+++ b/src/list.c
@@ -698,7 +698,7 @@ list_insert_tv(list_T *l, typval_T *tv, 
     listitem_T	*ni;
 
     if (l->lv_type != NULL && l->lv_type->tt_member != NULL
-		    && check_typval_type(l->lv_type->tt_member, tv, 0) == FAIL)
+		&& check_typval_arg_type(l->lv_type->tt_member, tv, 0) == FAIL)
 	return FAIL;
     ni = listitem_alloc();
     if (ni == NULL)
@@ -2135,8 +2135,8 @@ filter_map(typval_T *argvars, typval_T *
 		    }
 		    if (filtermap == FILTERMAP_MAP)
 		    {
-			if (type != NULL && check_typval_type(type->tt_member,
-							    &newtv, 0) == FAIL)
+			if (type != NULL && check_typval_arg_type(
+					   type->tt_member, &newtv, 0) == FAIL)
 			{
 			    clear_tv(&newtv);
 			    break;
@@ -2270,8 +2270,8 @@ filter_map(typval_T *argvars, typval_T *
 		    if (filtermap != FILTERMAP_FILTER)
 		    {
 			if (filtermap == FILTERMAP_MAP && type != NULL
-					  && check_typval_type(type->tt_member,
-							    &newtv, 0) == FAIL)
+				      && check_typval_arg_type(
+					   type->tt_member, &newtv, 0) == FAIL)
 			{
 			    clear_tv(&newtv);
 			    break;
@@ -2314,8 +2314,8 @@ filter_map(typval_T *argvars, typval_T *
 		    }
 		    if (filtermap == FILTERMAP_MAP)
 		    {
-			if (type != NULL && check_typval_type(type->tt_member,
-							    &newtv, 0) == FAIL)
+			if (type != NULL && check_typval_arg_type(
+					   type->tt_member, &newtv, 0) == FAIL)
 			{
 			    clear_tv(&newtv);
 			    break;
@@ -2584,7 +2584,8 @@ extend(typval_T *argvars, typval_T *rett
 	    }
 	    else
 		item = NULL;
-	    if (type != NULL && check_typval_type(type, &argvars[1], 2) == FAIL)
+	    if (type != NULL && check_typval_arg_type(
+						 type, &argvars[1], 2) == FAIL)
 		goto theend;
 	    list_extend(l1, l2, item);
 
@@ -2641,7 +2642,8 @@ extend(typval_T *argvars, typval_T *rett
 	    else
 		action = (char_u *)"force";
 
-	    if (type != NULL && check_typval_type(type, &argvars[1], 2) == FAIL)
+	    if (type != NULL && check_typval_arg_type(
+						 type, &argvars[1], 2) == FAIL)
 		goto theend;
 	    dict_extend(d1, d2, action);
 
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -25,7 +25,7 @@ void *call_func_retlist(char_u *func, in
 int eval_foldexpr(char_u *arg, int *cp);
 char_u *get_lval(char_u *name, typval_T *rettv, lval_T *lp, int unlet, int skip, int flags, int fne_flags);
 void clear_lval(lval_T *lp);
-void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op);
+void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, int flags, char_u *op, int var_idx);
 void *eval_for_line(char_u *arg, int *errp, exarg_T *eap, evalarg_T *evalarg);
 void skip_for_lines(void *fi_void, evalarg_T *evalarg);
 int next_for_item(void *fi_void, char_u *arg);
--- a/src/proto/evalvars.pro
+++ b/src/proto/evalvars.pro
@@ -70,7 +70,7 @@ void vars_clear(hashtab_T *ht);
 void vars_clear_ext(hashtab_T *ht, int free_val);
 void delete_var(hashtab_T *ht, hashitem_T *hi);
 void set_var(char_u *name, typval_T *tv, int copy);
-void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags);
+void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags, int var_idx);
 int var_check_permission(dictitem_T *di, char_u *name);
 int var_check_ro(int flags, char_u *name, int use_gettext);
 int var_check_lock(int flags, char_u *name, int use_gettext);
--- a/src/proto/vim9script.pro
+++ b/src/proto/vim9script.pro
@@ -14,5 +14,5 @@ void update_vim9_script_var(int create, 
 void hide_script_var(scriptitem_T *si, int idx, int func_defined);
 void free_all_script_vars(scriptitem_T *si);
 svar_T *find_typval_in_script(typval_T *dest);
-int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
+int check_script_var_type(typval_T *dest, typval_T *value, char_u *name, where_T where);
 /* vim: set ft=c : */
--- a/src/proto/vim9type.pro
+++ b/src/proto/vim9type.pro
@@ -11,10 +11,12 @@ int func_type_add_arg_types(type_T *func
 int need_convert_to_bool(type_T *type, typval_T *tv);
 type_T *typval2type(typval_T *tv, garray_T *type_gap);
 type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap);
-int check_typval_type(type_T *expected, typval_T *actual_tv, int argidx);
+int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx);
+int check_typval_type(type_T *expected, typval_T *actual_tv, where_T where);
 void type_mismatch(type_T *expected, type_T *actual);
-void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
-int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
+void arg_type_mismatch(type_T *expected, type_T *actual, int arg_idx);
+void type_mismatch_where(type_T *expected, type_T *actual, where_T where);
+int check_type(type_T *expected, type_T *actual, int give_msg, where_T where);
 int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name);
 char_u *skip_type(char_u *start, int optional);
 type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
--- a/src/structs.h
+++ b/src/structs.h
@@ -4387,3 +4387,10 @@ typedef enum {
     MAGIC_ON = 3,		// "\m" or 'magic'
     MAGIC_ALL = 4		// "\v" very magic
 } magic_T;
+
+// Struct used to pass to error messages about where the error happened.
+typedef struct {
+    char    wt_index;	    // argument or variable index, 0 means unknown
+    char    wt_variable;    // "variable" when TRUE, "argument" otherwise
+} where_T;
+
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -284,6 +284,14 @@ def Test_assign_unpack()
       [v1, v2] = ''
   END
   CheckDefFailure(lines, 'E1012: Type mismatch; expected list<any> but got string', 3)
+
+  lines =<< trim END
+    g:values = [false, 0]
+    var x: bool
+    var y: string
+    [x, y] = g:values
+  END
+  CheckDefExecAndScriptFailure(lines, 'E1163: Variable 2: type mismatch, expected string but got number')
 enddef
 
 def Test_assign_linebreak()
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2501,
+/**/
     2500,
 /**/
     2499,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -893,6 +893,8 @@ need_type(
 	int	silent,
 	int	actual_is_const)
 {
+    where_T where;
+
     if (expected == &t_bool && actual != &t_bool
 					&& (actual->tt_flags & TTFLAG_BOOL_OK))
     {
@@ -902,7 +904,9 @@ need_type(
 	return OK;
     }
 
-    if (check_type(expected, actual, FALSE, arg_idx) == OK)
+    where.wt_index = arg_idx;
+    where.wt_variable = FALSE;
+    if (check_type(expected, actual, FALSE, where) == OK)
 	return OK;
 
     // If the actual type can be the expected type add a runtime check.
@@ -4287,10 +4291,13 @@ compile_expr7t(char_u **arg, cctx_T *cct
     {
 	garray_T    *stack = &cctx->ctx_type_stack;
 	type_T	    *actual;
+	where_T	    where;
 
 	generate_ppconst(cctx, ppconst);
 	actual = ((type_T **)stack->ga_data)[stack->ga_len - 1];
-	if (check_type(want_type, actual, FALSE, 0) == FAIL)
+	where.wt_index = 0;
+	where.wt_variable = FALSE;
+	if (check_type(want_type, actual, FALSE, where) == FAIL)
 	{
 	    if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) == FAIL)
 		return FAIL;
@@ -8078,6 +8085,7 @@ compile_def_function(
 	    garray_T	*stack = &cctx.ctx_type_stack;
 	    type_T	*val_type;
 	    int		arg_idx = first_def_arg + i;
+	    where_T	where;
 
 	    ufunc->uf_def_arg_idx[i] = instr->ga_len;
 	    arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i];
@@ -8088,13 +8096,15 @@ compile_def_function(
 	    // Otherwise check that the default value type matches the
 	    // specified type.
 	    val_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
+	    where.wt_index = arg_idx + 1;
+	    where.wt_variable = FALSE;
 	    if (ufunc->uf_arg_types[arg_idx] == &t_unknown)
 	    {
 		did_set_arg_type = TRUE;
 		ufunc->uf_arg_types[arg_idx] = val_type;
 	    }
 	    else if (check_type(ufunc->uf_arg_types[arg_idx], val_type,
-						    TRUE, arg_idx + 1) == FAIL)
+							  TRUE, where) == FAIL)
 		goto erret;
 
 	    if (generate_STORE(&cctx, ISN_STORE, i - count - off, NULL) == FAIL)
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -851,7 +851,7 @@ store_var(char_u *name, typval_T *tv)
     funccal_entry_T entry;
 
     save_funccal(&entry);
-    set_var_const(name, NULL, tv, FALSE, ASSIGN_DECL);
+    set_var_const(name, NULL, tv, FALSE, ASSIGN_DECL, 0);
     restore_funccal();
 }
 
@@ -1146,6 +1146,7 @@ call_def_function(
     int		save_did_emsg_def = did_emsg_def;
     int		trylevel_at_start = trylevel;
     int		orig_funcdepth;
+    where_T	where;
 
 // Get pointer to item in the stack.
 #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx)
@@ -1202,7 +1203,7 @@ call_def_function(
 									 ++idx)
     {
 	if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
-		&& check_typval_type(ufunc->uf_arg_types[idx], &argv[idx],
+		&& check_typval_arg_type(ufunc->uf_arg_types[idx], &argv[idx],
 							      idx + 1) == FAIL)
 	    goto failed_early;
 	copy_tv(&argv[idx], STACK_TV_BOT(0));
@@ -1233,7 +1234,7 @@ call_def_function(
 
 	    for (idx = 0; idx < vararg_count; ++idx)
 	    {
-		if (check_typval_type(expected, &li->li_tv,
+		if (check_typval_arg_type(expected, &li->li_tv,
 						       argc + idx + 1) == FAIL)
 		    goto failed_early;
 		li = li->li_next;
@@ -1333,6 +1334,9 @@ call_def_function(
     emsg_silent_def = emsg_silent;
     did_emsg_def = 0;
 
+    where.wt_index = 0;
+    where.wt_variable = FALSE;
+
     // Decide where to start execution, handles optional arguments.
     init_instr_idx(ufunc, argc, &ectx);
 
@@ -3170,6 +3174,11 @@ call_def_function(
 			goto failed;
 		    ++ectx.ec_stack.ga_len;
 		    copy_tv(&li->li_tv, STACK_TV_BOT(-1));
+
+		    // Useful when used in unpack assignment.  Reset at
+		    // ISN_DROP.
+		    where.wt_index = index + 1;
+		    where.wt_variable = TRUE;
 		}
 		break;
 
@@ -3288,9 +3297,12 @@ call_def_function(
 
 		    tv = STACK_TV_BOT((int)ct->ct_off);
 		    SOURCING_LNUM = iptr->isn_lnum;
-		    if (check_typval_type(ct->ct_type, tv, ct->ct_arg_idx)
-								       == FAIL)
+		    if (!where.wt_variable)
+			where.wt_index = ct->ct_arg_idx;
+		    if (check_typval_type(ct->ct_type, tv, where) == FAIL)
 			goto on_error;
+		    if (!where.wt_variable)
+			where.wt_index = 0;
 
 		    // number 0 is FALSE, number 1 is TRUE
 		    if (tv->v_type == VAR_NUMBER
@@ -3573,6 +3585,8 @@ call_def_function(
 	    case ISN_DROP:
 		--ectx.ec_stack.ga_len;
 		clear_tv(STACK_TV_BOT(0));
+		where.wt_index = 0;
+		where.wt_variable = FALSE;
 		break;
 	}
 	continue;
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -650,7 +650,7 @@ vim9_declare_scriptvar(exarg_T *eap, cha
 	init_tv.v_type = VAR_NUMBER;
     else
 	init_tv.v_type = type->tt_type;
-    set_var_const(name, type, &init_tv, FALSE, 0);
+    set_var_const(name, type, &init_tv, FALSE, 0, 0);
 
     vim_free(name);
     return p;
@@ -855,7 +855,7 @@ find_typval_in_script(typval_T *dest)
 	if (sv->sv_name != NULL && sv->sv_tv == dest)
 	    return sv;
     }
-    iemsg("check_script_var_type(): not found");
+    iemsg("find_typval_in_script(): not found");
     return NULL;
 }
 
@@ -864,7 +864,11 @@ find_typval_in_script(typval_T *dest)
  * If needed convert "value" to a bool.
  */
     int
-check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
+check_script_var_type(
+	typval_T    *dest,
+	typval_T    *value,
+	char_u	    *name,
+	where_T	    where)
 {
     svar_T  *sv = find_typval_in_script(dest);
     int	    ret;
@@ -876,7 +880,7 @@ check_script_var_type(typval_T *dest, ty
 	    semsg(_(e_readonlyvar), name);
 	    return FAIL;
 	}
-	ret = check_typval_type(sv->sv_type, value, 0);
+	ret = check_typval_type(sv->sv_type, value, where);
 	if (ret == OK && need_convert_to_bool(sv->sv_type, value))
 	{
 	    int	val = tv2bool(value);
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -399,13 +399,22 @@ typval2type_vimvar(typval_T *tv, garray_
     return typval2type(tv, type_gap);
 }
 
+    int
+check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx)
+{
+    where_T	where;
+
+    where.wt_index = arg_idx;
+    where.wt_variable = FALSE;
+    return check_typval_type(expected, actual_tv, where);
+}
 
 /*
  * Return FAIL if "expected" and "actual" don't match.
  * When "argidx" > 0 it is included in the error message.
  */
     int
-check_typval_type(type_T *expected, typval_T *actual_tv, int argidx)
+check_typval_type(type_T *expected, typval_T *actual_tv, where_T where)
 {
     garray_T	type_list;
     type_T	*actual_type;
@@ -414,7 +423,7 @@ check_typval_type(type_T *expected, typv
     ga_init2(&type_list, sizeof(type_T *), 10);
     actual_type = typval2type(actual_tv, &type_list);
     if (actual_type != NULL)
-	res = check_type(expected, actual_type, TRUE, argidx);
+	res = check_type(expected, actual_type, TRUE, where);
     clear_type_list(&type_list);
     return res;
 }
@@ -426,15 +435,29 @@ type_mismatch(type_T *expected, type_T *
 }
 
     void
-arg_type_mismatch(type_T *expected, type_T *actual, int argidx)
+arg_type_mismatch(type_T *expected, type_T *actual, int arg_idx)
+{
+    where_T	where;
+
+    where.wt_index = arg_idx;
+    where.wt_variable = FALSE;
+    type_mismatch_where(expected, actual, where);
+}
+
+    void
+type_mismatch_where(type_T *expected, type_T *actual, where_T where)
 {
     char *tofree1, *tofree2;
     char *typename1 = type_name(expected, &tofree1);
     char *typename2 = type_name(actual, &tofree2);
 
-    if (argidx > 0)
-	semsg(_(e_argument_nr_type_mismatch_expected_str_but_got_str),
-						 argidx, typename1, typename2);
+    if (where.wt_index > 0)
+    {
+	semsg(_(where.wt_variable
+			? e_variable_nr_type_mismatch_expected_str_but_got_str
+			: e_argument_nr_type_mismatch_expected_str_but_got_str),
+					 where.wt_index, typename1, typename2);
+    }
     else
 	semsg(_(e_type_mismatch_expected_str_but_got_str),
 							 typename1, typename2);
@@ -448,7 +471,7 @@ arg_type_mismatch(type_T *expected, type
  * When "argidx" > 0 it is included in the error message.
  */
     int
-check_type(type_T *expected, type_T *actual, int give_msg, int argidx)
+check_type(type_T *expected, type_T *actual, int give_msg, where_T where)
 {
     int ret = OK;
 
@@ -469,7 +492,7 @@ check_type(type_T *expected, type_T *act
 		// Using number 0 or 1 for bool is OK.
 		return OK;
 	    if (give_msg)
-		arg_type_mismatch(expected, actual, argidx);
+		type_mismatch_where(expected, actual, where);
 	    return FAIL;
 	}
 	if (expected->tt_type == VAR_DICT || expected->tt_type == VAR_LIST)
@@ -477,7 +500,7 @@ check_type(type_T *expected, type_T *act
 	    // "unknown" is used for an empty list or dict
 	    if (actual->tt_member != &t_unknown)
 		ret = check_type(expected->tt_member, actual->tt_member,
-								     FALSE, 0);
+								 FALSE, where);
 	}
 	else if (expected->tt_type == VAR_FUNC)
 	{
@@ -486,7 +509,7 @@ check_type(type_T *expected, type_T *act
 	    if (expected->tt_member != &t_unknown
 					    && actual->tt_member != &t_unknown)
 		ret = check_type(expected->tt_member, actual->tt_member,
-								     FALSE, 0);
+								 FALSE, where);
 	    if (ret == OK && expected->tt_argcount != -1
 		    && actual->tt_argcount != -1
 		    && (actual->tt_argcount < expected->tt_min_argcount
@@ -500,8 +523,8 @@ check_type(type_T *expected, type_T *act
 		for (i = 0; i < expected->tt_argcount; ++i)
 		    // Allow for using "any" argument type, lambda's have them.
 		    if (actual->tt_args[i] != &t_any && check_type(
-			    expected->tt_args[i], actual->tt_args[i], FALSE, 0)
-								       == FAIL)
+			    expected->tt_args[i], actual->tt_args[i], FALSE,
+								where) == FAIL)
 		    {
 			ret = FAIL;
 			break;
@@ -509,7 +532,7 @@ check_type(type_T *expected, type_T *act
 	    }
 	}
 	if (ret == FAIL && give_msg)
-	    arg_type_mismatch(expected, actual, argidx);
+	    type_mismatch_where(expected, actual, where);
     }
     return ret;
 }
@@ -552,7 +575,7 @@ check_argument_types(
 	    expected = type->tt_args[type->tt_argcount - 1]->tt_member;
 	else
 	    expected = type->tt_args[i];
-	if (check_typval_type(expected, &argvars[i], i + 1) == FAIL)
+	if (check_typval_arg_type(expected, &argvars[i], i + 1) == FAIL)
 	    return FAIL;
     }
     return OK;