# HG changeset patch # User Bram Moolenaar # Date 1626958805 -7200 # Node ID cfbf40f749b0813414b839cfb4ba8ad79f201ad4 # Parent 6d48afa9f12d6429c31158ca1e5eff492f0fe5ca patch 8.2.3200: Vim9: hard to guess where a type error is given Commit: https://github.com/vim/vim/commit/7a3fe3e180bdbce8f193abdf399559c5154bdaae Author: Bram Moolenaar Date: Thu Jul 22 14:58:47 2021 +0200 patch 8.2.3200: Vim9: hard to guess where a type error is given Problem: Vim9: hard to guess where a type error is given. Solution: Add the function name where possible. (closes https://github.com/vim/vim/issues/8608) diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -1073,7 +1073,7 @@ failret: * Otherwise duplicate keys are ignored ("action" is "keep"). */ void -dict_extend(dict_T *d1, dict_T *d2, char_u *action) +dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name) { dictitem_T *di1; hashitem_T *hi2; @@ -1106,8 +1106,8 @@ dict_extend(dict_T *d1, dict_T *d2, char } if (type != NULL - && check_typval_arg_type(type, &HI2DI(hi2)->di_tv, 0) - == FAIL) + && check_typval_arg_type(type, &HI2DI(hi2)->di_tv, + func_name, 0) == FAIL) break; if (di1 == NULL) diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -192,8 +192,12 @@ EXTERN char e_name_too_long_str[] INIT(= N_("E1011: Name too long: %s")); EXTERN char e_type_mismatch_expected_str_but_got_str[] INIT(= N_("E1012: Type mismatch; expected %s but got %s")); +EXTERN char e_type_mismatch_expected_str_but_got_str_in_str[] + INIT(= N_("E1012: Type mismatch; expected %s but got %s in %s")); EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[] INIT(= N_("E1013: Argument %d: type mismatch, expected %s but got %s")); +EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str_in_str[] + INIT(= N_("E1013: Argument %d: type mismatch, expected %s but got %s in %s")); EXTERN char e_invalid_key_str[] INIT(= N_("E1014: Invalid key: %s")); EXTERN char e_name_expected_str[] @@ -494,6 +498,8 @@ EXTERN char e_register_name_must_be_one_ 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")); +EXTERN char e_variable_nr_type_mismatch_expected_str_but_got_str_in_str[] + INIT(= N_("E1163: Variable %d: type mismatch, expected %s but got %s in %s")); EXTERN char e_vim9cmd_must_be_followed_by_command[] INIT(= N_("E1164: vim9cmd must be followed by a command")); EXTERN char e_cannot_use_range_with_assignment_str[] diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1365,8 +1365,8 @@ set_var_lval( } else { - if (lp->ll_type != NULL - && check_typval_arg_type(lp->ll_type, rettv, 0) == FAIL) + if (lp->ll_type != NULL && check_typval_arg_type(lp->ll_type, rettv, + NULL, 0) == FAIL) return; set_var_const(lp->ll_name, lp->ll_type, rettv, copy, flags, var_idx); @@ -1450,7 +1450,8 @@ set_var_lval( } if (lp->ll_valtype != NULL - && check_typval_arg_type(lp->ll_valtype, rettv, 0) == FAIL) + && check_typval_arg_type(lp->ll_valtype, rettv, + NULL, 0) == FAIL) return; if (lp->ll_newkey != NULL) diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -2043,7 +2043,7 @@ DictionaryUpdate(DictionaryObject *self, return NULL; VimTryStart(); - dict_extend(self->dict, tv.vval.v_dict, (char_u *) "force"); + dict_extend(self->dict, tv.vval.v_dict, (char_u *) "force", NULL); clear_tv(&tv); if (VimTryEnd()) return NULL; diff --git a/src/list.c b/src/list.c --- a/src/list.c +++ b/src/list.c @@ -605,7 +605,8 @@ list_append_tv(list_T *l, typval_T *tv) listitem_T *li; if (l->lv_type != NULL && l->lv_type->tt_member != NULL - && check_typval_arg_type(l->lv_type->tt_member, tv, 0) == FAIL) + && check_typval_arg_type(l->lv_type->tt_member, tv, + NULL, 0) == FAIL) return FAIL; li = listitem_alloc(); if (li == NULL) @@ -722,7 +723,8 @@ 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_arg_type(l->lv_type->tt_member, tv, 0) == FAIL) + && check_typval_arg_type(l->lv_type->tt_member, tv, + NULL, 0) == FAIL) return FAIL; ni = listitem_alloc(); if (ni == NULL) @@ -2085,9 +2087,9 @@ filter_map(typval_T *argvars, typval_T * blob_T *b = NULL; int rem; int todo; - char_u *ermsg = (char_u *)(filtermap == FILTERMAP_MAP ? "map()" + char *func_name = filtermap == FILTERMAP_MAP ? "map()" : filtermap == FILTERMAP_MAPNEW ? "mapnew()" - : "filter()"); + : "filter()"; char_u *arg_errmsg = (char_u *)(filtermap == FILTERMAP_MAP ? N_("map() argument") : filtermap == FILTERMAP_MAPNEW @@ -2144,7 +2146,7 @@ filter_map(typval_T *argvars, typval_T * } else { - semsg(_(e_listdictblobarg), ermsg); + semsg(_(e_listdictblobarg), func_name); goto theend; } @@ -2210,7 +2212,8 @@ filter_map(typval_T *argvars, typval_T * if (filtermap == FILTERMAP_MAP) { if (type != NULL && check_typval_arg_type( - type->tt_member, &newtv, 0) == FAIL) + type->tt_member, &newtv, + func_name, 0) == FAIL) { clear_tv(&newtv); break; @@ -2345,7 +2348,8 @@ filter_map(typval_T *argvars, typval_T * { if (filtermap == FILTERMAP_MAP && type != NULL && check_typval_arg_type( - type->tt_member, &newtv, 0) == FAIL) + type->tt_member, &newtv, + func_name, 0) == FAIL) { clear_tv(&newtv); break; @@ -2389,7 +2393,7 @@ filter_map(typval_T *argvars, typval_T * if (filtermap == FILTERMAP_MAP) { if (type != NULL && check_typval_arg_type( - type->tt_member, &newtv, 0) == FAIL) + type->tt_member, &newtv, func_name, 0) == FAIL) { clear_tv(&newtv); break; @@ -2627,6 +2631,7 @@ extend(typval_T *argvars, typval_T *rett { type_T *type = NULL; garray_T type_list; + char *func_name = is_new ? "extendnew()" : "extend()"; if (!is_new && in_vim9script()) { @@ -2680,7 +2685,7 @@ extend(typval_T *argvars, typval_T *rett else item = NULL; if (type != NULL && check_typval_arg_type( - type, &argvars[1], 2) == FAIL) + type, &argvars[1], func_name, 2) == FAIL) goto theend; list_extend(l1, l2, item); @@ -2737,10 +2742,10 @@ extend(typval_T *argvars, typval_T *rett else action = (char_u *)"force"; - if (type != NULL && check_typval_arg_type( - type, &argvars[1], 2) == FAIL) + if (type != NULL && check_typval_arg_type(type, &argvars[1], + func_name, 2) == FAIL) goto theend; - dict_extend(d1, d2, action); + dict_extend(d1, d2, action, func_name); if (is_new) { @@ -2753,7 +2758,7 @@ extend(typval_T *argvars, typval_T *rett } } else - semsg(_(e_listdictarg), is_new ? "extendnew()" : "extend()"); + semsg(_(e_listdictarg), func_name); theend: if (type != NULL) diff --git a/src/proto/dict.pro b/src/proto/dict.pro --- a/src/proto/dict.pro +++ b/src/proto/dict.pro @@ -37,7 +37,7 @@ char_u *dict2string(typval_T *tv, int co char_u *skip_literal_key(char_u *key); char_u *get_literal_key(char_u **arg); int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal); -void dict_extend(dict_T *d1, dict_T *d2, char_u *action); +void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name); dictitem_T *dict_lookup(hashitem_T *hi); int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); void f_items(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -11,7 +11,7 @@ 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, int copyID, garray_T *type_gap, int do_member); type_T *typval2type_vimvar(typval_T *tv, garray_T *type_gap); -int check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx); +int check_typval_arg_type(type_T *expected, typval_T *actual_tv, char *func_name, 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 arg_idx); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4438,7 +4438,10 @@ typedef enum { // Struct used to pass to error messages about where the error happened. typedef struct { + char *wt_func_name; // function name or NULL char wt_index; // argument or variable index, 0 means unknown char wt_variable; // "variable" when TRUE, "argument" otherwise } where_T; +#define WHERE_INIT {NULL, 0, 0} + diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -795,6 +795,8 @@ def Test_extend_arg_types() CheckDefFailure(['extend([1], ["b"])'], 'E1013: Argument 2: type mismatch, expected list but got list') CheckDefExecFailure(['extend([1], ["b", 1])'], 'E1013: Argument 2: type mismatch, expected list but got list') + + CheckScriptFailure(['vim9script', 'extend([1], ["b", 1])'], 'E1013: Argument 2: type mismatch, expected list but got list in extend()') enddef func g:ExtendDict(d) @@ -1741,19 +1743,19 @@ def Test_map_item_type() var l: list = [0] echo map(l, (_, v) => []) END - CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) lines =<< trim END var l: list = range(2) echo map(l, (_, v) => []) END - CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) lines =<< trim END var d: dict = {key: 0} echo map(d, (_, v) => []) END - CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list in map()', 2) enddef def Test_maparg() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3200, +/**/ 3199, /**/ 3198, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1033,7 +1033,7 @@ need_type( int silent, int actual_is_const) { - where_T where; + where_T where = WHERE_INIT; if (expected == &t_bool && actual != &t_bool && (actual->tt_flags & TTFLAG_BOOL_OK)) @@ -1045,7 +1045,6 @@ need_type( } where.wt_index = arg_idx; - where.wt_variable = FALSE; if (check_type(expected, actual, FALSE, where) == OK) return OK; @@ -2804,10 +2803,8 @@ check_ppconst_bool(ppconst_T *ppconst) if (ppconst->pp_used > 0) { typval_T *tv = &ppconst->pp_tv[ppconst->pp_used - 1]; - where_T where; - - where.wt_index = 0; - where.wt_variable = FALSE; + where_T where = WHERE_INIT; + return check_typval_type(&t_bool, tv, where); } return OK; @@ -4822,12 +4819,10 @@ compile_expr7t(char_u **arg, cctx_T *cct { garray_T *stack = &cctx->ctx_type_stack; type_T *actual; - where_T where; + where_T where = WHERE_INIT; generate_ppconst(cctx, ppconst); actual = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - 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) @@ -7975,7 +7970,7 @@ compile_for(char_u *arg_start, cctx_T *c int vimvaridx = -1; type_T *type = &t_any; type_T *lhs_type = &t_any; - where_T where; + where_T where = WHERE_INIT; p = skip_var_one(arg, FALSE); varlen = p - arg; @@ -9325,7 +9320,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; + where_T where = WHERE_INIT; int r; int jump_instr_idx = instr->ga_len; isn_T *isn; @@ -9348,7 +9343,6 @@ compile_def_function( // 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; diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -730,15 +730,18 @@ call_bfunc(int func_idx, int argcount, e int idx; int did_emsg_before = did_emsg; ectx_T *prev_ectx = current_ectx; + char *save_func_name = ectx->ec_where.wt_func_name; if (call_prepare(argcount, argvars, ectx) == FAIL) return FAIL; + ectx->ec_where.wt_func_name = internal_func_name(func_idx); // Call the builtin function. Set "current_ectx" so that when it // recursively invokes call_def_function() a closure context can be set. current_ectx = ectx; call_internal_func_by_idx(func_idx, argvars, STACK_TV_BOT(-1)); current_ectx = prev_ectx; + ectx->ec_where.wt_func_name = save_func_name; // Clear the arguments. for (idx = 0; idx < argcount; ++idx) @@ -907,7 +910,7 @@ call_by_name( else if (ufunc->uf_va_type != NULL) type = ufunc->uf_va_type->tt_member; if (type != NULL && check_typval_arg_type(type, - &argv[i], i + 1) == FAIL) + &argv[i], NULL, i + 1) == FAIL) return FAIL; } } @@ -4535,7 +4538,8 @@ call_def_function( { if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len && check_typval_arg_type( - ufunc->uf_arg_types[idx], &argv[idx], idx + 1) == FAIL) + ufunc->uf_arg_types[idx], &argv[idx], + NULL, idx + 1) == FAIL) goto failed_early; copy_tv(&argv[idx], STACK_TV_BOT(0)); } @@ -4567,7 +4571,7 @@ call_def_function( for (idx = 0; idx < vararg_count; ++idx) { if (check_typval_arg_type(expected, &li->li_tv, - argc + idx + 1) == FAIL) + NULL, argc + idx + 1) == FAIL) goto failed_early; li = li->li_next; } diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -428,12 +428,16 @@ typval2type_vimvar(typval_T *tv, garray_ } int -check_typval_arg_type(type_T *expected, typval_T *actual_tv, int arg_idx) +check_typval_arg_type( + type_T *expected, + typval_T *actual_tv, + char *func_name, + int arg_idx) { - where_T where; + where_T where = WHERE_INIT; where.wt_index = arg_idx; - where.wt_variable = FALSE; + where.wt_func_name = func_name; return check_typval_type(expected, actual_tv, where); } @@ -465,10 +469,9 @@ type_mismatch(type_T *expected, type_T * void arg_type_mismatch(type_T *expected, type_T *actual, int arg_idx) { - where_T where; + where_T where = WHERE_INIT; where.wt_index = arg_idx; - where.wt_variable = FALSE; type_mismatch_where(expected, actual, where); } @@ -481,14 +484,23 @@ type_mismatch_where(type_T *expected, ty 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), + if (where.wt_func_name == NULL) + 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(_(where.wt_variable + ? e_variable_nr_type_mismatch_expected_str_but_got_str_in_str + : e_argument_nr_type_mismatch_expected_str_but_got_str_in_str), + where.wt_index, typename1, typename2, where.wt_func_name); } - else + else if (where.wt_func_name == NULL) semsg(_(e_type_mismatch_expected_str_but_got_str), typename1, typename2); + else + semsg(_(e_type_mismatch_expected_str_but_got_str_in_str), + typename1, typename2, where.wt_func_name); vim_free(tofree1); vim_free(tofree2); } @@ -604,7 +616,7 @@ check_argument_types( expected = type->tt_args[type->tt_argcount - 1]->tt_member; else expected = type->tt_args[i]; - if (check_typval_arg_type(expected, &argvars[i], i + 1) == FAIL) + if (check_typval_arg_type(expected, &argvars[i], NULL, i + 1) == FAIL) return FAIL; } return OK;