aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt2
-rw-r--r--src/nvim/eval.c114
-rw-r--r--src/nvim/eval.h3
-rw-r--r--src/nvim/shada.c39
-rw-r--r--test/functional/eval/msgpack_functions_spec.lua34
-rw-r--r--test/functional/shada/errors_spec.lua4
-rw-r--r--test/functional/shada/variables_spec.lua4
7 files changed, 153 insertions, 47 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 564ee10a70..45095d45a9 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -4712,7 +4712,7 @@ msgpackdump({list}) {Nvim} *msgpackdump()*
(dictionary with zero items is represented by 0x80 byte in
messagepack).
- Limitations:
+ Limitations: *E951* *E952*
1. |Funcref|s cannot be dumped.
2. Containers that reference themselves cannot be dumped.
3. Dictionary keys are always dumped as STR strings.
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index b60886704e..57d7002739 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -6479,7 +6479,8 @@ failret:
static int name##_convert_one_value(firstargtype firstargname, \
MPConvStack *const mpstack, \
typval_T *const tv, \
- const int copyID) \
+ const int copyID, \
+ const char *const objname) \
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT \
{ \
switch (tv->v_type) { \
@@ -6713,14 +6714,16 @@ name##_convert_one_value_regular_dict: \
return OK; \
} \
\
-scope int vim_to_##name(firstargtype firstargname, typval_T *const tv) \
+scope int vim_to_##name(firstargtype firstargname, typval_T *const tv, \
+ const char *const objname) \
FUNC_ATTR_WARN_UNUSED_RESULT \
{ \
current_copyID += COPYID_INC; \
const int copyID = current_copyID; \
MPConvStack mpstack; \
kv_init(mpstack); \
- if (name##_convert_one_value(firstargname, &mpstack, tv, copyID) == FAIL) { \
+ if (name##_convert_one_value(firstargname, &mpstack, tv, copyID, objname) \
+ == FAIL) { \
goto vim_to_msgpack_error_ret; \
} \
while (kv_size(mpstack)) { \
@@ -6769,8 +6772,8 @@ scope int vim_to_##name(firstargtype firstargname, typval_T *const tv) \
} \
const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; \
if (name##_convert_one_value(firstargname, &mpstack, \
- &kv_pair->lv_first->li_tv, copyID) \
- == FAIL) { \
+ &kv_pair->lv_first->li_tv, copyID, \
+ objname) == FAIL) { \
goto vim_to_msgpack_error_ret; \
} \
cur_tv = &kv_pair->lv_last->li_tv; \
@@ -6778,8 +6781,8 @@ scope int vim_to_##name(firstargtype firstargname, typval_T *const tv) \
break; \
} \
} \
- if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID) \
- == FAIL) { \
+ if (name##_convert_one_value(firstargname, &mpstack, cur_tv, copyID, \
+ objname) == FAIL) { \
goto vim_to_msgpack_error_ret; \
} \
} \
@@ -6975,7 +6978,7 @@ static char *tv2string(typval_T *tv, size_t *len)
{
garray_T ga;
ga_init(&ga, (int)sizeof(char), 80);
- vim_to_string(&ga, tv);
+ vim_to_string(&ga, tv, "tv2string() argument");
did_echo_string_emsg = false;
if (len != NULL) {
*len = (size_t) ga.ga_len;
@@ -7001,7 +7004,7 @@ static char *echo_string(typval_T *tv, size_t *len)
ga_concat(&ga, tv->vval.v_string);
}
} else {
- vim_to_echo(&ga, tv);
+ vim_to_echo(&ga, tv, ":echo argument");
did_echo_string_emsg = false;
}
if (len != NULL) {
@@ -12686,6 +12689,77 @@ static inline bool vim_list_to_buf(const list_T *const list,
return true;
}
+/// Show a error message when converting to msgpack value
+///
+/// @param[in] msg Error message to dump. Must contain exactly two %s that
+/// will be replaced with what was being dumped: first with
+/// something like “F” or “function argument”, second with path
+/// to the failed value.
+/// @param[in] mpstack Path to the failed value.
+/// @param[in] objname Dumped object name.
+///
+/// @return FAIL.
+static int conv_error(const char *const msg, const MPConvStack *const mpstack,
+ const char *const objname)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T msg_ga;
+ ga_init(&msg_ga, (int)sizeof(char), 80);
+ char *const key_msg = _("key %s");
+ char *const key_pair_msg = _("key %s at index %i from special map");
+ char *const idx_msg = _("index %i");
+ for (size_t i = 0; i < kv_size(*mpstack); i++) {
+ if (i != 0) {
+ ga_concat(&msg_ga, (char_u *) ", ");
+ }
+ MPConvStackVal v = kv_A(*mpstack, i);
+ switch (v.type) {
+ case kMPConvDict: {
+ typval_T key_tv = {
+ .v_type = VAR_STRING,
+ .vval = { .v_string = (v.data.d.hi == NULL
+ ? v.data.d.dict->dv_hashtab.ht_array
+ : (v.data.d.hi - 1))->hi_key },
+ };
+ char *const key = tv2string(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ break;
+ }
+ case kMPConvPairs:
+ case kMPConvList: {
+ int idx = 0;
+ const listitem_T *li;
+ for (li = v.data.l.list->lv_first;
+ li != NULL && li->li_next != v.data.l.li;
+ li = li->li_next) {
+ idx++;
+ }
+ if (v.type == kMPConvList
+ || li == NULL
+ || (li->li_tv.v_type != VAR_LIST
+ && li->li_tv.vval.v_list->lv_len <= 0)) {
+ vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx);
+ ga_concat(&msg_ga, IObuff);
+ } else {
+ typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv;
+ char *const key = echo_string(&key_tv, NULL);
+ vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
+ xfree(key);
+ ga_concat(&msg_ga, IObuff);
+ }
+ break;
+ }
+ }
+ }
+ EMSG3(msg, objname, (kv_size(*mpstack) == 0
+ ? _("itself")
+ : (char *) msg_ga.ga_data));
+ ga_clear(&msg_ga);
+ return FAIL;
+}
+
#define CONV_STRING(buf, len) \
do { \
if (buf == NULL) { \
@@ -12726,10 +12800,9 @@ static inline bool vim_list_to_buf(const list_T *const list,
msgpack_pack_double(packer, (double) (flt))
#define CONV_FUNC(fun) \
- do { \
- EMSG2(_(e_invarg2), "attempt to dump function reference"); \
- return FAIL; \
- } while (0)
+ return conv_error(_("E951: Error while dumping %s, %s: " \
+ "attempt to dump function reference"), \
+ mpstack, objname)
#define CONV_EMPTY_LIST() \
msgpack_pack_array(packer, 0)
@@ -12772,10 +12845,9 @@ static inline bool vim_list_to_buf(const list_T *const list,
#define CONV_LIST_BETWEEN_ITEMS(lst)
#define CONV_RECURSE(val, conv_type) \
- do { \
- EMSG2(_(e_invarg2), "container references itself"); \
- return FAIL; \
- } while (0)
+ return conv_error(_("E952: Unable to dump %s: " \
+ "container references itself in %s"), \
+ mpstack, objname)
#define CONV_ALLOW_SPECIAL true
@@ -12817,8 +12889,14 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv)
return;
}
msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write);
+ const char *const msg = _("msgpackdump() argument, index %i");
+ // Assume that translation will not take more then 4 times more space
+ char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN];
+ int idx = 0;
for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) {
- if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) {
+ vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx);
+ idx++;
+ if (vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) {
break;
}
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 19a1bbb083..ea8b5bc253 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -118,7 +118,8 @@ enum {
/// Maximum number of function arguments
#define MAX_FUNC_ARGS 20
-int vim_to_msgpack(msgpack_packer *const, typval_T *const);
+int vim_to_msgpack(msgpack_packer *const, typval_T *const,
+ const char *const objname);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval.h.generated.h"
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 6a30472a7c..347bd8664c 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -1684,17 +1684,18 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf);
msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write);
-#define DUMP_ADDITIONAL_ELEMENTS(src) \
+#define DUMP_ADDITIONAL_ELEMENTS(src, what) \
do { \
if ((src) != NULL) { \
for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \
- if (vim_to_msgpack(spacker, &li->li_tv) == FAIL) { \
+ if (vim_to_msgpack(spacker, &li->li_tv, \
+ _("additional elements of ShaDa " what)) == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
} \
} while (0)
-#define DUMP_ADDITIONAL_DATA(src) \
+#define DUMP_ADDITIONAL_DATA(src, what) \
do { \
dict_T *const d = (src); \
if (d != NULL) { \
@@ -1706,7 +1707,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
const size_t key_len = strlen((const char *) hi->hi_key); \
msgpack_pack_str(spacker, key_len); \
msgpack_pack_str_body(spacker, (const char *) hi->hi_key, key_len); \
- if (vim_to_msgpack(spacker, &di->di_tv) == FAIL) { \
+ if (vim_to_msgpack(spacker, &di->di_tv, \
+ _("additional data of ShaDa " what)) == FAIL) { \
goto shada_pack_entry_error; \
} \
} \
@@ -1741,7 +1743,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
if (is_hist_search) {
msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep);
}
- DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements,
+ "history entry item");
break;
}
case kSDItemVariable: {
@@ -1750,14 +1753,20 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
? 0
: entry.data.global_var.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size);
- PACK_BIN(cstr_as_string(entry.data.global_var.name));
- if (vim_to_msgpack(spacker, &entry.data.global_var.value) == FAIL) {
+ const String varname = cstr_as_string(entry.data.global_var.name);
+ PACK_BIN(varname);
+ char vardesc[256] = "variable g:";
+ memcpy(&vardesc[sizeof("variable g:") - 1], varname.data,
+ varname.size + 1);
+ if (vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc)
+ == FAIL) {
ret = kSDWriteIgnError;
EMSG2(_(WERR "Failed to write variable %s"),
entry.data.global_var.name);
goto shada_pack_entry_error;
}
- DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements,
+ "variable item");
break;
}
case kSDItemSubString: {
@@ -1767,7 +1776,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
: entry.data.sub_string.additional_elements->lv_len);
msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.sub_string.sub));
- DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements);
+ DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements,
+ "sub string item");
break;
}
case kSDItemSearchPattern: {
@@ -1814,7 +1824,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_int64(spacker, entry.data.search_pattern.offset);
}
#undef PACK_BOOL
- DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data,
+ "search pattern item");
break;
}
case kSDItemChange:
@@ -1849,7 +1860,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
PACK_STATIC_STR(KEY_NAME_CHAR);
msgpack_pack_uint8(spacker, (uint8_t) entry.data.filemark.name);
}
- DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data,
+ "mark (change, jump, global or local) item");
break;
}
case kSDItemRegister: {
@@ -1877,7 +1889,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
PACK_STATIC_STR(REG_KEY_WIDTH);
msgpack_pack_uint64(spacker, (uint64_t) entry.data.reg.width);
}
- DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item");
break;
}
case kSDItemBufferList: {
@@ -1908,7 +1920,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_uint64(
spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.col);
}
- DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data);
+ DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data,
+ "buffer list subitem");
}
break;
}
diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua
index da42fc9d26..41b0faf76c 100644
--- a/test/functional/eval/msgpack_functions_spec.lua
+++ b/test/functional/eval/msgpack_functions_spec.lua
@@ -541,27 +541,27 @@ describe('msgpackdump() function', function()
it('fails to dump a function reference', function()
execute('let Todump = function("tr")')
- eq('Vim(call):E475: Invalid argument: attempt to dump function reference',
+ eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, itself: attempt to dump function reference',
exc_exec('call msgpackdump([Todump])'))
end)
it('fails to dump a function reference in a list', function()
execute('let todump = [function("tr")]')
- eq('Vim(call):E475: Invalid argument: attempt to dump function reference',
+ eq('Vim(call):E951: Error while dumping msgpackdump() argument, index 0, index 0: attempt to dump function reference',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive list', function()
execute('let todump = [[[]]]')
execute('call add(todump[0][0], todump)')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 0, index 0',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive dict', function()
execute('let todump = {"d": {"d": {}}}')
execute('call extend(todump.d.d, {"d": todump})')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key \'d\', key \'d\', key \'d\'',
exc_exec('call msgpackdump([todump])'))
end)
@@ -580,21 +580,35 @@ describe('msgpackdump() function', function()
it('fails to dump a recursive list in a special dict', function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, todump)')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (key) map in a special dict', function()
- execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
+ execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [todump, 0])')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 1',
exc_exec('call msgpackdump([todump])'))
end)
it('fails to dump a recursive (val) map in a special dict', function()
- execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
+ execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": []}')
execute('call add(todump._VAL, [0, todump])')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key 0 at index 0 from special map',
+ exc_exec('call msgpackdump([todump])'))
+ end)
+
+ it('fails to dump a recursive (key) map in a special dict, _VAL reference', function()
+ execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
+ execute('call add(todump._VAL[0][0], todump._VAL)')
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [[[[...@0], []]]] at index 0 from special map, index 0',
+ exc_exec('call msgpackdump([todump])'))
+ end)
+
+ it('fails to dump a recursive (val) map in a special dict, _VAL reference', function()
+ execute('let todump = {"_TYPE": v:msgpack_types.map, "_VAL": [[[], []]]}')
+ execute('call add(todump._VAL[0][1], todump._VAL)')
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in key [] at index 0 from special map, index 0',
exc_exec('call msgpackdump([todump])'))
end)
@@ -602,7 +616,7 @@ describe('msgpackdump() function', function()
function()
execute('let todump = {"_TYPE": v:msgpack_types.array, "_VAL": []}')
execute('call add(todump._VAL, [0, todump._VAL])')
- eq('Vim(call):E475: Invalid argument: container references itself',
+ eq('Vim(call):E952: Unable to dump msgpackdump() argument, index 0: container references itself in index 0, index 1',
exc_exec('call msgpackdump([todump])'))
end)
diff --git a/test/functional/shada/errors_spec.lua b/test/functional/shada/errors_spec.lua
index f6265bf24b..e7951ee74c 100644
--- a/test/functional/shada/errors_spec.lua
+++ b/test/functional/shada/errors_spec.lua
@@ -497,7 +497,7 @@ $
it('errors when a funcref is stored in a variable', function()
nvim_command('let F = function("tr")')
nvim_command('set shada+=!')
- eq('\nE475: Invalid argument: attempt to dump function reference'
+ eq('\nE951: Error while dumping variable g:F, itself: attempt to dump function reference'
.. '\nE574: Failed to write variable F',
redir_exec('wshada'))
end)
@@ -506,7 +506,7 @@ $
nvim_command('let L = []')
nvim_command('call add(L, L)')
nvim_command('set shada+=!')
- eq('\nE475: Invalid argument: container references itself'
+ eq('\nE952: Unable to dump variable g:L: container references itself in index 0'
.. '\nE574: Failed to write variable L',
redir_exec('wshada'))
end)
diff --git a/test/functional/shada/variables_spec.lua b/test/functional/shada/variables_spec.lua
index 22054db353..3becf1bc32 100644
--- a/test/functional/shada/variables_spec.lua
+++ b/test/functional/shada/variables_spec.lua
@@ -143,7 +143,7 @@ describe('ShaDa support code', function()
meths.set_var('U', '10')
nvim_command('set shada+=!')
set_additional_cmd('set shada+=!')
- eq('Vim(wshada):E475: Invalid argument: attempt to dump function reference',
+ eq('Vim(wshada):E951: Error while dumping variable g:F, itself: attempt to dump function reference',
exc_exec('wshada'))
meths.set_option('shada', '')
reset()
@@ -156,7 +156,7 @@ describe('ShaDa support code', function()
nvim_command('call add(L, L)')
meths.set_var('U', '10')
nvim_command('set shada+=!')
- eq('Vim(wshada):E475: Invalid argument: container references itself',
+ eq('Vim(wshada):E952: Unable to dump variable g:L: container references itself in index 0',
exc_exec('wshada'))
meths.set_option('shada', '')
set_additional_cmd('set shada+=!')