diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/eval/typval.c | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-usermarks.tar.gz rneovim-usermarks.tar.bz2 rneovim-usermarks.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/eval/typval.c')
-rw-r--r-- | src/nvim/eval/typval.c | 289 |
1 files changed, 188 insertions, 101 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8822bb0491..05b4737206 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -28,9 +28,9 @@ #include "nvim/lua/executor.h" #include "nvim/macros.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/os/fileio.h" #include "nvim/os/input.h" #include "nvim/pos.h" #include "nvim/types.h" @@ -40,6 +40,15 @@ # include "eval/typval.c.generated.h" #endif +static char e_string_required_for_argument_nr[] + = N_("E1174: String required for argument %d"); +static char e_non_empty_string_required_for_argument_nr[] + = N_("E1175: Non-empty string required for argument %d"); +static char e_number_required_for_argument_nr[] + = N_("E1210: Number required for argument %d"); +static char e_string_or_list_required_for_argument_nr[] + = N_("E1222: String or List required for argument %d"); + bool tv_in_free_unref_items = false; // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -317,10 +326,12 @@ void tv_list_free_list(list_T *const l) void tv_list_free(list_T *const l) FUNC_ATTR_NONNULL_ALL { - if (!tv_in_free_unref_items) { - tv_list_free_contents(l); - tv_list_free_list(l); + if (tv_in_free_unref_items) { + return; } + + tv_list_free_contents(l); + tv_list_free_list(l); } /// Unreference a list @@ -831,7 +842,7 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) } /// "join()" function -void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_join(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_LIST) { emsg(_(e_listreq)); @@ -855,7 +866,7 @@ void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "list2str()" function -void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { garray_T ga; @@ -889,8 +900,8 @@ void tv_list_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) list_T *l; bool error = false; - if (var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { + if (value_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { return; } @@ -1016,7 +1027,7 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) if (sortinfo->item_compare_lc) { res = strcoll(p1, p2); } else { - res = sortinfo->item_compare_ic ? STRICMP(p1, p2): STRCMP(p1, p2); + res = sortinfo->item_compare_ic ? STRICMP(p1, p2): strcmp(p1, p2); } } else { double n1, n2; @@ -1079,15 +1090,17 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this funcexe_T funcexe = FUNCEXE_INIT; - funcexe.evaluate = true; - funcexe.partial = partial; - funcexe.selfdict = sortinfo->item_compare_selfdict; + funcexe.fe_evaluate = true; + funcexe.fe_partial = partial; + funcexe.fe_selfdict = sortinfo->item_compare_selfdict; res = call_func(func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); if (res == FAIL) { + // XXX: ITEM_COMPARE_FAIL is unused res = ITEM_COMPARE_FAIL; + sortinfo->item_compare_func_err = true; } else { res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); if (res > 0) { @@ -1143,7 +1156,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) semsg(_(e_listarg), sort ? "sort()" : "uniq()"); } else { list_T *const l = argvars[0].vval.v_list; - if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + if (value_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { goto theend; } tv_list_set_ret(rettv, l); @@ -1242,20 +1255,18 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) item_compare_func_ptr = item_compare_keeping_zero; } - int idx = 0; for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) ; li != NULL;) { listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); if (item_compare_func_ptr(&prev_li, &li) == 0) { - if (info.item_compare_func_err) { // -V547 - emsg(_("E882: Uniq compare function failed")); - break; - } li = tv_list_item_remove(l, li); } else { - idx++; li = TV_LIST_ITEM_NEXT(l, li); } + if (info.item_compare_func_err) { + emsg(_("E882: Uniq compare function failed")); + break; + } } } @@ -1267,13 +1278,13 @@ theend: } /// "sort"({list})" function -void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { do_sort_uniq(argvars, rettv, true); } /// "uniq({list})" function -void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_uniq(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { do_sort_uniq(argvars, rettv, false); } @@ -1356,7 +1367,7 @@ void tv_list_reverse(list_T *const l) /// true list will not be modified. Must be initialized to false /// by the caller. void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, - const ListSorter item_compare_func, bool *errp) + const ListSorter item_compare_func, const bool *errp) FUNC_ATTR_NONNULL_ARG(3, 4) { const int len = tv_list_len(l); @@ -1568,7 +1579,7 @@ bool tv_callback_equal(const Callback *cb1, const Callback *cb2) } switch (cb1->type) { case kCallbackFuncref: - return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; + return strcmp(cb1->data.funcref, cb2->data.funcref) == 0; case kCallbackPartial: // FIXME: this is inconsistent with tv_equal but is needed for precision // maybe change dictwatcheradd to return a watcher id instead? @@ -1588,7 +1599,7 @@ void callback_free(Callback *callback) { switch (callback->type) { case kCallbackFuncref: - func_unref((char_u *)callback->data.funcref); + func_unref(callback->data.funcref); xfree(callback->data.funcref); break; case kCallbackPartial: @@ -1617,7 +1628,7 @@ void callback_put(Callback *cb, typval_T *tv) case kCallbackFuncref: tv->v_type = VAR_FUNC; tv->vval.v_string = xstrdup(cb->data.funcref); - func_ref((char_u *)cb->data.funcref); + func_ref(cb->data.funcref); break; case kCallbackLua: // TODO(tjdevries): Unified Callback. @@ -1643,7 +1654,7 @@ void callback_copy(Callback *dest, Callback *src) break; case kCallbackFuncref: dest->data.funcref = xstrdup(src->data.funcref); - func_ref((char_u *)src->data.funcref); + func_ref(src->data.funcref); break; case kCallbackLua: dest->data.luaref = api_new_luaref(src->data.luaref); @@ -1740,9 +1751,8 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) const size_t len = watcher->key_pattern_len; if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; - } else { - return strcmp(key, watcher->key_pattern) == 0; } + return strcmp(key, watcher->key_pattern) == 0; } /// Send a change notification to all dictionary watchers that match given key @@ -1774,7 +1784,7 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, typval_T tv_dict_add(argv[2].vval.v_dict, v); } - if (oldtv) { + if (oldtv && oldtv->v_type != VAR_UNKNOWN) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); tv_copy(oldtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); @@ -1834,6 +1844,7 @@ dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) di->di_key[key_len] = NUL; di->di_flags = DI_FLAGS_ALLOC; di->di_tv.v_lock = VAR_UNLOCKED; + di->di_tv.v_type = VAR_UNKNOWN; return di; } @@ -2053,9 +2064,24 @@ int tv_dict_get_tv(dict_T *d, const char *const key, typval_T *rettv) varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + return tv_dict_get_number_def(d, key, 0); +} + +/// Get a number item from a dictionary. +/// +/// Returns "def" if the entry doesn't exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// @param[in] def Default value. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number_def(const dict_T *const d, const char *const key, const int def) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { - return 0; + return def; } return tv_get_number(&di->di_tv); } @@ -2074,7 +2100,7 @@ char **tv_dict_to_env(dict_T *denv) TV_DICT_ITER(denv, var, { const char *str = tv_get_string(&var->di_tv); assert(str); - size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; + size_t len = strlen((char *)var->di_key) + strlen(str) + strlen("=") + 1; env[i] = xmalloc(len); snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); i++; @@ -2183,6 +2209,15 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_ return res; } +/// Check for adding a function to g: or l:. +/// If the name is wrong give an error message and return true. +int tv_dict_wrong_func_name(dict_T *d, typval_T *tv, const char *name) +{ + return (d == &globvardict || &d->dv_hashtab == get_funccal_local_ht()) + && tv_is_func(*tv) + && var_wrong_func_name(name, true); +} + //{{{2 dict_add* /// Add item to dictionary @@ -2194,7 +2229,10 @@ bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_ int tv_dict_add(dict_T *const d, dictitem_T *const item) FUNC_ATTR_NONNULL_ALL { - return hash_add(&d->dv_hashtab, item->di_key); + if (tv_dict_wrong_func_name(d, &item->di_tv, (const char *)item->di_key)) { + return FAIL; + } + return hash_add(&d->dv_hashtab, (char *)item->di_key); } /// Add a list entry to dictionary @@ -2410,10 +2448,10 @@ void tv_dict_clear(dict_T *const d) /// /// @param d1 Dictionary to extend. /// @param[in] d2 Dictionary to extend with. -/// @param[in] action "error", "force", "keep": -/// +/// @param[in] action "error", "force", "move", "keep": /// e*, including "error": duplicate key gives an error. /// f*, including "force": duplicate d2 keys override d1. +/// m*, including "move": move items instead of copying. /// other, including "keep": duplicate d2 keys ignored. void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action) FUNC_ATTR_NONNULL_ALL @@ -2422,27 +2460,33 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action const char *const arg_errmsg = _("extend() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); - TV_DICT_ITER(d2, di2, { + if (*action == 'm') { + hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() + } + + HASHTAB_ITER(&d2->dv_hashtab, hi2, { + dictitem_T *const di2 = TV_DICT_HI2DI(hi2); dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); - if (d1->dv_scope != VAR_NO_SCOPE) { - // Disallow replacing a builtin function in l: and g:. - // Check the key to be valid when adding to any scope. - if (d1->dv_scope == VAR_DEF_SCOPE - && tv_is_func(di2->di_tv) - && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { - break; - } - if (!valid_varname((const char *)di2->di_key)) { - break; - } + // Check the key to be valid when adding to any scope. + if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) { + break; } if (di1 == NULL) { - dictitem_T *const new_di = tv_dict_item_copy(di2); - if (tv_dict_add(d1, new_di) == FAIL) { - tv_dict_item_free(new_di); - } else if (watched) { - tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, - NULL); + if (*action == 'm') { + // Cheap way to move a dict item from "d2" to "d1". + // If dict_add() fails then "d2" won't be empty. + dictitem_T *const new_di = di2; + if (tv_dict_add(d1, new_di) == OK) { + hash_remove(&d2->dv_hashtab, hi2); + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL); + } + } else { + dictitem_T *const new_di = tv_dict_item_copy(di2); + if (tv_dict_add(d1, new_di) == FAIL) { + tv_dict_item_free(new_di); + } else if (watched) { + tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL); + } } } else if (*action == 'e') { semsg(_("E737: Key already exists: %s"), di2->di_key); @@ -2450,10 +2494,14 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action } else if (*action == 'f' && di2 != di1) { typval_T oldtv; - if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { break; } + // Disallow replacing a builtin function. + if (tv_dict_wrong_func_name(d1, &di2->di_tv, (const char *)di2->di_key)) { + break; + } if (watched) { tv_copy(&di1->di_tv, &oldtv); @@ -2469,6 +2517,10 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action } } }); + + if (*action == 'm') { + hash_unlock(&d2->dv_hashtab); + } } /// Compare two dictionaries @@ -2483,10 +2535,14 @@ bool tv_dict_equal(dict_T *const d1, dict_T *const d2, const bool ic, const bool if (d1 == d2) { return true; } - if (d1 == NULL || d2 == NULL) { + if (tv_dict_len(d1) != tv_dict_len(d2)) { return false; } - if (tv_dict_len(d1) != tv_dict_len(d2)) { + if (tv_dict_len(d1) == 0) { + // empty and NULL dicts are considered equal + return true; + } + if (d1 == NULL || d2 == NULL) { return false; } @@ -2532,8 +2588,8 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, dict_T *const orig, const bool if (conv == NULL || conv->vc_type == CONV_NONE) { new_di = tv_dict_item_alloc((const char *)di->di_key); } else { - size_t len = STRLEN(di->di_key); - char *const key = (char *)string_convert(conv, di->di_key, &len); + size_t len = strlen((char *)di->di_key); + char *const key = (char *)string_convert(conv, (char *)di->di_key, &len); if (key == NULL) { new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); } else { @@ -2654,7 +2710,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) { blob_T *const b = argvars[0].vval.v_blob; - if (b != NULL && var_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { + if (b != NULL && value_check_lock(b->bv_lock, arg_errmsg, TV_TRANSLATE)) { return; } @@ -2674,7 +2730,7 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) } if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - char_u *const p = (char_u *)b->bv_ga.ga_data; + uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; rettv->vval.v_number = (varnumber_T)(*(p + idx)); memmove(p + idx, p + idx + 1, (size_t)(len - idx - 1)); b->bv_ga.ga_len--; @@ -2696,9 +2752,8 @@ void tv_blob_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) blob->bv_ga.ga_len = (int)(end - idx + 1); ga_grow(&blob->bv_ga, (int)(end - idx + 1)); - char_u *const p = (char_u *)b->bv_ga.ga_data; - memmove((char_u *)blob->bv_ga.ga_data, p + idx, - (size_t)(end - idx + 1)); + uint8_t *const p = (uint8_t *)b->bv_ga.ga_data; + memmove(blob->bv_ga.ga_data, p + idx, (size_t)(end - idx + 1)); tv_blob_set_ret(rettv, blob); if (len - end - 1 > 0) { @@ -2777,7 +2832,7 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi switch (what) { case kDictListKeys: tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = (char *)vim_strsave(di->di_key); + tv_item.vval.v_string = xstrdup((char *)di->di_key); break; case kDictListValues: tv_copy(&di->di_tv, &tv_item); @@ -2806,25 +2861,25 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi } /// "items(dict)" function -void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 2); } /// "keys()" function -void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 0); } /// "values(dict)" function -void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { tv_dict_list(argvars, rettv, 1); } /// "has_key()" function -void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { if (argvars[0].v_type != VAR_DICT) { emsg(_(e_dictreq)); @@ -2846,7 +2901,7 @@ void tv_dict_remove(typval_T *argvars, typval_T *rettv, const char *arg_errmsg) if (argvars[2].v_type != VAR_UNKNOWN) { semsg(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + && !value_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { dictitem_T *di = tv_dict_find(d, key, -1); @@ -2951,7 +3006,7 @@ void tv_blob_copy(typval_T *const from, typval_T *const to) (tv)->v_lock = VAR_UNLOCKED; \ } while (0) -static inline int _nothing_conv_func_start(typval_T *const tv, char_u *const fun) +static inline int _nothing_conv_func_start(typval_T *const tv, char *const fun) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) { tv->v_lock = VAR_UNLOCKED; @@ -3114,6 +3169,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, dict_T **const dic #define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const #define TYPVAL_ENCODE_FIRST_ARG_NAME ignored #include "nvim/eval/typval_encode.c.h" + #undef TYPVAL_ENCODE_SCOPE #undef TYPVAL_ENCODE_NAME #undef TYPVAL_ENCODE_FIRST_ARG_TYPE @@ -3178,7 +3234,7 @@ void tv_free(typval_T *tv) partial_unref(tv->vval.v_partial); break; case VAR_FUNC: - func_unref((char_u *)tv->vval.v_string); + func_unref(tv->vval.v_string); FALLTHROUGH; case VAR_STRING: xfree(tv->vval.v_string); @@ -3231,7 +3287,7 @@ void tv_copy(const typval_T *const from, typval_T *const to) if (from->vval.v_string != NULL) { to->vval.v_string = xstrdup(from->vval.v_string); if (from->v_type == VAR_FUNC) { - func_ref((char_u *)to->vval.v_string); + func_ref(to->vval.v_string); } } break; @@ -3403,12 +3459,12 @@ bool tv_check_lock(const typval_T *tv, const char *name, size_t name_len) default: break; } - return var_check_lock(tv->v_lock, name, name_len) - || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len)); + return value_check_lock(tv->v_lock, name, name_len) + || (lock != VAR_UNLOCKED && value_check_lock(lock, name, name_len)); } -/// @return true if variable "name" is locked (immutable) -bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len) +/// @return true if variable "name" has a locked (immutable) value +bool value_check_lock(VarLockStatus lock, const char *name, size_t name_len) { const char *error_message = NULL; switch (lock) { @@ -3583,13 +3639,13 @@ bool tv_check_str_or_nr(const typval_T *const tv) #define FUNC_ERROR "E703: Using a Funcref as a Number" static const char *const num_errors[] = { - [VAR_PARTIAL]=N_(FUNC_ERROR), - [VAR_FUNC]=N_(FUNC_ERROR), - [VAR_LIST]=N_("E745: Using a List as a Number"), - [VAR_DICT]=N_("E728: Using a Dictionary as a Number"), - [VAR_FLOAT]=N_("E805: Using a Float as a Number"), - [VAR_BLOB]=N_("E974: Using a Blob as a Number"), - [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number"), + [VAR_PARTIAL]= N_(FUNC_ERROR), + [VAR_FUNC]= N_(FUNC_ERROR), + [VAR_LIST]= N_("E745: Using a List as a Number"), + [VAR_DICT]= N_("E728: Using a Dictionary as a Number"), + [VAR_FLOAT]= N_("E805: Using a Float as a Number"), + [VAR_BLOB]= N_("E974: Using a Blob as a Number"), + [VAR_UNKNOWN]= N_("E685: using an invalid value as a Number"), }; #undef FUNC_ERROR @@ -3628,13 +3684,13 @@ bool tv_check_num(const typval_T *const tv) #define FUNC_ERROR "E729: using Funcref as a String" static const char *const str_errors[] = { - [VAR_PARTIAL]=N_(FUNC_ERROR), - [VAR_FUNC]=N_(FUNC_ERROR), - [VAR_LIST]=N_("E730: using List as a String"), - [VAR_DICT]=N_("E731: using Dictionary as a String"), - [VAR_FLOAT]=((const char *)e_float_as_string), - [VAR_BLOB]=N_("E976: using Blob as a String"), - [VAR_UNKNOWN]=N_("E908: using an invalid value as a String"), + [VAR_PARTIAL]= N_(FUNC_ERROR), + [VAR_FUNC]= N_(FUNC_ERROR), + [VAR_LIST]= N_("E730: using List as a String"), + [VAR_DICT]= N_("E731: using Dictionary as a String"), + [VAR_FLOAT]= ((const char *)e_float_as_string), + [VAR_BLOB]= N_("E976: using Blob as a String"), + [VAR_UNKNOWN]= N_("E908: using an invalid value as a String"), }; #undef FUNC_ERROR @@ -3717,8 +3773,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_STRING: { varnumber_T n = 0; if (tv->vval.v_string != NULL) { - vim_str2nr((char_u *)tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, - false); + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0, false); } return n; } @@ -3746,9 +3801,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) linenr_T tv_get_lnum(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + const int did_emsg_before = did_emsg; linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL); - if (lnum == 0) { // No valid number, try using same function as line() does. + if (lnum <= 0 && did_emsg_before == did_emsg && tv->v_type != VAR_NUMBER) { int fnum; + // No valid number, try using same function as line() does. pos_T *const fp = var2fpos(tv, true, &fnum, false); if (fp != NULL) { lnum = fp->lnum; @@ -3801,26 +3858,56 @@ float_T tv_get_float(const typval_T *const tv) return 0; } -// Give an error and return FAIL unless "tv" is a string. -int tv_check_for_string(const typval_T *const tv) +/// Give an error and return FAIL unless "args[idx]" is a string. +int tv_check_for_string_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_STRING) { + semsg(_(e_string_required_for_argument_nr), idx + 1); + return FAIL; + } + return OK; +} + +/// Give an error and return FAIL unless "args[idx]" is a non-empty string. +int tv_check_for_nonempty_string_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { - if (tv->v_type != VAR_STRING) { - emsg(_(e_stringreq)); + if (tv_check_for_string_arg(args, idx) == FAIL) { + return FAIL; + } + if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL) { + semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1); return FAIL; } return OK; } -// Give an error and return FAIL unless "tv" is a non-empty string. -int tv_check_for_nonempty_string(const typval_T *const tv) +/// Give an error and return FAIL unless "args[idx]" is a number. +int tv_check_for_number_arg(const typval_T *const args, const int idx) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { - if (tv_check_for_string(tv) == FAIL) { + if (args[idx].v_type != VAR_NUMBER) { + semsg(_(e_number_required_for_argument_nr), idx + 1); return FAIL; } - if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL) { - emsg(_(e_non_empty_string_required)); + return OK; +} + +/// Check for an optional number argument at "idx" +int tv_check_for_opt_number_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + return (args[idx].v_type == VAR_UNKNOWN + || tv_check_for_number_arg(args, idx) != FAIL) ? OK : FAIL; +} + +/// Give an error and return FAIL unless "args[idx]" is a string or a list. +int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST) { + semsg(_(e_string_or_list_required_for_argument_nr), idx + 1); return FAIL; } return OK; |