diff options
-rw-r--r-- | runtime/doc/eval.txt | 2 | ||||
-rw-r--r-- | src/nvim/eval.c | 114 | ||||
-rw-r--r-- | src/nvim/eval.h | 3 | ||||
-rw-r--r-- | src/nvim/shada.c | 39 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 34 | ||||
-rw-r--r-- | test/functional/shada/errors_spec.lua | 4 | ||||
-rw-r--r-- | test/functional/shada/variables_spec.lua | 4 |
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+=!') |