aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/typval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval/typval.c')
-rw-r--r--src/nvim/eval/typval.c289
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;