From fb146e80aa1ead96518f38b9684e39249bc83485 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 26 Jul 2016 23:16:23 +0300 Subject: eval: Split eval.c into smaller files --- src/clint.py | 7 +- src/nvim/api/private/helpers.c | 17 +- src/nvim/api/vim.c | 5 +- src/nvim/buffer.c | 2 +- src/nvim/buffer_defs.h | 45 +- src/nvim/charset.c | 26 +- src/nvim/cursor.c | 15 +- src/nvim/edit.c | 13 +- src/nvim/eval.c | 2961 +++++++++++++------------------------- src/nvim/eval/decode.c | 98 +- src/nvim/eval/decode.h | 2 +- src/nvim/eval/encode.c | 9 +- src/nvim/eval/executor.c | 114 ++ src/nvim/eval/executor.h | 9 + src/nvim/eval/gc.c | 11 + src/nvim/eval/gc.h | 12 + src/nvim/eval/typval.c | 1171 +++++++++++++++ src/nvim/eval/typval.h | 285 ++++ src/nvim/eval/typval_encode.c.h | 2 +- src/nvim/eval/typval_encode.h | 2 +- src/nvim/eval_defs.h | 286 ---- src/nvim/ex_cmds.c | 2 +- src/nvim/ex_cmds.h | 2 +- src/nvim/ex_cmds2.c | 24 +- src/nvim/ex_docmd.c | 4 +- src/nvim/ex_eval.c | 14 +- src/nvim/ex_getln.c | 6 +- src/nvim/ex_getln.h | 2 +- src/nvim/globals.h | 16 +- src/nvim/main.c | 7 +- src/nvim/mark.c | 23 + src/nvim/mark_defs.h | 2 +- src/nvim/mbyte.c | 41 +- src/nvim/mbyte.h | 23 + src/nvim/memory.c | 13 + src/nvim/normal.c | 14 +- src/nvim/ops.c | 73 +- src/nvim/ops.h | 2 +- src/nvim/quickfix.c | 2 +- src/nvim/regexp.c | 51 +- src/nvim/regexp_nfa.c | 4 +- src/nvim/shada.c | 36 +- src/nvim/spell.c | 2 +- src/nvim/strings.c | 2 +- src/nvim/strings.h | 2 +- src/nvim/tag.c | 8 +- src/nvim/undo.c | 4 +- src/nvim/window.c | 15 +- test/unit/eval/decode_spec.lua | 2 +- test/unit/eval/helpers.lua | 16 +- test/unit/eval/tricks_spec.lua | 2 +- test/unit/eval/tv_clear_spec.lua | 12 +- 52 files changed, 2977 insertions(+), 2541 deletions(-) create mode 100644 src/nvim/eval/executor.c create mode 100644 src/nvim/eval/executor.h create mode 100644 src/nvim/eval/gc.c create mode 100644 src/nvim/eval/gc.h create mode 100644 src/nvim/eval/typval.c create mode 100644 src/nvim/eval/typval.h delete mode 100644 src/nvim/eval_defs.h diff --git a/src/clint.py b/src/clint.py index ce31822ada..61c53d128e 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2268,11 +2268,14 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # //!< Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment + # or they are Vim {{{ fold markers match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^!< ', line[commentend:]) or Search(r'^/< ', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) + Search(r'^/+ ', line[commentend:]) or + Search(r'^(?:\{{3}|\}{3})\d*(?: |$)', + line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -3575,7 +3578,7 @@ def main(): if __name__ == '__main__': main() -# vim: ts=4 sts=4 sw=4 +# vim: ts=4 sts=4 sw=4 foldmarker=▶,▲ # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 7efa086af2..ff45cad8f5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -14,6 +14,7 @@ #include "nvim/window.h" #include "nvim/memory.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/map_defs.h" #include "nvim/map.h" #include "nvim/option.h" @@ -87,7 +88,7 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (uint8_t *) key.data); + hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); if (HASHITEM_EMPTY(hi)) { api_set_error(err, Validation, _("Key not found")); @@ -177,13 +178,13 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (retval) { rv = vim_to_object(&di->di_tv); } - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); } // Update the value copy_tv(&tv, &di->di_tv); // Clear the temporary variable - clear_tv(&tv); + tv_clear(&tv); } return rv; @@ -682,20 +683,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; case kObjectTypeArray: { - list_T *list = list_alloc(); + list_T *const list = tv_list_alloc(); for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); if (!object_to_vim(item, &li->li_tv, err)) { // cleanup - listitem_free(li); - list_free(list); + tv_list_item_free(li); + tv_list_free(list); return false; } - list_append(list, li); + tv_list_append(list, li); } list->lv_refcount++; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 413456c615..59c0200395 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -22,6 +22,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/option.h" #include "nvim/syntax.h" #include "nvim/getchar.h" @@ -237,11 +238,11 @@ Object nvim_call_function(String fname, Array args, Error *err) if (!try_end(err)) { rv = vim_to_object(&rettv); } - clear_tv(&rettv); + tv_clear(&rettv); free_vim_args: while (i > 0) { - clear_tv(&vim_args[--i]); + tv_clear(&vim_args[--i]); } return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4a07884f98..f7333fead4 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -1472,7 +1472,7 @@ static inline void buf_init_changedtick(buf_T *const buf) { STATIC_ASSERT(sizeof("changedtick") <= sizeof(buf->changedtick_di.di_key), "buf->changedtick_di cannot hold large enough keys"); - buf->changedtick_di = (dictitem16_T) { + buf->changedtick_di = (ChangedtickDictItem) { .di_flags = DI_FLAGS_RO|DI_FLAGS_FIX, // Must not include DI_FLAGS_ALLOC. .di_tv = (typval_T) { .v_type = VAR_NUMBER, diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 9d350c763e..20a2b931bd 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -21,8 +21,6 @@ typedef struct { #include "nvim/pos.h" // for the number window-local and buffer-local options #include "nvim/option_defs.h" -// for optional iconv support -#include "nvim/iconv.h" // for jump list and tag stack sizes in a buffer and mark types #include "nvim/mark_defs.h" // for u_header_T; needs buf_T. @@ -30,7 +28,9 @@ typedef struct { // for hashtab_T #include "nvim/hashtab.h" // for dict_T -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" +// for proftime_T +#include "nvim/profile.h" // for String #include "nvim/api/private/defs.h" // for Map(K, V) @@ -318,25 +318,6 @@ typedef struct { String save_inputbuf; } tasave_T; -/* - * Used for conversion of terminal I/O and script files. - */ -typedef struct { - int vc_type; /* zero or one of the CONV_ values */ - int vc_factor; /* max. expansion factor */ -# ifdef USE_ICONV - iconv_t vc_fd; /* for CONV_ICONV */ -# endif - bool vc_fail; /* fail for invalid char, don't use '?' */ -} vimconv_T; - -#define CONV_NONE 0 -#define CONV_TO_UTF8 1 -#define CONV_9_TO_UTF8 2 -#define CONV_TO_LATIN1 3 -#define CONV_TO_LATIN9 4 -#define CONV_ICONV 5 - /* * Structure used for mappings and abbreviations. */ @@ -447,6 +428,10 @@ typedef struct { char_u *b_syn_isk; // iskeyword option } synblock_T; +/// Type used for changedtick_di member in buf_T +/// +/// Primary exists so that literals of relevant type can be made. +typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem; #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 @@ -491,7 +476,7 @@ struct file_buffer { // file has been changed and not written out. /// Change identifier incremented for each change, including undo #define b_changedtick changedtick_di.di_tv.vval.v_number - dictitem16_T changedtick_di; // b:changedtick dictionary item. + ChangedtickDictItem changedtick_di; // b:changedtick dictionary item. bool b_saving; /* Set to true if we are in the middle of saving the buffer. */ @@ -735,8 +720,8 @@ struct file_buffer { int b_bad_char; /* "++bad=" argument when edit started or 0 */ int b_start_bomb; /* 'bomb' when it was read */ - dictitem_T b_bufvar; /* variable for "b:" Dictionary */ - dict_T *b_vars; /* internal variables, local to buffer */ + ScopeDictDictItem b_bufvar; ///< Variable for "b:" Dictionary. + dict_T *b_vars; ///< b: scope dictionary. /* When a buffer is created, it starts without a swap file. b_may_swap is * then set to indicate that a swap file may be opened later. It is reset @@ -824,9 +809,9 @@ struct tabpage_S { buf_T *(tp_diffbuf[DB_COUNT]); int tp_diff_invalid; ///< list of diffs is outdated frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots - dictitem_T tp_winvar; ///< variable for "t:" Dictionary - dict_T *tp_vars; ///< internal variables, local to tab page - char_u *tp_localdir; ///< Absolute path of local CWD or NULL + ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary. + dict_T *tp_vars; ///< Internal variables, local to tab page. + char_u *tp_localdir; ///< Absolute path of local cwd or NULL. }; /* @@ -1118,8 +1103,8 @@ struct window_S { long w_scbind_pos; - dictitem_T w_winvar; /* variable for "w:" Dictionary */ - dict_T *w_vars; /* internal variables, local to window */ + ScopeDictDictItem w_winvar; ///< Variable for "w:" dictionary. + dict_T *w_vars; ///< Dictionary with w: variables. int w_farsi; /* for the window dependent Farsi functions */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index efe32b915f..cb6a9fa43c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -41,8 +41,10 @@ static bool chartab_initialized = false; (buf)->b_chartab[(unsigned)(c) >> 6] |= (1ull << ((c) & 0x3f)) #define RESET_CHARTAB(buf, c) \ (buf)->b_chartab[(unsigned)(c) >> 6] &= ~(1ull << ((c) & 0x3f)) +#define GET_CHARTAB_TAB(chartab, c) \ + ((chartab)[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) #define GET_CHARTAB(buf, c) \ - ((buf)->b_chartab[(unsigned)(c) >> 6] & (1ull << ((c) & 0x3f))) + GET_CHARTAB_TAB((buf)->b_chartab, c) // Table used below, see init_chartab() for an explanation static char_u g_chartab[256]; @@ -634,7 +636,7 @@ int char2cells(int c) /// @param p /// /// @return number of display cells. -int ptr2cells(char_u *p) +int ptr2cells(const char_u *p) { // For UTF-8 we need to look at more bytes if the first byte is >= 0x80. if (*p >= 0x80) { @@ -776,6 +778,21 @@ bool vim_iswordc(int c) return vim_iswordc_buf(c, curbuf); } +/// Check that "c" is a keyword character +/// Letters and characters from 'iskeyword' option for given buffer. +/// For multi-byte characters mb_get_class() is used (builtin rules). +/// +/// @param[in] c Character to check. +/// @param[in] chartab Buffer chartab. +bool vim_iswordc_tab(const int c, const uint64_t *const chartab) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + if (c >= 0x100) { + return utf_class(c) >= 2; + } + return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; +} + /// Check that "c" is a keyword character: /// Letters and characters from 'iskeyword' option for given buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). @@ -785,10 +802,7 @@ bool vim_iswordc(int c) bool vim_iswordc_buf(int c, buf_T *buf) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(2) { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB(buf, c) != 0; + return vim_iswordc_tab(c, buf->b_chartab); } /// Just like vim_iswordc() but uses a pointer to the (multi-byte) character. diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 82f1bf0a16..45abd314fc 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -12,6 +12,7 @@ #include "nvim/state.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/mark.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "cursor.c.generated.h" @@ -227,9 +228,10 @@ static int coladvance2( } } - /* prevent from moving onto a trail byte */ - if (has_mbyte) - mb_adjustpos(curbuf, pos); + // Prevent from moving onto a trail byte. + if (has_mbyte) { + mark_mb_adjustpos(curbuf, pos); + } if (col < wcol) return FAIL; @@ -361,9 +363,10 @@ void check_cursor_col_win(win_T *win) win->w_cursor.col = len; } else { win->w_cursor.col = len - 1; - /* Move the cursor to the head byte. */ - if (has_mbyte) - mb_adjustpos(win->w_buffer, &win->w_cursor); + // Move the cursor to the head byte. + if (has_mbyte) { + mark_mb_adjustpos(win->w_buffer, &win->w_cursor); + } } } else if (win->w_cursor.col < 0) { win->w_cursor.col = 0; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 5544f0b163..77d5b2c816 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -15,6 +15,7 @@ #include "nvim/cursor.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/farsi.h" @@ -3461,8 +3462,8 @@ expand_by_function ( matchdict = rettv.vval.v_dict; break; default: - /* TODO: Give error message? */ - clear_tv(&rettv); + // TODO(brammool): Give error message? + tv_clear(&rettv); break; } } @@ -3484,10 +3485,12 @@ expand_by_function ( ins_compl_add_dict(matchdict); theend: - if (matchdict != NULL) + if (matchdict != NULL) { dict_unref(matchdict); - if (matchlist != NULL) - list_unref(matchlist); + } + if (matchlist != NULL) { + tv_list_unref(matchlist); + } } /* diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 927a1dcb0e..03d6eef9e3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -94,7 +94,11 @@ #include "nvim/lib/kvec.h" #include "nvim/lib/khash.h" #include "nvim/lib/queue.h" -#include "nvim/eval/typval_encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/gc.h" + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -150,7 +154,6 @@ typedef struct lval_S { static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_listidx = N_("E684: list index out of range: %" PRId64); static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); @@ -167,17 +170,21 @@ static char *e_funcexts = N_( static char *e_funcdict = N_("E717: Dictionary entry already exists"); static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); -static char *e_letwrong = N_("E734: Wrong variable type for %s="); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); -static char_u * const empty_string = (char_u *)""; +// TODO(ZyX-I): move to eval/executor +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + static char_u * const namespace_char = (char_u *)"abglstvw"; -static dictitem_T globvars_var; /* variable used for g: */ +/// Variable used for g: +static ScopeDictDictItem globvars_var; + +/// g: value #define globvarht globvardict.dv_hashtab /* @@ -188,12 +195,15 @@ static hashtab_T compat_hashtab; hashtab_T func_hashtab; +// Used for checking if local variables or arguments used in a lambda. +static int *eval_lavars_used = NULL; + /* * Array to hold the hashtab with variables local to each sourced script. * Each item holds a variable (nameless) that points to the dict_T. */ typedef struct { - dictitem_T sv_var; + ScopeDictDictItem sv_var; dict_T sv_dict; } scriptvar_T; @@ -231,17 +241,44 @@ typedef enum { // The names of packages that once were loaded are remembered. static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL }; -// List heads for garbage collection. Although there can be a reference loop -// from partial to dict to partial, we don't need to keep track of the partial, -// since it will get freed when the dict is unused and gets freed. -static dict_T *first_dict = NULL; // list of all dicts -static list_T *first_list = NULL; // list of all lists - -#define FLEN_FIXED 40 - #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] +/// Short variable name length +#define VAR_SHORT_LEN 20 +/// Number of fixed variables used for arguments +#define FIXVAR_CNT 12 + +struct funccall_S { + ufunc_T *func; ///< Function being called. + int linenr; ///< Next line to be executed. + int returned; ///< ":return" used. + /// Fixed variables for arguments. + TV_DICTITEM_STRUCT(VAR_SHORT_LEN + 1) fixvar[FIXVAR_CNT]; + dict_T l_vars; ///< l: local function variables. + ScopeDictDictItem l_vars_var; ///< Variable for l: scope. + dict_T l_avars; ///< a: argument variables. + ScopeDictDictItem l_avars_var; ///< Variable for a: scope. + list_T l_varlist; ///< List for a:000. + listitem_T l_listitems[MAX_FUNC_ARGS]; ///< List items for a:000. + typval_T *rettv; ///< Return value. + linenr_T breakpoint; ///< Next line with breakpoint or zero. + int dbg_tick; ///< Debug_tick when breakpoint was set. + int level; ///< Top nesting level of executed function. + proftime_T prof_child; ///< Time spent in a child. + funccall_T *caller; ///< Calling function or NULL. + int fc_refcount; ///< Number of user functions that reference this funccall. + int fc_copyID; ///< CopyID used for garbage collection. + garray_T fc_funcs; ///< List of ufunc_T* which keep a reference to "func". +}; + +///< Structure used by trans_function_name() +typedef struct { + dict_T *fd_dict; ///< Dictionary used. + char_u *fd_newkey; ///< New key in "dict" in allocated memory. + dictitem_T *fd_di; ///< Dictionary item used. +} funcdict_T; + /* * Info used by a ":for" loop. */ @@ -283,8 +320,8 @@ typedef enum { // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - dictitem16_T vv_di; ///< Value and name for key (max 16 chars) - char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. + TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { // VV_ tails differing from upcased string literals: @@ -389,7 +426,10 @@ static struct vimvar { #define vv_dict vv_di.di_tv.vval.v_dict #define vv_tv vv_di.di_tv -static dictitem_T vimvars_var; // variable used for v: +/// Variable used for v: +static ScopeDictDictItem vimvars_var; + +/// v: hashtab #define vimvarht vimvardict.dv_hashtab typedef enum { @@ -465,6 +505,17 @@ typedef struct fst { KHASH_MAP_INIT_STR(functions, VimLFuncDef) +/// Type of assert_* check being performed +typedef enum +{ + ASSERT_EQUAL, + ASSERT_NOTEQUAL, + ASSERT_MATCH, + ASSERT_NOTMATCH, + ASSERT_INRANGE, + ASSERT_OTHER, +} assert_type_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -547,7 +598,7 @@ void eval_init(void) dict_T *const msgpack_types_dict = dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = list_alloc(); + list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); @@ -570,7 +621,7 @@ void eval_init(void) dict_T *v_event = dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -601,7 +652,7 @@ void eval_clear(void) xfree(p->vv_str); p->vv_str = NULL; } else if (p->vv_di.di_tv.v_type == VAR_LIST) { - list_unref(p->vv_list); + tv_list_unref(p->vv_list); p->vv_list = NULL; } } @@ -832,7 +883,7 @@ void var_redir_stop(void) int eval_charconvert(const char *const enc_from, const char *const enc_to, const char *const fname_from, const char *const fname_to) { - int err = false; + bool err = false; set_vim_var_string(VV_CC_FROM, enc_from, -1); set_vim_var_string(VV_CC_TO, enc_to, -1); @@ -854,7 +905,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to, int eval_printexpr(const char *const fname, const char *const args) { - int err = false; + bool err = false; set_vim_var_string(VV_FNAME_IN, fname, -1); set_vim_var_string(VV_CMDARG, args, -1); @@ -874,7 +925,7 @@ int eval_printexpr(const char *const fname, const char *const args) void eval_diff(const char *const origfile, const char *const newfile, const char *const outfile) { - int err = FALSE; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_NEW, newfile, -1); @@ -888,7 +939,7 @@ void eval_diff(const char *const origfile, const char *const newfile, void eval_patch(const char *const origfile, const char *const difffile, const char *const outfile) { - int err; + bool err = false; set_vim_var_string(VV_FNAME_IN, origfile, -1); set_vim_var_string(VV_FNAME_DIFF, difffile, -1); @@ -907,27 +958,29 @@ void eval_patch(const char *const origfile, const char *const difffile, int eval_to_bool ( char_u *arg, - int *error, + bool *error, char_u **nextcmd, int skip /* only parse, don't execute */ ) { typval_T tv; - int retval = FALSE; + bool retval = false; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL) - *error = TRUE; - else { - *error = FALSE; + if (skip) { + emsg_skip++; + } + if (eval0(arg, &tv, nextcmd, !skip) == FAIL) { + *error = true; + } else { + *error = false; if (!skip) { retval = (get_tv_number_chk(&tv, error) != 0); - clear_tv(&tv); + tv_clear(&tv); } } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -953,7 +1006,7 @@ eval_to_string_skip ( retval = NULL; else { retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } if (skip) --emsg_skip; @@ -992,9 +1045,10 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) + tv_list_join(&ga, tv.vval.v_list, "\n"); + if (tv.vval.v_list->lv_len > 0) { ga_append(&ga, NL); + } } ga_append(&ga, NUL); retval = (char_u *)ga.ga_data; @@ -1003,7 +1057,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) retval = vim_strsave(numbuf); } else retval = vim_strsave(get_tv_string(&tv)); - clear_tv(&tv); + tv_clear(&tv); } return retval; @@ -1047,7 +1101,7 @@ int eval_to_number(char_u *expr) retval = -1; else { retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); } --emsg_off; @@ -1104,11 +1158,12 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) if (p_verbose == 0) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == OK) { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else + if (eval1(&p, &rettv, true) == OK) { + if (rettv.v_type != VAR_LIST) { + tv_clear(&rettv); + } else { list = rettv.vval.v_list; + } } if (p_verbose == 0) @@ -1207,7 +1262,7 @@ int call_vim_function( ++sandbox; } - rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); @@ -1218,7 +1273,7 @@ int call_vim_function( xfree(argvars); if (ret == FAIL) { - clear_tv(rettv); + tv_clear(rettv); } return ret; @@ -1245,7 +1300,7 @@ call_func_retnr ( return -1; retval = get_tv_number_chk(&rettv, NULL); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1270,7 +1325,7 @@ call_func_retstr ( return NULL; retval = vim_strsave(get_tv_string(&rettv)); - clear_tv(&rettv); + tv_clear(&rettv); return retval; } @@ -1294,7 +1349,7 @@ call_func_retlist ( return NULL; if (rettv.v_type != VAR_LIST) { - clear_tv(&rettv); + tv_clear(&rettv); return NULL; } @@ -1392,7 +1447,7 @@ int eval_foldexpr(char_u *arg, int *cp) *cp = *s++; retval = atol((char *)s); } - clear_tv(&tv); + tv_clear(&tv); } --emsg_off; if (use_sandbox) @@ -1464,13 +1519,13 @@ void ex_let(exarg_T *eap) ++emsg_skip; i = eval0(expr, &rettv, &eap->nextcmd, !eap->skip); if (eap->skip) { - if (i != FAIL) - clear_tv(&rettv); - --emsg_skip; + if (i != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; } else if (i != FAIL) { - (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count, - op); - clear_tv(&rettv); + (void)ex_let_vars(eap->arg, &rettv, false, semicolon, var_count, op); + tv_clear(&rettv); } } } @@ -1516,7 +1571,7 @@ ex_let_vars ( return FAIL; } - i = list_len(l); + i = tv_list_len(l); if (semicolon == 0 && var_count < i) { EMSG(_("E687: Less targets than List items")); return FAIL; @@ -1538,9 +1593,9 @@ ex_let_vars ( if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = list_alloc(); + l = tv_list_alloc(); while (item != NULL) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } @@ -1549,11 +1604,12 @@ ex_let_vars ( ltv.vval.v_list = l; l->lv_refcount = 1; - arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, - (char_u *)"]", nextchars); - clear_tv(<v); - if (arg == NULL) + arg = ex_let_one(skipwhite(arg + 1), <v, false, + (char_u *)"]", nextchars); + tv_clear(<v); + if (arg == NULL) { return FAIL; + } break; } else if (*arg != ',' && *arg != ']') { EMSG2(_(e_intern2), "ex_let_vars()"); @@ -1774,7 +1830,7 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) tv.v_type, s == NULL ? "" : s, first); xfree(s); } - clear_tv(&tv); + tv_clear(&tv); } } } @@ -1788,6 +1844,8 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) return arg; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. * Returns a pointer to the char just after the var name. @@ -1952,6 +2010,8 @@ ex_let_one ( return arg_end; } +// TODO(ZyX-I): move to eval/executor + /// Get an lvalue /// /// Lvalue may be @@ -2081,8 +2141,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ return NULL; if (get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + // Not a number or string. + tv_clear(&var1); return NULL; } } @@ -2090,35 +2150,41 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Optionally get the second index [ :expr]. */ if (*p == ':') { if (lp->ll_tv->v_type == VAR_DICT) { - if (!quiet) + if (!quiet) { EMSG(_(e_dictrange)); - if (!empty1) - clear_tv(&var1); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)) { - if (!quiet) - EMSG(_("E709: [:] requires a List value")); - if (!empty1) - clear_tv(&var1); + if (!quiet) { + emsgf(_("E709: [:] requires a List value")); + } + if (!empty1) { + tv_clear(&var1); + } return NULL; } p = skipwhite(p + 1); - if (*p == ']') - lp->ll_empty2 = TRUE; - else { - lp->ll_empty2 = FALSE; - if (eval1(&p, &var2, TRUE) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (*p == ']') { + lp->ll_empty2 = true; + } else { + lp->ll_empty2 = false; + if (eval1(&p, &var2, true) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return NULL; } if (get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return NULL; } } @@ -2127,12 +2193,15 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_range = FALSE; if (*p != ']') { - if (!quiet) - EMSG(_(e_missbrac)); - if (!empty1) - clear_tv(&var1); - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); + if (!quiet) { + emsgf(_(e_missbrac)); + } + if (!empty1) { + tv_clear(&var1); + } + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } return NULL; } @@ -2145,7 +2214,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, // "[key]": get key from "var1" key = get_tv_string_chk(&var1); // is number or string if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return NULL; } } @@ -2184,56 +2253,63 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, /* Key does not exist in dict: may need to add it. */ if (*p == '[' || *p == '.' || unlet) { - if (!quiet) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); + if (!quiet) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } return NULL; } - if (len == -1) + if (len == -1) { lp->ll_newkey = vim_strsave(key); - else + } else { lp->ll_newkey = vim_strnsave(key, len); - if (len == -1) - clear_tv(&var1); + } + if (len == -1) { + tv_clear(&var1); + } break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { if (len == -1) { - clear_tv(&var1); + tv_clear(&var1); } return NULL; } - if (len == -1) - clear_tv(&var1); + if (len == -1) { + tv_clear(&var1); + } lp->ll_tv = &lp->ll_di->di_tv; } else { /* * Get the number and item for the only or first index of the List. */ - if (empty1) + if (empty1) { lp->ll_n1 = 0; - else { - lp->ll_n1 = get_tv_number(&var1); /* is number or string */ - clear_tv(&var1); + } else { + lp->ll_n1 = get_tv_number(&var1); // Is number or string. + tv_clear(&var1); } lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); if (lp->ll_li == NULL) { if (lp->ll_n1 < 0) { lp->ll_n1 = 0; - lp->ll_li = list_find(lp->ll_list, lp->ll_n1); + lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) - clear_tv(&var2); - if (!quiet) + if (lp->ll_range && !lp->ll_empty2) { + tv_clear(&var2); + } + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); + } return NULL; } @@ -2244,24 +2320,26 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); /* is number or string */ - clear_tv(&var2); + lp->ll_n2 = get_tv_number(&var2); // Is number or string. + tv_clear(&var2); if (lp->ll_n2 < 0) { - ni = list_find(lp->ll_list, lp->ll_n2); + ni = tv_list_find(lp->ll_list, lp->ll_n2); if (ni == NULL) { if (!quiet) EMSGN(_(e_listidx), lp->ll_n2); return NULL; } - lp->ll_n2 = list_idx_of_item(lp->ll_list, ni); + lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); } - /* Check that lp->ll_n2 isn't before lp->ll_n1. */ - if (lp->ll_n1 < 0) - lp->ll_n1 = list_idx_of_item(lp->ll_list, lp->ll_li); + // Check that lp->ll_n2 isn't before lp->ll_n1. + if (lp->ll_n1 < 0) { + lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); + } if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) + if (!quiet) { EMSGN(_(e_listidx), lp->ll_n2); + } return NULL; } } @@ -2273,6 +2351,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return p; } +// TODO(ZyX-I): move to eval/executor + /* * Clear lval "lp" that was filled by get_lval(). */ @@ -2282,6 +2362,8 @@ static void clear_lval(lval_T *lp) xfree(lp->ll_newkey); } +// TODO(ZyX-I): move to eval/executor + /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. @@ -2308,10 +2390,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch STRLEN(lp->ll_name)) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) - && tv_op(&tv, rettv, op) == OK) { + && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { set_var(lp->ll_name, &tv, false); } - clear_tv(&tv); + tv_clear(&tv); } } else { set_var(lp->ll_name, rettv, copy); @@ -2344,18 +2426,18 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch * Assign the List values to the list items. */ for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { - if (op != NULL && *op != '=') - tv_op(&lp->ll_li->li_tv, &ri->li_tv, op); - else { - clear_tv(&lp->ll_li->li_tv); + if (op != NULL && *op != '=') { + eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + } else { + tv_clear(&lp->ll_li->li_tv); copy_tv(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) break; if (lp->ll_li->li_next == NULL) { - /* Need to add an empty item. */ - list_append_number(lp->ll_list, 0); + // Need to add an empty item. + tv_list_append_number(lp->ll_list, 0); assert(lp->ll_li->li_next); } lp->ll_li = lp->ll_li->li_next; @@ -2398,10 +2480,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } if (op != NULL && *op != '=') { - tv_op(lp->ll_tv, rettv, op); + eexe_mod_op(lp->ll_tv, rettv, (const char *)op); goto notify; } else { - clear_tv(lp->ll_tv); + tv_clear(lp->ll_tv); } } @@ -2421,145 +2503,13 @@ notify: } else { dictitem_T *di = lp->ll_di; dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); - clear_tv(&oldtv); - } - } - } -} - -/* - * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" - * Returns OK or FAIL. - */ -static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op) -{ - long n; - char_u numbuf[NUMBUFLEN]; - char_u *s; - - // Can't do anything with a Funcref, a Dict or special value on the right. - if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { - switch (tv1->v_type) { - case VAR_DICT: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: - break; - - case VAR_LIST: - if (*op != '+' || tv2->v_type != VAR_LIST) - break; - /* List += List */ - if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) - list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); - return OK; - - case VAR_NUMBER: - case VAR_STRING: - if (tv2->v_type == VAR_LIST) - break; - if (*op == '+' || *op == '-') { - /* nr += nr or nr -= nr*/ - n = get_tv_number(tv1); - if (tv2->v_type == VAR_FLOAT) { - float_T f = n; - - if (*op == '+') - f += tv2->vval.v_float; - else - f -= tv2->vval.v_float; - clear_tv(tv1); - tv1->v_type = VAR_FLOAT; - tv1->vval.v_float = f; - } else { - if (*op == '+') - n += get_tv_number(tv2); - else - n -= get_tv_number(tv2); - clear_tv(tv1); - tv1->v_type = VAR_NUMBER; - tv1->vval.v_number = n; - } - } else { - if (tv2->v_type == VAR_FLOAT) - break; - - /* str .= str */ - s = get_tv_string(tv1); - s = concat_str(s, get_tv_string_buf(tv2, numbuf)); - clear_tv(tv1); - tv1->v_type = VAR_STRING; - tv1->vval.v_string = s; + tv_clear(&oldtv); } - return OK; - - case VAR_FLOAT: - { - float_T f; - - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) - break; - if (tv2->v_type == VAR_FLOAT) - f = tv2->vval.v_float; - else - f = get_tv_number(tv2); - if (*op == '+') - tv1->vval.v_float += f; - else - tv1->vval.v_float -= f; - } - return OK; - - case VAR_UNKNOWN: - assert(false); - } - } - - EMSG2(_(e_letwrong), op); - return FAIL; -} - -/* - * Add a watcher to a list. - */ -void list_add_watch(list_T *l, listwatch_T *lw) -{ - lw->lw_next = l->lv_watch; - l->lv_watch = lw; -} - -/* - * Remove a watcher from a list. - * No warning when it isn't found... - */ -void list_rem_watch(list_T *l, listwatch_T *lwrem) -{ - listwatch_T *lw, **lwp; - - lwp = &l->lv_watch; - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - if (lw == lwrem) { - *lwp = lw->lw_next; - break; } - lwp = &lw->lw_next; } } -/* - * Just before removing an item from a list: advance watchers to the next - * item. - */ -static void list_fix_watch(list_T *l, listitem_T *item) -{ - listwatch_T *lw; - - for (lw = l->lv_watch; lw != NULL; lw = lw->lw_next) - if (lw->lw_item == item) - lw->lw_item = item->li_next; -} +// TODO(ZyX-I): move to eval/ex_cmds /* * Evaluate the expression used in a ":for var in expr" command. @@ -2567,14 +2517,14 @@ static void list_fix_watch(list_T *l, listitem_T *item) * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) +void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); char_u *expr; typval_T tv; list_T *l; - *errp = TRUE; /* default: there is an error */ + *errp = true; // Default: there is an error. expr = skip_var_list(arg, &fi->fi_varcount, &fi->fi_semicolon); if (expr == NULL) @@ -2589,20 +2539,20 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) if (skip) ++emsg_skip; if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) { - *errp = FALSE; + *errp = false; if (!skip) { l = tv.vval.v_list; if (tv.v_type != VAR_LIST) { EMSG(_(e_listreq)); - clear_tv(&tv); + tv_clear(&tv); } else if (l == NULL) { // a null list is like an empty list: do nothing - clear_tv(&tv); + tv_clear(&tv); } else { /* No need to increment the refcount, it's already set for the * list being used in "tv". */ fi->fi_list = l; - list_add_watch(l, &fi->fi_lw); + tv_list_watch_add(l, &fi->fi_lw); fi->fi_lw.lw_item = l->lv_first; } } @@ -2613,6 +2563,8 @@ void *eval_for_line(char_u *arg, int *errp, char_u **nextcmdp, int skip) return fi; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Use the first item in a ":for" list. Advance to the next. * Assign the values to the variable (list). "arg" points to the first one. @@ -2636,6 +2588,8 @@ int next_for_item(void *fi_void, char_u *arg) return result; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Free the structure used to store info used by ":for". */ @@ -2644,8 +2598,8 @@ void free_for_info(void *fi_void) forinfo_T *fi = (forinfo_T *)fi_void; if (fi != NULL && fi->fi_list != NULL) { - list_rem_watch(fi->fi_list, &fi->fi_lw); - list_unref(fi->fi_list); + tv_list_watch_remove(fi->fi_list, &fi->fi_lw); + tv_list_unref(fi->fi_list); } xfree(fi); } @@ -2733,6 +2687,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) xp->xp_pattern = arg; } +// TODO(ZyX-I): move to eval/ex_cmds /* * ":1,25call func(arg1, arg2)" function call. @@ -2752,13 +2707,14 @@ void ex_call(exarg_T *eap) partial_T *partial = NULL; if (eap->skip) { - /* trans_function_name() doesn't work well when skipping, use eval0() - * instead to skip to any following command, e.g. for: - * :if 0 | call dict.foo().bar() | endif */ - ++emsg_skip; - if (eval0(eap->arg, &rettv, &eap->nextcmd, FALSE) != FAIL) - clear_tv(&rettv); - --emsg_skip; + // trans_function_name() doesn't work well when skipping, use eval0() + // instead to skip to any following command, e.g. for: + // :if 0 | call dict.foo().bar() | endif. + emsg_skip++; + if (eval0(eap->arg, &rettv, &eap->nextcmd, false) != FAIL) { + tv_clear(&rettv); + } + emsg_skip--; return; } @@ -2786,7 +2742,7 @@ void ex_call(exarg_T *eap) /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ startarg = skipwhite(arg); - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { EMSG2(_("E107: Missing parentheses: %s"), eap->arg); @@ -2825,9 +2781,10 @@ void ex_call(exarg_T *eap) break; } - clear_tv(&rettv); - if (doesrange || eap->skip) + tv_clear(&rettv); + if (doesrange || eap->skip) { break; + } /* Stop when immediately aborting on error, or when an interrupt * occurred or an exception was thrown but not caught. @@ -2853,6 +2810,8 @@ end: xfree(tofree); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet[!] var1 ... " command. */ @@ -2861,6 +2820,8 @@ void ex_unlet(exarg_T *eap) ex_unletlock(eap, eap->arg, 0); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":lockvar" and ":unlockvar" commands */ @@ -2879,6 +2840,8 @@ void ex_lockvar(exarg_T *eap) ex_unletlock(eap, arg, deep); } +// TODO(ZyX-I): move to eval/ex_cmds + /* * ":unlet", ":lockvar" and ":unlockvar" are quite similar. */ @@ -2912,8 +2875,9 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) error = TRUE; } else { if (do_lock_var(&lv, name_end, deep, - eap->cmdidx == CMD_lockvar) == FAIL) - error = TRUE; + eap->cmdidx == CMD_lockvar) == FAIL) { + error = true; + } } } @@ -2926,6 +2890,8 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) eap->nextcmd = check_nextcmd(arg); } +// TODO(ZyX-I): move to eval/ex_cmds + static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) { int ret = OK; @@ -2966,14 +2932,14 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) /* Delete a range of List items. */ while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { li = lp->ll_li->li_next; - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; ++lp->ll_n1; } } else { if (lp->ll_list != NULL) { // unlet a List item. - listitem_remove(lp->ll_list, lp->ll_li); + tv_list_item_remove(lp->ll_list, lp->ll_li); } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; @@ -2992,7 +2958,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) if (watched) { dictwatcher_notify(d, key, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); xfree(key); } } @@ -3001,6 +2967,8 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) return ret; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * "unlet" a variable. Return OK if it existed, FAIL if not. * When "forceit" is TRUE don't complain if the variable doesn't exist. @@ -3060,7 +3028,7 @@ int do_unlet(char_u *name, int forceit) if (watched) { dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } return OK; } @@ -3071,12 +3039,15 @@ int do_unlet(char_u *name, int forceit) return FAIL; } +// TODO(ZyX-I): move to eval/ex_cmds + /* * Lock or unlock variable indicated by "lp". * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) +static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, + const bool lock) { int ret = OK; @@ -3104,117 +3075,28 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock) } else { di->di_flags &= ~DI_FLAGS_LOCK; } - item_lock(&di->di_tv, deep, lock); + tv_item_lock(&di->di_tv, deep, lock); } } else if (lp->ll_range) { listitem_T *li = lp->ll_li; /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - item_lock(&li->li_tv, deep, lock); + tv_item_lock(&li->li_tv, deep, lock); li = li->li_next; ++lp->ll_n1; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(&lp->ll_li->li_tv, deep, lock); } else { // (un)lock a Dictionary item. - item_lock(&lp->ll_di->di_tv, deep, lock); + tv_item_lock(&lp->ll_di->di_tv, deep, lock); } return ret; } -/* - * Lock or unlock an item. "deep" is nr of levels to go. - */ -static void item_lock(typval_T *tv, int deep, int lock) -{ - static int recurse = 0; - - if (recurse >= DICT_MAXNEST) { - EMSG(_("E743: variable nested too deep for (un)lock")); - return; - } - if (deep == 0) { - return; - } - recurse++; - - // lock/unlock the item itself -#define CHANGE_LOCK(var, lock) \ - do { \ - var = ((VarLockStatus[]) { \ - [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ - [VAR_FIXED] = VAR_FIXED, \ - })[var]; \ - } while (0) - CHANGE_LOCK(tv->v_lock, lock); - - switch (tv->v_type) { - case VAR_LIST: { - list_T *const l = tv->vval.v_list; - if (l != NULL) { - CHANGE_LOCK(l->lv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - item_lock(&li->li_tv, deep - 1, lock); - } - } - } - break; - } - case VAR_DICT: { - dict_T *const d = tv->vval.v_dict; - if (d != NULL) { - CHANGE_LOCK(d->dv_lock, lock); - if (deep < 0 || deep > 1) { - // Recursive: lock/unlock the items the List contains. - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } - } - } - break; - } - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_STRING: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_SPECIAL: { - break; - } - case VAR_UNKNOWN: { - assert(false); - } - } -#undef CHANGE_LOCK - recurse--; -} - -/* - * Return TRUE if typeval "tv" is locked: Either that value is locked itself - * or it refers to a List or Dictionary that is locked. - */ -static int tv_islocked(typval_T *tv) -{ - return (tv->v_lock & VAR_LOCKED) - || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) - || (tv->v_type == VAR_DICT - && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED)); -} - /* * Delete all "menutrans_" variables. */ @@ -3343,6 +3225,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) return NULL; } +// TODO(ZyX-I): move to eval/expressions /// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. @@ -3379,6 +3262,8 @@ typedef enum { , TYPE_NOMATCH /* !~ */ } exptype_T; +// TODO(ZyX-I): move to eval/expressions + /* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type @@ -3400,15 +3285,15 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) p = skipwhite(arg); ret = eval1(&p, rettv, evaluate); if (ret == FAIL || !ends_excmd(*p)) { - if (ret != FAIL) - clear_tv(rettv); - /* - * Report the invalid expression unless the expression evaluation has - * been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting()) - EMSG2(_(e_invexpr2), arg); + if (ret != FAIL) { + tv_clear(rettv); + } + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception. + if (!aborting()) { + emsgf(_(e_invexpr2), arg); + } ret = FAIL; } if (nextcmd != NULL) @@ -3417,6 +3302,8 @@ static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle top level expression: * expr2 ? expr1 : expr1 @@ -3442,13 +3329,15 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if ((*arg)[0] == '?') { result = FALSE; if (evaluate) { - int error = FALSE; + bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; + } } /* @@ -3462,9 +3351,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Check for the ":". */ if ((*arg)[0] != ':') { - EMSG(_("E109: Missing ':' after '?'")); - if (evaluate && result) - clear_tv(rettv); + emsgf(_("E109: Missing ':' after '?'")); + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } @@ -3472,9 +3362,10 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) * Get the third variable. */ *arg = skipwhite(*arg + 1); - if (eval1(arg, &var2, evaluate && !result) == FAIL) { /* recursive! */ - if (evaluate && result) - clear_tv(rettv); + if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive! + if (evaluate && result) { + tv_clear(rettv); + } return FAIL; } if (evaluate && !result) @@ -3484,6 +3375,8 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle first level expression: * expr2 || expr2 || expr2 logical OR @@ -3498,7 +3391,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3513,12 +3406,14 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) - result = TRUE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) != 0) { + result = true; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3532,11 +3427,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) - result = TRUE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) != 0) { + result = true; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3547,6 +3444,8 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle second level expression: * expr3 && expr3 && expr3 logical AND @@ -3561,7 +3460,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) typval_T var2; long result; int first; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -3576,12 +3475,14 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) - result = FALSE; - clear_tv(rettv); - if (error) + if (get_tv_number_chk(rettv, &error) == 0) { + result = false; + } + tv_clear(rettv); + if (error) { return FAIL; - first = FALSE; + } + first = false; } /* @@ -3595,11 +3496,13 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) - result = FALSE; - clear_tv(&var2); - if (error) + if (get_tv_number_chk(&var2, &error) == 0) { + result = false; + } + tv_clear(&var2); + if (error) { return FAIL; + } } if (evaluate) { rettv->v_type = VAR_NUMBER; @@ -3610,6 +3513,8 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle third level expression: * var1 == var2 @@ -3706,7 +3611,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(p + len); if (eval5(arg, &var2, evaluate) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3728,15 +3633,15 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { EMSG(_("E692: Invalid operation for List")); } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { - /* Compare two Lists for being equal or unequal. */ - n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Lists for being equal or unequal. + n1 = tv_list_equal(rettv->vval.v_list, var2.vval.v_list, ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT) { if (type_is) { @@ -3750,8 +3655,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) EMSG(_("E735: Can only compare Dictionary with Dictionary")); else EMSG(_("E736: Invalid operation for Dictionary")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } else { /* Compare two Dictionaries for being equal or unequal. */ @@ -3765,8 +3670,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) || var2.v_type == VAR_PARTIAL) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if ((rettv->v_type == VAR_PARTIAL @@ -3868,8 +3773,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_UNKNOWN: break; /* avoid gcc warning */ } } - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = n1; } @@ -3878,6 +3783,8 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fourth level expression: * + number addition @@ -3925,7 +3832,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * without evaluating the 2nd operand. So check before to avoid * side effects after an error. */ if (evaluate && get_tv_string_chk(rettv) == NULL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } } @@ -3935,7 +3842,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ *arg = skipwhite(*arg + 1); if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { - clear_tv(rettv); + tv_clear(rettv); return FAIL; } @@ -3946,28 +3853,28 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) if (op == '.') { s1 = get_tv_string_buf(rettv, buf1); /* already checked */ s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { /* type error ? */ - clear_tv(rettv); - clear_tv(&var2); + if (s2 == NULL) { // Type error ? + tv_clear(rettv); + tv_clear(&var2); return FAIL; } p = concat_str(s1, s2); - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; } else if (op == '+' && rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST) { - /* concatenate Lists */ - if (list_concat(rettv->vval.v_list, var2.vval.v_list, - &var3) == FAIL) { - clear_tv(rettv); - clear_tv(&var2); + // Concatenate Lists. + if (tv_list_concat(rettv->vval.v_list, var2.vval.v_list, &var3) + == FAIL) { + tv_clear(rettv); + tv_clear(&var2); return FAIL; } - clear_tv(rettv); + tv_clear(rettv); *rettv = var3; } else { - int error = FALSE; + bool error = false; if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; @@ -3978,7 +3885,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned * before evaluating the 2nd operand. */ - clear_tv(rettv); + tv_clear(rettv); return FAIL; } if (var2.v_type == VAR_FLOAT) @@ -3990,14 +3897,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) } else { n2 = get_tv_number_chk(&var2, &error); if (error) { - clear_tv(rettv); - clear_tv(&var2); + tv_clear(rettv); + tv_clear(&var2); return FAIL; } if (rettv->v_type == VAR_FLOAT) f2 = n2; } - clear_tv(rettv); + tv_clear(rettv); /* If there is a float on either side the result is a float. */ if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT) { @@ -4016,12 +3923,14 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) rettv->vval.v_number = n1; } } - clear_tv(&var2); + tv_clear(&var2); } } return OK; } +// TODO(ZyX-I): move to eval/expressions + /* * Handle fifth level expression: * * number multiplication @@ -4046,7 +3955,7 @@ eval6 ( long n1, n2; int use_float = FALSE; float_T f1 = 0, f2; - int error = FALSE; + bool error = false; /* * Get the first variable. @@ -4069,11 +3978,13 @@ eval6 ( n1 = 0; } else n1 = get_tv_number_chk(rettv, &error); - clear_tv(rettv); - if (error) + tv_clear(rettv); + if (error) { return FAIL; - } else + } + } else { n1 = 0; + } /* * Get the second variable. @@ -4092,11 +4003,13 @@ eval6 ( n2 = 0; } else { n2 = get_tv_number_chk(&var2, &error); - clear_tv(&var2); - if (error) + tv_clear(&var2); + if (error) { return FAIL; - if (use_float) + } + if (use_float) { f2 = n2; + } } /* @@ -4154,6 +4067,8 @@ eval6 ( return OK; } +// TODO(ZyX-I): move to eval/expressions + // Handle sixth level expression: // number number constant // "string" string constant @@ -4192,7 +4107,7 @@ static int eval7( int ret = OK; char_u *alias; - // Initialise variable so that clear_tv() can't mistake this for a + // Initialise variable so that tv_clear() can't mistake this for a // string and free a string that isn't there. rettv->v_type = VAR_UNKNOWN; @@ -4308,7 +4223,7 @@ static int eval7( ++*arg; } else if (ret == OK) { EMSG(_("E110: Missing ')'")); - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } break; @@ -4349,7 +4264,7 @@ static int eval7( // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') { - rettv->vval.v_string = empty_string; + rettv->vval.v_string = (char_u *)tv_empty_string; rettv->v_type = VAR_FUNC; } @@ -4358,7 +4273,7 @@ static int eval7( // an exception was thrown but not caught. if (aborting()) { if (ret == OK) { - clear_tv(rettv); + tv_clear(rettv); } ret = FAIL; } @@ -4382,7 +4297,7 @@ static int eval7( // Apply logical NOT and unary '-', from right to left, ignore '+'. if (ret == OK && evaluate && end_leader > start_leader) { - int error = false; + bool error = false; int val = 0; float_T f = 0.0; @@ -4392,7 +4307,7 @@ static int eval7( val = get_tv_number_chk(rettv, &error); } if (error) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } else { while (end_leader > start_leader) { @@ -4412,10 +4327,10 @@ static int eval7( } } if (rettv->v_type == VAR_FLOAT) { - clear_tv(rettv); + tv_clear(rettv); rettv->vval.v_float = f; } else { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = val; } @@ -4425,6 +4340,8 @@ static int eval7( return ret; } +// TODO(ZyX-I): move to eval/expressions + /* * Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". * "*arg" points to the '[' or '.'. @@ -4499,13 +4416,13 @@ eval_index ( * Get the (first) variable from inside the []. */ *arg = skipwhite(*arg + 1); - if (**arg == ':') - empty1 = TRUE; - else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ + if (**arg == ':') { + empty1 = true; + } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - else if (evaluate && get_tv_string_chk(&var1) == NULL) { - /* not a number or string */ - clear_tv(&var1); + } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + // Not a number or string. + tv_clear(&var1); return FAIL; } @@ -4515,28 +4432,32 @@ eval_index ( if (**arg == ':') { range = TRUE; *arg = skipwhite(*arg + 1); - if (**arg == ']') - empty2 = TRUE; - else if (eval1(arg, &var2, evaluate) == FAIL) { /* recursive! */ - if (!empty1) - clear_tv(&var1); + if (**arg == ']') { + empty2 = true; + } else if (eval1(arg, &var2, evaluate) == FAIL) { // Recursive! + if (!empty1) { + tv_clear(&var1); + } return FAIL; } else if (evaluate && get_tv_string_chk(&var2) == NULL) { - /* not a number or string */ - if (!empty1) - clear_tv(&var1); - clear_tv(&var2); + // Not a number or string. + if (!empty1) { + tv_clear(&var1); + } + tv_clear(&var2); return FAIL; } } /* Check for the ']'. */ if (**arg != ']') { - if (verbose) - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); + if (verbose) { + emsgf(_(e_missbrac)); + } + tv_clear(&var1); + if (range) { + tv_clear(&var2); + } return FAIL; } *arg = skipwhite(*arg + 1); /* skip the ']' */ @@ -4546,14 +4467,14 @@ eval_index ( n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { n1 = get_tv_number(&var1); - clear_tv(&var1); + tv_clear(&var1); } if (range) { if (empty2) n2 = -1; else { n2 = get_tv_number(&var2); - clear_tv(&var2); + tv_clear(&var2); } } @@ -4587,15 +4508,16 @@ eval_index ( else s = vim_strnsave(s + n1, 1); } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = s; break; case VAR_LIST: - len = list_len(rettv->vval.v_list); - if (n1 < 0) + len = tv_list_len(rettv->vval.v_list); + if (n1 < 0) { n1 = len + n1; + } if (!empty1 && (n1 < 0 || n1 >= len)) { /* For a range we allow invalid values and return an empty * list. A list index out of range is an error. */ @@ -4616,29 +4538,31 @@ eval_index ( n2 = len - 1; if (!empty2 && (n2 < 0 || n2 + 1 < n1)) n2 = -1; - l = list_alloc(); - item = list_find(rettv->vval.v_list, n1); + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - list_append_tv(l, &item->li_tv); + tv_list_append_tv(l, &item->li_tv); item = item->li_next; } - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; ++l->lv_refcount; } else { - copy_tv(&list_find(rettv->vval.v_list, n1)->li_tv, &var1); - clear_tv(rettv); + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); *rettv = var1; } break; case VAR_DICT: if (range) { - if (verbose) - EMSG(_(e_dictrange)); - if (len == -1) - clear_tv(&var1); + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } return FAIL; } { @@ -4647,22 +4571,25 @@ eval_index ( if (len == -1) { key = get_tv_string_chk(&var1); if (key == NULL) { - clear_tv(&var1); + tv_clear(&var1); return FAIL; } } item = dict_find(rettv->vval.v_dict, key, (int)len); - if (item == NULL && verbose) - EMSG2(_(e_dictkey), key); - if (len == -1) - clear_tv(&var1); - if (item == NULL) + if (item == NULL && verbose) { + emsgf(_(e_dictkey), key); + } + if (len == -1) { + tv_clear(&var1); + } + if (item == NULL) { return FAIL; + } copy_tv(&item->di_tv, &var1); - clear_tv(rettv); + tv_clear(rettv); *rettv = var1; } break; @@ -4678,6 +4605,8 @@ eval_index ( return OK; } +// TODO(ZyX-I): move to eval/executor + /// Get an option value /// /// @param[in,out] arg Points to the '&' or '+' before the option name. Is @@ -4931,10 +4860,12 @@ char_u *partial_name(partial_T *pt) return pt->pt_func->uf_name; } +// TODO(ZyX-I): Move to eval/typval.h + static void partial_free(partial_T *pt) { for (int i = 0; i < pt->pt_argc; i++) { - clear_tv(&pt->pt_argv[i]); + tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); dict_unref(pt->pt_dict); @@ -4947,6 +4878,8 @@ static void partial_free(partial_T *pt) xfree(pt); } +// TODO(ZyX-I): Move to eval/typval.h + /// Unreference a closure: decrement the reference count and free it when it /// becomes zero. void partial_unref(partial_T *pt) @@ -4965,7 +4898,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) listitem_T *item; if (evaluate) { - l = list_alloc(); + l = tv_list_alloc(); } *arg = skipwhite(*arg + 1); @@ -4973,10 +4906,10 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ goto failret; if (evaluate) { - item = listitem_alloc(); + item = tv_list_item_alloc(); item->li_tv = tv; item->li_tv.v_lock = 0; - list_append(l, item); + tv_list_append(l, item); } if (**arg == ']') @@ -4992,7 +4925,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { - list_free(l); + tv_list_free(l); } return FAIL; } @@ -5007,142 +4940,8 @@ failret: return OK; } -/* - * Allocate an empty header for a list. - * Caller should take care of the reference count. - */ -list_T *list_alloc(void) FUNC_ATTR_NONNULL_RET -{ - list_T *list = xcalloc(1, sizeof(list_T)); - /* Prepend the list to the list of lists for garbage collection. */ - if (first_list != NULL) - first_list->lv_used_prev = list; - list->lv_used_prev = NULL; - list->lv_used_next = first_list; - first_list = list; - return list; -} - -/* - * Allocate an empty list for a return value. - */ -static list_T *rettv_list_alloc(typval_T *rettv) -{ - list_T *l = list_alloc(); - rettv->vval.v_list = l; - rettv->v_type = VAR_LIST; - rettv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; - return l; -} - -/// Unreference a list: decrement the reference count and free it when it -/// becomes zero. -void list_unref(list_T *l) { - if (l != NULL && --l->lv_refcount <= 0) { - list_free(l); - } -} - -/// Free a list, including all items it points to. -/// Ignores the reference count. -static void list_free_contents(list_T *l) { - listitem_T *item; - - for (item = l->lv_first; item != NULL; item = l->lv_first) { - // Remove the item before deleting it. - l->lv_first = item->li_next; - clear_tv(&item->li_tv); - xfree(item); - } -} - -static void list_free_list(list_T *l) { - // Remove the list from the list of lists for garbage collection. - if (l->lv_used_prev == NULL) { - first_list = l->lv_used_next; - } else { - l->lv_used_prev->lv_used_next = l->lv_used_next; - } - if (l->lv_used_next != NULL) { - l->lv_used_next->lv_used_prev = l->lv_used_prev; - } - - xfree(l); -} - -void list_free(list_T *l) { - if (!in_free_unref_items) { - list_free_contents(l); - list_free_list(l); - } -} - -/* - * Allocate a list item. - * It is not initialized, don't forget to set v_lock. - */ -listitem_T *listitem_alloc(void) FUNC_ATTR_NONNULL_RET -{ - return xmalloc(sizeof(listitem_T)); -} - -/* - * Free a list item. Also clears the value. Does not notify watchers. - */ -void listitem_free(listitem_T *item) -{ - clear_tv(&item->li_tv); - xfree(item); -} - -/* - * Remove a list item from a List and free it. Also clears the value. - */ -void listitem_remove(list_T *l, listitem_T *item) -{ - vim_list_remove(l, item, item); - listitem_free(item); -} - -/* - * Get the number of items in a list. - */ -static long list_len(list_T *l) -{ - if (l == NULL) - return 0L; - return l->lv_len; -} - -/* - * Return TRUE when two lists have exactly the same values. - */ -static int -list_equal ( - list_T *l1, - list_T *l2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - listitem_T *item1, *item2; - - if (l1 == NULL || l2 == NULL) - return FALSE; - if (l1 == l2) - return TRUE; - if (list_len(l1) != list_len(l2)) - return FALSE; - - for (item1 = l1->lv_first, item2 = l2->lv_first; - item1 != NULL && item2 != NULL; - item1 = item1->li_next, item2 = item2->li_next) - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) - return FALSE; - return item1 == NULL && item2 == NULL; -} +// TODO(ZyX-I): move to eval/typval /* * Return the dictitem that an entry in a hashtable points to. @@ -5152,6 +4951,8 @@ dictitem_T *dict_lookup(hashitem_T *hi) return HI2DI(hi); } +// TODO(ZyX-I): move to eval/typval + /* * Return TRUE when two dictionaries have exactly the same key/values. */ @@ -5255,8 +5056,7 @@ static bool func_equal( * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ -static int -tv_equal ( +int tv_equal( typval_T *tv1, typval_T *tv2, int ic, /* ignore case */ @@ -5298,501 +5098,41 @@ tv_equal ( switch (tv1->v_type) { case VAR_LIST: - ++recursive_cnt; - r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE); - --recursive_cnt; + recursive_cnt++; + r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); + recursive_cnt--; return r; case VAR_DICT: ++recursive_cnt; r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - -/* - * Locate item with index "n" in list "l" and return it. - * A negative index is counted from the end; -1 is the last item. - * Returns NULL when "n" is out of range. - */ -listitem_T *list_find(list_T *l, long n) -{ - listitem_T *item; - long idx; - - if (l == NULL) - return NULL; - - /* Negative index is relative to the end. */ - if (n < 0) - n = l->lv_len + n; - - /* Check for index out of range. */ - if (n < 0 || n >= l->lv_len) - return NULL; - - /* When there is a cached index may start search from there. */ - if (l->lv_idx_item != NULL) { - if (n < l->lv_idx / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else if (n > (l->lv_idx + l->lv_len) / 2) { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } else { - /* closest to the cached index */ - item = l->lv_idx_item; - idx = l->lv_idx; - } - } else { - if (n < l->lv_len / 2) { - /* closest to the start of the list */ - item = l->lv_first; - idx = 0; - } else { - /* closest to the end of the list */ - item = l->lv_last; - idx = l->lv_len - 1; - } - } - - while (n > idx) { - /* search forward */ - item = item->li_next; - ++idx; - } - while (n < idx) { - /* search backward */ - item = item->li_prev; - --idx; - } - - /* cache the used index */ - l->lv_idx = idx; - l->lv_idx_item = item; - - return item; -} - -/* - * Get list item "l[idx]" as a number. - */ -static long -list_find_nr ( - list_T *l, - long idx, - int *errorp /* set to TRUE when something wrong */ -) -{ - listitem_T *li; - - li = list_find(l, idx); - if (li == NULL) { - if (errorp != NULL) - *errorp = TRUE; - return -1L; - } - return get_tv_number_chk(&li->li_tv, errorp); -} - -/* - * Get list item "l[idx - 1]" as a string. Returns NULL for failure. - */ -char_u *list_find_str(list_T *l, long idx) -{ - listitem_T *li; - - li = list_find(l, idx - 1); - if (li == NULL) { - EMSGN(_(e_listidx), idx); - return NULL; - } - return get_tv_string(&li->li_tv); -} - -/* - * Locate "item" list "l" and return its index. - * Returns -1 when "item" is not in the list. - */ -static long list_idx_of_item(list_T *l, listitem_T *item) -{ - long idx = 0; - listitem_T *li; - - if (l == NULL) - return -1; - idx = 0; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) - ++idx; - if (li == NULL) - return -1; - return idx; -} - -/* - * Append item "item" to the end of list "l". - */ -void list_append(list_T *l, listitem_T *item) -{ - if (l->lv_last == NULL) { - /* empty list */ - l->lv_first = item; - l->lv_last = item; - item->li_prev = NULL; - } else { - l->lv_last->li_next = item; - item->li_prev = l->lv_last; - l->lv_last = item; - } - ++l->lv_len; - item->li_next = NULL; -} - -/* - * Append typval_T "tv" to the end of list "l". - */ -void list_append_tv(list_T *l, typval_T *tv) -{ - listitem_T *li = listitem_alloc(); - copy_tv(tv, &li->li_tv); - list_append(l, li); -} - -/* - * Add a list to a list. - */ -void list_append_list(list_T *list, list_T *itemlist) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = itemlist; - list_append(list, li); - ++itemlist->lv_refcount; -} - -/* - * Add a dictionary to a list. Used by getqflist(). - */ -void list_append_dict(list_T *list, dict_T *dict) -{ - listitem_T *li = listitem_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_dict = dict; - list_append(list, li); - ++dict->dv_refcount; -} - -/// Make a copy of "str" and append it as an item to list "l" -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -/// @param[in] len Length of the appended string. May be negative, in this -/// case string is considered to be usual zero-terminated -/// string. -void list_append_string(list_T *l, const char_u *str, int len) - FUNC_ATTR_NONNULL_ARG(1) -{ - if (str == NULL) { - list_append_allocated_string(l, NULL); - } else { - list_append_allocated_string(l, (len >= 0 - ? xmemdupz((char *) str, len) - : xstrdup((char *) str))); - } -} - -/// Append given string to the list -/// -/// Unlike list_append_string this function does not copy the string. -/// -/// @param[out] l List to append to. -/// @param[in] str String to append. -void list_append_allocated_string(list_T *l, char *const str) - FUNC_ATTR_NONNULL_ARG(1) -{ - listitem_T *li = listitem_alloc(); - - list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) str; -} - -/* - * Append "n" to list "l". - */ -void list_append_number(list_T *l, varnumber_T n) -{ - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_number = n; - list_append(l, li); -} - -/* - * Insert typval_T "tv" in list "l" before "item". - * If "item" is NULL append at the end. - */ -void list_insert_tv(list_T *l, typval_T *tv, listitem_T *item) -{ - listitem_T *ni = listitem_alloc(); - - copy_tv(tv, &ni->li_tv); - list_insert(l, ni, item); -} - -void list_insert(list_T *l, listitem_T *ni, listitem_T *item) -{ - if (item == NULL) - /* Append new item at end of list. */ - list_append(l, ni); - else { - /* Insert new item before existing item. */ - ni->li_prev = item->li_prev; - ni->li_next = item; - if (item->li_prev == NULL) { - l->lv_first = ni; - ++l->lv_idx; - } else { - item->li_prev->li_next = ni; - l->lv_idx_item = NULL; - } - item->li_prev = ni; - ++l->lv_len; - } -} - -/* - * Extend "l1" with "l2". - * If "bef" is NULL append at the end, otherwise insert before this item. - */ -static void list_extend(list_T *l1, list_T *l2, listitem_T *bef) -{ - listitem_T *item; - int todo = l2->lv_len; - - /* We also quit the loop when we have inserted the original item count of - * the list, avoid a hang when we extend a list with itself. */ - for (item = l2->lv_first; item != NULL && --todo >= 0; item = item->li_next) { - list_insert_tv(l1, &item->li_tv, bef); - } -} - -/* - * Concatenate lists "l1" and "l2" into a new list, stored in "tv". - * Return FAIL on failure to copy. - */ -static int list_concat(list_T *l1, list_T *l2, typval_T *tv) -{ - list_T *l; - - if (l1 == NULL || l2 == NULL) - return FAIL; - - /* make a copy of the first list. */ - l = list_copy(NULL, l1, false, 0); - if (l == NULL) - return FAIL; - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - - /* append all items from the second list */ - list_extend(l, l2, NULL); - return OK; -} - -/// Make a copy of list -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original list to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied list. May be NULL in case original list is NULL or some -/// failure happens. The refcount of the new list is set to 1. -static list_T *list_copy(const vimconv_T *const conv, - list_T *const orig, - const bool deep, - const int copyID) - FUNC_ATTR_WARN_UNUSED_RESULT -{ - listitem_T *item; - listitem_T *ni; - - if (orig == NULL) - return NULL; - - list_T *copy = list_alloc(); - if (copyID != 0) { - /* Do this before adding the items, because one of the items may - * refer back to this list. */ - orig->lv_copyID = copyID; - orig->lv_copylist = copy; - } - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { - ni = listitem_alloc(); - if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { - xfree(ni); - break; - } - } else - copy_tv(&item->li_tv, &ni->li_tv); - list_append(copy, ni); - } - ++copy->lv_refcount; - if (item != NULL) { - list_unref(copy); - copy = NULL; - } - - return copy; -} - -/// Remove items "item" to "item2" from list "l". -/// @warning Does not free the listitem or the value! -void vim_list_remove(list_T *l, listitem_T *item, listitem_T *item2) -{ - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { - --l->lv_len; - list_fix_watch(l, ip); - if (ip == item2) { - break; - } - } - - if (item2->li_next == NULL) { - l->lv_last = item->li_prev; - } else { - item2->li_next->li_prev = item->li_prev; - } - if (item->li_prev == NULL) { - l->lv_first = item2->li_next; - } else { - item->li_prev->li_next = item2->li_next; - } - l->lv_idx_item = NULL; -} - -typedef struct join_S { - char_u *s; - char_u *tofree; -} join_T; - -/// Join list into a string, helper function -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l List to join. -/// @param[in] sep Used separator. -/// @param[in] join_gap Garray to keep each list item string. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join_inner(garray_T *const gap, list_T *const l, - const char *const sep, garray_T *const join_gap) - FUNC_ATTR_NONNULL_ALL -{ - int sumlen = 0; - bool first = true; - listitem_T *item; - - /* Stringify each item in the list. */ - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - char *s; - size_t len; - s = encode_tv2echo(&item->li_tv, &len); - if (s == NULL) { - return FAIL; - } - - sumlen += (int) len; - - join_T *const p = GA_APPEND_VIA_PTR(join_T, join_gap); - p->tofree = p->s = (char_u *) s; - - line_breakcheck(); - } - - /* Allocate result buffer with its total size, avoid re-allocation and - * multiple copy operations. Add 2 for a tailing ']' and NUL. */ - if (join_gap->ga_len >= 2) - sumlen += (int)STRLEN(sep) * (join_gap->ga_len - 1); - ga_grow(gap, sumlen + 2); - - for (int i = 0; i < join_gap->ga_len && !got_int; ++i) { - if (first) { - first = false; - } else { - ga_concat(gap, (const char_u *) sep); - } - const join_T *const p = ((const join_T *)join_gap->ga_data) + i; - - if (p->s != NULL) - ga_concat(gap, p->s); - line_breakcheck(); - } - - return OK; -} - -/// Join list into a string using given separator -/// -/// @param[out] gap Garray where result will be saved. -/// @param[in] l Joined list. -/// @param[in] sep Separator. -/// -/// @return OK in case of success, FAIL otherwise. -static int list_join(garray_T *const gap, list_T *const l, - const char *const sep) - FUNC_ATTR_NONNULL_ALL -{ - if (l->lv_len < 1) { - return OK; - } + return r; - garray_T join_ga; - int retval; + case VAR_NUMBER: + return tv1->vval.v_number == tv2->vval.v_number; - ga_init(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, &join_ga); + case VAR_FLOAT: + return tv1->vval.v_float == tv2->vval.v_float; + + case VAR_STRING: + s1 = get_tv_string_buf(tv1, buf1); + s2 = get_tv_string_buf(tv2, buf2); + return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; -# define FREE_JOIN_TOFREE(join) xfree((join)->tofree) - GA_DEEP_CLEAR(&join_ga, join_T, FREE_JOIN_TOFREE); + case VAR_SPECIAL: + return tv1->vval.v_special == tv2->vval.v_special; - return retval; + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_UNKNOWN: + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does + // not equal anything, not even self. + return false; + } + + assert(false); + return false; } /// Get next (unique) copy ID @@ -6035,7 +5375,7 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// Note: This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// /// @param copyID Free lists/dictionaries that don't have this ID. /// @return true, if something was freed. @@ -6048,14 +5388,14 @@ static int free_unref_items(int copyID) // Let all "free" functions know that we are here. This means no // dictionaries, lists, or jobs are to be freed, because we will // do that here. - in_free_unref_items = true; + tv_in_free_unref_items = true; // PASS 1: free the contents of the items. We don't free the items // themselves yet, so that it is possible to decrement refcount counters. // Go through the list of dicts and free items without the copyID. // Don't free dicts that are referenced internally. - for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) { + for (dict_T *dd = gc_first_dict; dd != NULL; dd = dd->dv_used_next) { if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list @@ -6068,36 +5408,36 @@ static int free_unref_items(int copyID) // Go through the list of lists and free items without the copyID. // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. - for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) { + for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_contents(ll); + tv_list_free_contents(ll); did_free = true; } } // PASS 2: free the items themselves. - for (dd = first_dict; dd != NULL; dd = dd_next) { + for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { dict_free_dict(dd); } } - for (ll = first_list; ll != NULL; ll = ll_next) { + for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) && ll->lv_watch == NULL) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. - list_free_list(ll); + tv_list_free_list(ll); } } - in_free_unref_items = false; + tv_in_free_unref_items = false; return did_free; } @@ -6369,12 +5709,13 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET { dict_T *d = xmalloc(sizeof(dict_T)); - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; d->dv_used_prev = NULL; - first_dict = d; + gc_first_dict = d; hash_init(&d->dv_hashtab); d->dv_lock = VAR_UNLOCKED; @@ -6434,21 +5775,18 @@ void dict_unref(dict_T *d) /// Free a Dictionary, including all items it contains. /// Ignores the reference count. -static void dict_free_contents(dict_T *d) { - int todo; - hashitem_T *hi; - dictitem_T *di; - - - /* Lock the hashtab, we don't want it to resize while freeing items. */ +static void dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. hash_lock(&d->dv_hashtab); assert(d->dv_hashtab.ht_locked > 0); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { + size_t todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { if (!HASHITEM_EMPTY(hi)) { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = HI2DI(hi); hash_remove(&d->dv_hashtab, hi); dictitem_free(di); todo--; @@ -6465,10 +5803,12 @@ static void dict_free_contents(dict_T *d) { hash_clear(&d->dv_hashtab); } -static void dict_free_dict(dict_T *d) { +static void dict_free_dict(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ // Remove the dict from the list of dicts for garbage collection. if (d->dv_used_prev == NULL) { - first_dict = d->dv_used_next; + gc_first_dict = d->dv_used_next; } else { d->dv_used_prev->dv_used_next = d->dv_used_next; } @@ -6479,8 +5819,10 @@ static void dict_free_dict(dict_T *d) { xfree(d); } -void dict_free(dict_T *d) { - if (!in_free_unref_items) { +void dict_free(dict_T *d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { dict_free_contents(d); dict_free_dict(d); } @@ -6536,7 +5878,7 @@ static void dictitem_remove(dict_T *dict, dictitem_T *item) */ void dictitem_free(dictitem_T *item) { - clear_tv(&item->di_tv); + tv_clear(&item->di_tv); if (item->di_flags & DI_FLAGS_ALLOC) { xfree(item); } @@ -6767,7 +6109,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) copy_tv(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); - clear_tv(&tv); + tv_clear(&tv); return res; } @@ -6845,34 +6187,35 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; if (**arg != ':') { EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } if (evaluate) { key = get_tv_string_buf_chk(&tvkey, buf); if (key == NULL) { // "key" is NULL when get_tv_string_buf_chk() gave an errmsg - clear_tv(&tvkey); + tv_clear(&tvkey); goto failret; } } *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) { /* recursive! */ - if (evaluate) - clear_tv(&tvkey); + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! + if (evaluate) { + tv_clear(&tvkey); + } goto failret; } if (evaluate) { item = dict_find(d, key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); + tv_clear(&tvkey); + tv_clear(&tv); goto failret; } item = dictitem_alloc(key); - clear_tv(&tvkey); + tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; if (dict_add(d, item) == FAIL) { @@ -7391,8 +6734,9 @@ get_func_tv ( } } - while (--argcount >= 0) - clear_tv(&argvars[argcount]); + while (--argcount >= 0) { + tv_clear(&argvars[argcount]); + } *arg = skipwhite(argp); return ret; @@ -7688,7 +7032,7 @@ call_func( } while (argv_clear > 0) { - clear_tv(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear]); } xfree(tofree); xfree(name); @@ -7804,7 +7148,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_op_wrapper(argvars, rettv, (FunPtr)&fabs); } else { varnumber_T n; - int error = FALSE; + bool error = false; n = get_tv_number_chk(&argvars[0], &error); if (error) @@ -7829,7 +7173,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - list_append_tv(l, &argvars[1]); + tv_list_append_tv(l, &argvars[1]); copy_tv(&argvars[0], rettv); } } else @@ -7950,9 +7294,10 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; } else { - rettv_list_alloc(rettv); - for (idx = 0; idx < ARGCOUNT; ++idx) { - list_append_string(rettv->vval.v_list, alist_name(&ARGLIST[idx]), -1); + tv_list_alloc_ret(rettv); + for (idx = 0; idx < ARGCOUNT; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&ARGLIST[idx]), -1); } } } @@ -8024,10 +7369,10 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc()); } - list_append_string(vimvars[VV_ERRORS].vv_list, - gap->ga_data, gap->ga_len); + tv_list_append_string(vimvars[VV_ERRORS].vv_list, + (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } static void assert_equal_common(typval_T *argvars, assert_type_T atype) @@ -8116,10 +7461,10 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { - int error = (int)false; - varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + bool error = false; + const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); + const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); + const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); if (error) { return; @@ -8129,8 +7474,9 @@ void assert_inrange(typval_T *argvars) prepare_assert_error(&ga); char msg[55]; - vim_snprintf(msg, sizeof(msg), "range %" PRId64 " - %" PRId64 ",", - (int64_t)lower, (int64_t)upper); + vim_snprintf(msg, sizeof(msg), + "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", + lower, upper); fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], ASSERT_INRANGE); assert_error(&ga); @@ -8141,7 +7487,7 @@ void assert_inrange(typval_T *argvars) // Common for assert_true() and assert_false(). static void assert_bool(typval_T *argvars, bool is_true) { - int error = (int)false; + bool error = false; garray_T ga; if ((argvars[0].v_type != VAR_NUMBER @@ -8363,7 +7709,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = false; + bool error = false; char_u *name; rettv->vval.v_number = -1; @@ -8512,9 +7858,10 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, &dummy, true, partial, selfdict); } - /* Free the arguments. */ - while (argc > 0) - clear_tv(&argv[--argc]); + // Free the arguments. + while (argc > 0) { + tv_clear(&argv[--argc]); + } return r; } @@ -8707,8 +8054,8 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u buf2[NUMBUFLEN]; int def = 1; int type = VIM_GENERIC; - char_u *typestr; - int error = FALSE; + char_u *typestr; + bool error = false; message = get_tv_string_chk(&argvars[0]); if (message == NULL) @@ -8768,15 +8115,16 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { li = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; ic = get_tv_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { idx = get_tv_number_chk(&argvars[3], &error); if (!error) { - li = list_find(l, idx); - if (li == NULL) + li = tv_list_find(l, idx); + if (li == NULL) { EMSGN(_(e_listidx), idx); + } } } if (error) @@ -8793,7 +8141,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) hashitem_T *hi; if ((d = argvars[0].vval.v_dict) != NULL) { - int error = FALSE; + bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { ic = get_tv_number_chk(&argvars[2], &error); @@ -9349,7 +8697,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Handle d.key, l[idx], f(expr). n = (handle_subscript((const char **)&p, &tv, true, false) == OK); if (n) { - clear_tv(&tv); + tv_clear(&tv); } } } @@ -9372,8 +8720,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; expand_T xpc; - int error = FALSE; - char_u *result; + bool error = false; + char_u *result; rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN @@ -9390,9 +8738,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars(s, s, &len, NULL, &errormsg, NULL); --emsg_off; if (rettv->v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (result != NULL) { - list_append_string(rettv->vval.v_list, result, -1); + tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } } else rettv->vval.v_string = result; @@ -9407,14 +8755,14 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, - options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } @@ -9479,12 +8827,12 @@ void dict_extend(dict_T *d1, dict_T *d2, char_u *action) copy_tv(&di1->di_tv, &oldtv); } - clear_tv(&di1->di_tv); + tv_clear(&di1->di_tv); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); if (watched) { dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -9504,7 +8852,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l1, *l2; listitem_T *item; long before; - int error = FALSE; + bool error = false; l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; @@ -9515,10 +8863,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (error) return; /* type error; errmsg already given */ - if (before == l1->lv_len) + if (before == l1->lv_len) { item = NULL; - else { - item = list_find(l1, before); + } else { + item = tv_list_find(l1, before); if (item == NULL) { EMSGN(_(e_listidx), before); return; @@ -9526,7 +8874,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else item = NULL; - list_extend(l1, l2, item); + tv_list_extend(l1, l2, item); copy_tv(&argvars[0], rettv); } @@ -9616,8 +8964,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) char_u *p; char_u pathbuf[NUMBUFLEN]; int count = 1; - int first = TRUE; - int error = FALSE; + bool first = true; + bool error = false; rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; @@ -9632,13 +8980,14 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) if (*p != NUL) path = p; - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { count = get_tv_number_chk(&argvars[2], &error); + } } } if (count < 0) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (*fname != NUL && !error) { @@ -9652,11 +9001,11 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) (find_what == FINDFILE_DIR ? (char_u *)"" : curbuf->b_p_sua)); - first = FALSE; - - if (fresult != NULL && rettv->v_type == VAR_LIST) - list_append_string(rettv->vval.v_list, fresult, -1); + first = false; + if (fresult != NULL && rettv->v_type == VAR_LIST) { + tv_list_append_string(rettv->vval.v_list, (const char *)fresult, -1); + } } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); } @@ -9736,9 +9085,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); int r = filter_map_one(&di->di_tv, expr, map, &rem); - clear_tv(&vimvars[VV_KEY].vv_tv); - if (r == FAIL || did_emsg) + tv_clear(&vimvars[VV_KEY].vv_tv); + if (r == FAIL || did_emsg) { break; + } if (!map && rem) { if (var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { @@ -9762,9 +9112,10 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL || did_emsg) break; - if (!map && rem) - listitem_remove(l, li); - ++idx; + if (!map && rem) { + tv_list_item_remove(l, li); + } + idx++; } } @@ -9819,24 +9170,25 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } } if (map) { - /* map(): replace the list item value */ - clear_tv(tv); + // map(): replace the list item value. + tv_clear(tv); rettv.v_lock = 0; *tv = rettv; } else { - int error = FALSE; + bool error = false; - /* filter(): when expr is zero remove the item */ + // filter(): when expr is zero remove the item *remp = (get_tv_number_chk(&rettv, &error) == 0); - clear_tv(&rettv); - /* On type error, nothing has been removed; return FAIL to stop the - * loop. The error message was given by get_tv_number_chk(). */ - if (error) + tv_clear(&rettv); + // On type error, nothing has been removed; return FAIL to stop the + // loop. The error message was given by get_tv_number_chk(). + if (error) { goto theend; + } } retval = OK; theend: - clear_tv(&vimvars[VV_VAL].vv_tv); + tv_clear(&vimvars[VV_VAL].vv_tv); return retval; } @@ -10278,11 +9630,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL) { - int error = FALSE; + bool error = false; - li = list_find(l, get_tv_number_chk(&argvars[1], &error)); - if (!error && li != NULL) + li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + if (!error && li != NULL) { tv = &li->li_tv; + } } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { @@ -10326,11 +9679,9 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (STRCMP(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (rettv_list_alloc(rettv) != NULL) { - int i; - - for (i = 0; i < pt->pt_argc; i++) { - list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); + if (tv_list_alloc_ret(rettv) != NULL) { + for (int i = 0; i < pt->pt_argc; i++) { + tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } } } else { @@ -10353,13 +9704,13 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *d = dict_alloc(); + dict_T *const d = dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); - list_append_dict(l, d); + tv_list_append_dict(l, d); } } @@ -10384,17 +9735,17 @@ static dict_T *get_buffer_info(buf_T *buf) dict_add_dict(dict, "variables", buf->b_vars); // List of windows displaying this buffer - list_T *windows = list_alloc(); + list_T *const windows = tv_list_alloc(); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { - list_append_number(windows, (varnumber_T)wp->handle); + tv_list_append_number(windows, (varnumber_T)wp->handle); } } dict_add_list(dict, "windows", windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *signs = list_alloc(); + list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); dict_add_list(dict, "signs", signs); } @@ -10410,7 +9761,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -10452,9 +9803,9 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } - dict_T *d = get_buffer_info(buf); + dict_T *const d = get_buffer_info(buf); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (argbuf != NULL) { return; @@ -10475,7 +9826,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (retlist) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) @@ -10496,8 +9847,8 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli if (end > buf->b_ml.ml_line_count) end = buf->b_ml.ml_line_count; while (start <= end) { - list_append_string( - rettv->vval.v_list, ml_get_buf(buf, start++, FALSE), -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)ml_get_buf(buf, start++, false), -1); } } } @@ -10594,7 +9945,7 @@ f_getbufvar_end: static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n; - int error = FALSE; + bool error = false; no_mapping++; for (;; ) { @@ -10802,12 +10153,13 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (pat != NULL) { ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } } xfree(pat); @@ -11110,7 +10462,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -11145,7 +10497,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (cur != NULL) { dict_T *dict = dict_alloc(); if (cur->match.regprog == NULL) { @@ -11158,11 +10510,11 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = list_alloc(); - list_append_number(l, (varnumber_T)llpos->lnum); + list_T *l = tv_list_alloc(); + tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { - list_append_number(l, (varnumber_T)llpos->col); - list_append_number(l, (varnumber_T)llpos->len); + tv_list_append_number(l, (varnumber_T)llpos->col); + tv_list_append_number(l, (varnumber_T)llpos->len); } sprintf(buf, "pos%d", i + 1); dict_add_list(dict, buf, l); @@ -11181,7 +10533,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); } - list_append_dict(rettv->vval.v_list, dict); + tv_list_append_dict(rettv->vval.v_list, dict); cur = cur->next; } } @@ -11205,20 +10557,22 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = rettv_list_alloc(rettv); - list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); - list_append_number(l, (fp != NULL) ? (varnumber_T)fp->lnum : (varnumber_T)0); - list_append_number(l, - (fp != NULL) - ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) - : (varnumber_T)0); - list_append_number(l, - (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); + list_T *l = tv_list_alloc_ret(rettv); + tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); + tv_list_append_number(l, ((fp != NULL) + ? (varnumber_T)fp->lnum + : (varnumber_T)0)); + tv_list_append_number( + l, ((fp != NULL) + ? (varnumber_T)(fp->col == MAXCOL ? MAXCOL : fp->col + 1) + : (varnumber_T)0)); + tv_list_append_number( + l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { update_curswant(); - list_append_number(l, curwin->w_curswant == MAXCOL + tv_list_append_number(l, (curwin->w_curswant == MAXCOL ? (varnumber_T)MAXCOL - : (varnumber_T)curwin->w_curswant + 1); + : (varnumber_T)curwin->w_curswant + 1)); } } @@ -11251,7 +10605,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) int regname; int arg2 = false; bool return_list = false; - int error = false; + bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { strregname = get_tv_string_chk(&argvars[0]); @@ -11279,7 +10633,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = list_alloc(); + rettv->vval.v_list = tv_list_alloc(); } rettv->vval.v_list->lv_refcount++; } else { @@ -11328,9 +10682,9 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) dict_add_nr_str(dict, "tabnr", tp_idx, NULL); - list_T *l = list_alloc(); + list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - list_append_number(l, (varnumber_T)wp->handle); + tv_list_append_number(l, (varnumber_T)wp->handle); } dict_add_list(dict, "windows", l); @@ -11345,7 +10699,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -11362,9 +10716,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (tparg != NULL && tp != tparg) { continue; } - dict_T *d = get_tabpage_info(tp, tpnr); + dict_T *const d = get_tabpage_info(tp, tpnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (tparg != NULL) { return; @@ -11447,7 +10801,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -11467,9 +10821,9 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } winnr++; - dict_T *d = get_win_info(wp, tabnr, winnr); + dict_T *const d = get_win_info(wp, tabnr, winnr); if (d != NULL) { - list_append_dict(rettv->vval.v_list, d); + tv_list_append_dict(rettv->vval.v_list, d); } if (wparg != NULL) { // found information about a specific window @@ -11504,7 +10858,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = get_tv_number_chk(vp, NULL); + int nr = (int)get_tv_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -11645,7 +10999,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int options = WILD_SILENT|WILD_USE_NL; expand_T xpc; - int error = FALSE; + bool error = false; /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ @@ -11669,14 +11023,15 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) xpc.xp_context = EXPAND_FILES; if (p_wic) options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), - NULL, options, WILD_ALL); - else { - rettv_list_alloc(rettv); + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, + options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { - list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } ExpandCleanup(&xpc); } @@ -11688,7 +11043,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int flags = 0; // Flags for globpath. - int error = false; + bool error = false; // Return a string, or a list if the optional third argument is non-zero. rettv->v_type = VAR_STRING; @@ -11722,10 +11077,10 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); for (int i = 0; i < ga.ga_len; i++) { - list_append_string(rettv->vval.v_list, - ((char_u **)(ga.ga_data))[i], -1); + tv_list_append_string(rettv->vval.v_list, + ((const char **)(ga.ga_data))[i], -1); } } @@ -12249,11 +11604,11 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l != NULL) { item = l->lv_first; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; - /* Start at specified item. Use the cached index that list_find() - * sets, so that a negative number also works. */ - item = list_find(l, get_tv_number_chk(&argvars[2], &error)); + // Start at specified item. Use the cached index that tv_list_find() + // sets, so that a negative number also works. + item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); idx = l->lv_idx; if (argvars[3].v_type != VAR_UNKNOWN) ic = get_tv_number_chk(&argvars[3], &error); @@ -12456,10 +11811,8 @@ static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long before = 0; - listitem_T *item; - list_T *l; - int error = false; + list_T *l; + bool error = false; const char *const arg_errmsg = _("insert() argument"); const size_t arg_errmsg_len = strlen(arg_errmsg); @@ -12467,6 +11820,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "insert()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { + long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = get_tv_number_chk(&argvars[2], &error); } @@ -12475,17 +11829,16 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (before == l->lv_len) - item = NULL; - else { - item = list_find(l, before); + listitem_T *item = NULL; + if (before != l->lv_len) { + item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); l = NULL; } } if (l != NULL) { - list_insert_tv(l, &argvars[1], item); + tv_list_insert_tv(l, &argvars[1], item); copy_tv(&argvars[0], rettv); } } @@ -12574,7 +11927,7 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) if ((d = argvars[0].vval.v_dict) == NULL) return; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { @@ -12582,8 +11935,8 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) --todo; di = HI2DI(hi); - li = listitem_alloc(); - list_append(rettv->vval.v_list, li); + li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); if (what == 0) { /* keys() */ @@ -12594,21 +11947,21 @@ static void dict_list(typval_T *argvars, typval_T *rettv, int what) /* values() */ copy_tv(&di->di_tv, &li->li_tv); } else { - /* items() */ - l2 = list_alloc(); + // items() + l2 = tv_list_alloc(); li->li_tv.v_type = VAR_LIST; li->li_tv.v_lock = 0; li->li_tv.vval.v_list = l2; ++l2->lv_refcount; - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); li2->li_tv.v_type = VAR_STRING; li2->li_tv.v_lock = 0; li2->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = listitem_alloc(); - list_append(l2, li2); + li2 = tv_list_item_alloc(); + tv_list_append(l2, li2); copy_tv(&di->di_tv, &li2->li_tv); } } @@ -12983,7 +12336,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } list_T *args = argvars[0].vval.v_list; - list_T *rv = list_alloc(); + list_T *rv = tv_list_alloc(); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); @@ -12994,11 +12347,11 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER || !(data = find_job(arg->li_tv.vval.v_number))) { - list_append_number(rv, -3); + tv_list_append_number(rv, -3); } else { // append the list item and set the status pointer so we'll collect the // status code when the job exits - list_append_number(rv, -1); + tv_list_append_number(rv, -1); data->status_ptr = &rv->lv_last->li_tv.vval.v_number; // Process any pending events for the job because we'll temporarily // replace the parent queue @@ -13097,7 +12450,7 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (sep != NULL) { ga_init(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, (char *) sep); + tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } else @@ -13181,7 +12534,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) get_tv_string(&argvars[0])); break; case VAR_LIST: - rettv->vval.v_number = list_len(argvars[0].vval.v_list); + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); @@ -13435,12 +12788,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 3 || type == 4) { // type 3: return empty list when there are no matches. // type 4: return ["", -1, -1, -1] - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (type == 4) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); - list_append_number(rettv->vval.v_list, (varnumber_T)-1); + tv_list_append_string(rettv->vval.v_list, "", 0); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); } } else if (type == 2) { rettv->v_type = VAR_STRING; @@ -13461,16 +12814,17 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[2], &error); if (error) goto theend; if (l != NULL) { - li = list_find(l, start); - if (li == NULL) + li = tv_list_find(l, start); + if (li == NULL) { goto theend; - idx = l->lv_idx; /* use the cached index */ + } + idx = l->lv_idx; // Use the cached index. } else { if (start < 0) start = 0; @@ -13552,11 +12906,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* return list with matched string and submatches */ for (i = 0; i < NSUBEXP; ++i) { if (regmatch.endp[i] == NULL) { - list_append_string(rettv->vval.v_list, (char_u *)"", 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } else { - list_append_string(rettv->vval.v_list, - regmatch.startp[i], - (int)(regmatch.endp[i] - regmatch.startp[i])); + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); } } } else if (type == 2) { @@ -13583,7 +12937,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (type == 4 && l == NULL) { // matchstrpos() without a list: drop the second item - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first->li_next); + tv_list_item_remove(rettv->vval.v_list, + rettv->vval.v_list->lv_first->li_next); } theend: @@ -13609,7 +12964,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ int prio = 10; /* default priority */ int id = -1; - int error = false; + bool error = false; char_u *conceal_char = NULL; rettv->vval.v_number = -1; @@ -13667,7 +13022,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - int error = false; + bool error = false; int prio = 10; int id = -1; char_u *conceal_char = NULL; @@ -13708,7 +13063,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); int id = get_tv_number(&argvars[0]); @@ -13716,11 +13071,12 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *m; if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { - list_append_string(rettv->vval.v_list, syn_id2name(m->hlg_id), -1); - list_append_string(rettv->vval.v_list, m->pattern, -1); + tv_list_append_string(rettv->vval.v_list, + (const char *)syn_id2name(m->hlg_id), -1); + tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); } else { - list_append_string(rettv->vval.v_list, NULL, -1); - list_append_string(rettv->vval.v_list, NULL, -1); + tv_list_append_string(rettv->vval.v_list, NULL, 0); + tv_list_append_string(rettv->vval.v_list, NULL, 0); } } } @@ -13768,7 +13124,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) { long n = 0; long i; - int error = FALSE; + bool error = false; if (argvars[0].v_type == VAR_LIST) { list_T *l; @@ -13938,7 +13294,7 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL) { return; @@ -13966,7 +13322,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = rettv_list_alloc(rettv); + list_T *ret_list = tv_list_alloc_ret(rettv); const list_T *list = argvars[0].vval.v_list; if (list == NULL || list->lv_first == NULL) { return; @@ -14011,9 +13367,9 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(ret_list, li); + tv_list_append(ret_list, li); if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; @@ -14188,11 +13544,11 @@ static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long start; - long end; - long stride = 1; + varnumber_T start; + varnumber_T end; + varnumber_T stride = 1; long i; - int error = FALSE; + bool error = false; start = get_tv_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { @@ -14204,16 +13560,17 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) stride = get_tv_number_chk(&argvars[2], &error); } - if (error) - return; /* type error; errmsg already given */ - if (stride == 0) - EMSG(_("E726: Stride is zero")); - else if (stride > 0 ? end + 1 < start : end - 1 > start) - EMSG(_("E727: Start past end")); - else { - rettv_list_alloc(rettv); + if (error) { + return; // Type error; errmsg already given. + } + if (stride == 0) { + emsgf(_("E726: Stride is zero")); + } else if (stride > 0 ? end + 1 < start : end - 1 > start) { + emsgf(_("E727: Start past end")); + } else { + tv_list_alloc_ret(rettv); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { - list_append_number(rettv->vval.v_list, (varnumber_T)i); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } } } @@ -14244,7 +13601,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) maxline = get_tv_number(&argvars[2]); } - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); /* Always open the file in binary mode, library functions have a mind of * their own about CR-LF conversion. */ @@ -14293,11 +13650,11 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = s; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); start = p + 1; /* step over newline */ if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) @@ -14372,8 +13729,8 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ if (maxline < 0) while (cnt > -maxline) { - listitem_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - --cnt; + tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); + cnt--; } xfree(prev); @@ -14395,9 +13752,9 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL return FAIL; } - int error = false; - varnumber_T n1 = list_find_nr(arg->vval.v_list, 0L, &error); - varnumber_T n2 = list_find_nr(arg->vval.v_list, 1L, &error); + bool error = false; + varnumber_T n1 = tv_list_find_nr(arg->vval.v_list, 0L, &error); + varnumber_T n2 = tv_list_find_nr(arg->vval.v_list, 1L, &error); if (error) { return FAIL; } @@ -14456,9 +13813,9 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - rettv_list_alloc(rettv); - list_append_number(rettv->vval.v_list, u.split.high); - list_append_number(rettv->vval.v_list, u.split.low); + tv_list_alloc_ret(rettv); + tv_list_append_number(rettv->vval.v_list, u.split.high); + tv_list_append_number(rettv->vval.v_list, u.split.low); } /// f_reltimestr - return a string that represents the value of {time} @@ -14519,27 +13876,27 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listdictarg), "remove()"); } else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { - int error = (int)false; + bool error = false; idx = get_tv_number_chk(&argvars[1], &error); - if (error) - ; /* type error: do nothing, errmsg already given */ - else if ((item = list_find(l, idx)) == NULL) + if (error) { + // Type error: do nothing, errmsg already given. + } else if ((item = tv_list_find(l, idx)) == NULL) { EMSGN(_(e_listidx), idx); - else { + } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - vim_list_remove(l, item, item); + tv_list_remove_items(l, item, item); *rettv = item->li_tv; xfree(item); } else { /* Remove range of items, return list with values. */ end = get_tv_number_chk(&argvars[2], &error); - if (error) - ; /* type error: do nothing */ - else if ((item2 = list_find(l, end)) == NULL) + if (error) { + // Type error: do nothing. + } else if ((item2 = tv_list_find(l, end)) == NULL) { EMSGN(_(e_listidx), end); - else { + } else { int cnt = 0; for (li = item; li != NULL; li = li->li_next) { @@ -14547,11 +13904,11 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (li == item2) break; } - if (li == NULL) /* didn't find "item2" after "item" */ - EMSG(_(e_invrange)); - else { - vim_list_remove(l, item, item2); - l = rettv_list_alloc(rettv); + if (li == NULL) { // Didn't find "item2" after "item". + emsgf(_(e_invrange)); + } else { + tv_list_remove_items(l, item, item2); + l = tv_list_alloc_ret(rettv); l->lv_first = item; l->lv_last = item2; item->li_prev = NULL; @@ -14588,10 +13945,10 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].vval.v_list != NULL) { while (n-- > 0) { - list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } } else { @@ -14802,12 +14159,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) l->lv_len = 0; while (li != NULL) { listitem_T *const ni = li->li_prev; - list_append(l, li); + tv_list_append(l, li); li = ni; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + l->lv_refcount++; l->lv_idx = l->lv_len - l->lv_idx - 1; } } @@ -15243,7 +14600,7 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int locally = 1; int thisblock = 0; - int error = FALSE; + bool error = false; rettv->vval.v_number = 1; /* default: FAIL */ @@ -15346,15 +14703,15 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); } /* @@ -15386,7 +14743,6 @@ do_searchpair ( int n; int r; int nest = 1; - int err; int options = SEARCH_KEEP; proftime_T tm; @@ -15442,7 +14798,8 @@ do_searchpair ( if (*skip != NUL) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - r = eval_to_bool(skip, &err, NULL, FALSE); + bool err; + r = eval_to_bool(skip, &err, NULL, false); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ @@ -15513,7 +14870,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int flags = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); n = search_cmn(argvars, &match_pos, &flags); if (n > 0) { @@ -15521,10 +14878,11 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) col = match_pos.col; } - list_append_number(rettv->vval.v_list, (varnumber_T)lnum); - list_append_number(rettv->vval.v_list, (varnumber_T)col); - if (flags & SP_SUBPAT) - list_append_number(rettv->vval.v_list, (varnumber_T)n); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); + tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); + if (flags & SP_SUBPAT) { + tv_list_append_number(rettv->vval.v_list, (varnumber_T)n); + } } /// "serverlist()" function @@ -15534,13 +14892,13 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = rettv_list_alloc(rettv); + list_T *l = tv_list_alloc_ret(rettv); for (size_t i = 0; i < n; i++) { - listitem_T *li = listitem_alloc(); + listitem_T *li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *) addrs[i]; - list_append(l, li); + li->li_tv.vval.v_string = (char_u *)addrs[i]; + tv_list_append(l, li); } xfree(addrs); } @@ -15610,7 +14968,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; aco_save_T aco; // set curbuf to be our buf, temporarily @@ -15915,7 +15273,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) d = li->li_tv.vval.v_dict; if (dict_find(d, (char_u *)"pattern", -1) == NULL) { if (s == NULL) { - s = list_alloc(); + s = tv_list_alloc(); if (s == NULL) { return; } @@ -15929,7 +15287,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &di->di_tv); s->lv_refcount++; } else { break; @@ -15949,7 +15307,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); - list_unref(s); + tv_list_unref(s); s = NULL; } xfree(group); @@ -16193,7 +15551,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) if (*varname == '&') { long numval; char_u *strval; - int error = false; + bool error = false; ++varname; numval = get_tv_number_chk(varp, &error); @@ -16272,7 +15630,7 @@ typedef struct { char_u *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; - int item_compare_func_err; + bool item_compare_func_err; } sortinfo_T; static sortinfo_T *sortinfo = NULL; @@ -16399,13 +15757,13 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si1->item->li_tv, &argv[0]); copy_tv(&si2->item->li_tv, &argv[1]); - rettv.v_type = VAR_UNKNOWN; // clear_tv() uses this + rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func(func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); + tv_clear(&argv[0]); + tv_clear(&argv[1]); if (res == FAIL) { res = ITEM_COMPARE_FAIL; @@ -16415,7 +15773,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type } - clear_tv(&rettv); + tv_clear(&rettv); // When the result would be zero, compare the pointers themselves. Makes // the sort stable. @@ -16470,7 +15828,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) rettv->v_type = VAR_LIST; ++l->lv_refcount; - len = list_len(l); + len = tv_list_len(l); if (len <= 1) { goto theend; // short list sorts pretty quickly } @@ -16490,7 +15848,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { - int error = FALSE; + bool error = false; i = get_tv_number_chk(&argvars[1], &error); if (error) { @@ -16569,7 +15927,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) l->lv_len = 0; for (i = 0; i < len; i++) { - list_append(l, ptrs[i].item); + tv_list_append(l, ptrs[i].item); } } } @@ -16605,8 +15963,8 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { l->lv_last = ptrs[i].item; } - list_fix_watch(l, li); - listitem_free(li); + tv_list_watch_fix(l, li); + tv_list_item_free(li); l->lv_len--; } } @@ -16667,7 +16025,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { /* Find the start and length of the badly spelled word. */ @@ -16692,14 +16050,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - list_append_string(rettv->vval.v_list, word, (int)len); - list_append_string(rettv->vval.v_list, - (char_u *)(attr == HLF_SPB ? "bad" : - attr == HLF_SPR ? "rare" : - attr == HLF_SPL ? "local" : - attr == HLF_SPC ? "caps" : - ""), - -1); + tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, + (attr == HLF_SPB ? "bad" + : attr == HLF_SPR ? "rare" + : attr == HLF_SPL ? "local" + : attr == HLF_SPC ? "caps" + : NULL), -1); } /* @@ -16708,13 +16065,13 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *str; - int typeerr = FALSE; + bool typeerr = false; int maxcount; garray_T ga; listitem_T *li; bool need_capital = false; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { str = get_tv_string(&argvars[0]); @@ -16735,11 +16092,11 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) for (int i = 0; i < ga.ga_len; ++i) { str = ((char_u **)ga.ga_data)[i]; - li = listitem_alloc(); + li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; li->li_tv.vval.v_string = str; - list_append(rettv->vval.v_list, li); + tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); } @@ -16755,8 +16112,8 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *save_cpo; int match; colnr_T col = 0; - int keepempty = FALSE; - int typeerr = FALSE; + bool keepempty = false; + bool typeerr = false; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; @@ -16765,15 +16122,17 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) str = get_tv_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) - typeerr = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) - keepempty = get_tv_number_chk(&argvars[2], &typeerr); + if (pat == NULL) { + typeerr = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + } } if (pat == NULL || *pat == NUL) pat = (char_u *)"[\\x01- ]\\+"; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (typeerr) return; @@ -16793,7 +16152,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 && *str != NUL && match && end < regmatch.endp[0])) { - list_append_string(rettv->vval.v_list, str, (int)(end - str)); + tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); } if (!match) break; @@ -16917,33 +16276,29 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "strgetchar()" function static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - int len; - int error = false; - int charidx; - rettv->vval.v_number = -1; - str = get_tv_string_chk(&argvars[0]); + + char_u *str = get_tv_string_chk(&argvars[0]); if (str == NULL) { return; } - len = (int)STRLEN(str); - charidx = get_tv_number_chk(&argvars[1], &error); + + bool error = false; + varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); if (error) { return; } - { - int byteidx = 0; + const size_t len = STRLEN(str); + size_t byteidx = 0; - while (charidx >= 0 && byteidx < len) { - if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); - break; - } - charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + while (charidx >= 0 && byteidx < len) { + if (charidx == 0) { + rettv->vval.v_number = mb_ptr2char(str + byteidx); + break; } + charidx--; + byteidx += MB_CPTR2LEN(str + byteidx); } } @@ -16966,7 +16321,7 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; /* type error; errmsg already given */ if (argvars[2].v_type != VAR_UNKNOWN) { - int error = FALSE; + bool error = false; start_idx = get_tv_number_chk(&argvars[2], &error); if (error || start_idx >= (int)STRLEN(haystack)) @@ -17048,22 +16403,17 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // "strcharpart()" function -static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int nchar; - int nbyte = 0; - int charlen; - int len = 0; - int slen; - int error = false; - - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); +static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + char_u *const p = get_tv_string(&argvars[0]); + const size_t slen = STRLEN(p); - nchar = get_tv_number_chk(&argvars[1], &error); + int nbyte = 0; + bool error = false; + varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { - while (nchar > 0 && nbyte < slen) { + while (nchar > 0 && (size_t)nbyte < slen) { nbyte += MB_CPTR2LEN(p + nbyte); nchar--; } @@ -17071,9 +16421,10 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { nbyte = nchar; } } + int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - charlen = get_tv_number(&argvars[2]); - while (charlen > 0 && nbyte + len < slen) { + int charlen = get_tv_number(&argvars[2]); + while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; if (off < 0) { @@ -17092,12 +16443,12 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (nbyte < 0) { len += nbyte; nbyte = 0; - } else if (nbyte > slen) { + } else if ((size_t)nbyte > slen) { nbyte = slen; } if (len < 0) { len = 0; - } else if (nbyte + len > slen) { + } else if (nbyte + len > (int)slen) { len = slen - nbyte; } @@ -17114,7 +16465,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n; int len; int slen; - int error = FALSE; + bool error = false; p = get_tv_string(&argvars[0]); slen = (int)STRLEN(p); @@ -17205,7 +16556,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int error = FALSE; + bool error = false; int no = (int)get_tv_number_chk(&argvars[0], &error); if (error) { return; @@ -17270,7 +16621,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) long lnum; long col; int trans; - int transerr = FALSE; + bool transerr = false; lnum = get_tv_lnum(argvars); /* -1 on type error */ col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ @@ -17396,7 +16747,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); @@ -17417,10 +16768,10 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); + tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen - list_append_string(rettv->vval.v_list, str, -1); - list_append_number(rettv->vval.v_list, matchid); + tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); + tv_list_append_number(rettv->vval.v_list, matchid); } /* @@ -17441,43 +16792,24 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && col <= (long)STRLEN(ml_get(lnum))) { - rettv_list_alloc(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, FALSE, NULL, TRUE); + tv_list_alloc_ret(rettv); + (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); int id; int i = 0; while ((id = syn_get_stack_item(i++)) >= 0) { - list_append_number(rettv->vval.v_list, id); + tv_list_append_number(rettv->vval.v_list, id); } } } -static list_T* string_to_list(char_u *str, size_t len, bool keepempty) +static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { - list_T *list = list_alloc(); - - // Copy each line to a list element using NL as the delimiter. - for (size_t i = 0; i < len; i++) { - char_u *start = str + i; - size_t line_len = (char_u *) xmemscan(start, NL, len - i) - start; - i += line_len; - - // Don't use a str function to copy res as it may contains NULs. - char_u *s = xmemdupz(start, line_len); - memchrsub(s, NUL, NL, line_len); // Replace NUL with NL to avoid truncation - - listitem_T *li = listitem_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - list_append(list, li); + if (!keepempty && str[len - 1] == NL) { + len--; } - - // Optionally retain final newline, if present - if (keepempty && str[len-1] == NL) { - list_append_string(list, (char_u*)"", 0); - } - + list_T *const list = tv_list_alloc(); + encode_list_write(list, str, len); return list; } @@ -17522,7 +16854,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -17534,7 +16866,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { keepempty = get_tv_number(&argvars[2]); } - rettv->vval.v_list = string_to_list((char_u *) res, nread, keepempty != 0); + rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; rettv->v_type = VAR_LIST; @@ -17588,9 +16920,9 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) wp = (tp == curtab) ? firstwin : tp->tp_firstwin; } if (wp != NULL) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); while (wp != NULL) { - list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); + tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; } } @@ -17683,16 +17015,16 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; + char *fname; tagname_T tn; - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); fname = xmalloc(MAXPATHL); - int first = TRUE; - while (get_tagfname(&tn, first, fname) == OK) { - list_append_string(rettv->vval.v_list, fname, -1); - first = FALSE; + bool first = true; + while (get_tagfname(&tn, first, (char_u *)fname) == OK) { + tv_list_append_string(rettv->vval.v_list, fname, -1); + first = false; } tagname_free(&tn); @@ -17712,7 +17044,7 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*tag_pattern == NUL) return; - (void)get_tags(rettv_list_alloc(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); } /* @@ -18029,7 +17361,7 @@ static void timer_due_cb(TimeWatcher *tw, void *data) init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { // special case: timeout=0 means the callback will be @@ -18295,7 +17627,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); - list = list_alloc(); + list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); dict_add_list(dict, "entries", list); } @@ -18356,7 +17688,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_findbuf(argvars, rettv->vval.v_list); } @@ -18375,7 +17707,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_list_alloc(rettv); + tv_list_alloc_ret(rettv); win_id2tabwin(argvars, rettv->vval.v_list); } @@ -18786,40 +18118,47 @@ var2fpos ( if (varp->v_type == VAR_LIST) { list_T *l; int len; - int error = FALSE; - listitem_T *li; + bool error = false; + listitem_T *li; l = varp->vval.v_list; if (l == NULL) return NULL; - /* Get the line number */ - pos.lnum = list_find_nr(l, 0L, &error); - if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) - return NULL; /* invalid line number */ + // Get the line number. + pos.lnum = tv_list_find_nr(l, 0L, &error); + if (error || pos.lnum <= 0 || pos.lnum > curbuf->b_ml.ml_line_count) { + // Invalid line number. + return NULL; + } - /* Get the column number */ - pos.col = list_find_nr(l, 1L, &error); - if (error) + // Get the column number. + pos.col = tv_list_find_nr(l, 1L, &error); + if (error) { return NULL; + } len = (long)STRLEN(ml_get(pos.lnum)); - /* We accept "$" for the column number: last column. */ - li = list_find(l, 1L); + // We accept "$" for the column number: last column. + li = tv_list_find(l, 1L); if (li != NULL && li->li_tv.v_type == VAR_STRING && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) + && STRCMP(li->li_tv.vval.v_string, "$") == 0) { pos.col = len + 1; + } - /* Accept a position up to the NUL after the line. */ - if (pos.col == 0 || (int)pos.col > len + 1) - return NULL; /* invalid column number */ - --pos.col; + // Accept a position up to the NUL after the line. + if (pos.col == 0 || (int)pos.col > len + 1) { + // Invalid column number. + return NULL; + } + pos.col--; - /* Get the virtual offset. Defaults to zero. */ - pos.coladd = list_find_nr(l, 2L, &error); - if (error) + // Get the virtual offset. Defaults to zero. + pos.coladd = tv_list_find_nr(l, 2L, &error); + if (error) { pos.coladd = 0; + } return &pos; } @@ -18890,32 +18229,37 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) return FAIL; if (fnump != NULL) { - n = list_find_nr(l, i++, NULL); /* fnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // fnum + if (n < 0) { return FAIL; - if (n == 0) - n = curbuf->b_fnum; /* current buffer */ + } + if (n == 0) { + n = curbuf->b_fnum; // Current buffer. + } *fnump = n; } - n = list_find_nr(l, i++, NULL); /* lnum */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // lnum + if (n < 0) { return FAIL; + } posp->lnum = n; - n = list_find_nr(l, i++, NULL); /* col */ - if (n < 0) + n = tv_list_find_nr(l, i++, NULL); // col + if (n < 0) { return FAIL; + } posp->col = n; - n = list_find_nr(l, i, NULL); // off - if (n < 0) + n = tv_list_find_nr(l, i, NULL); // off + if (n < 0) { posp->coladd = 0; - else + } else { posp->coladd = n; + } if (curswantp != NULL) { - *curswantp = list_find_nr(l, i + 1, NULL); // curswant + *curswantp = tv_list_find_nr(l, i + 1, NULL); // curswant } return OK; @@ -19259,7 +18603,7 @@ void set_vcount(long count, long count1, int set_prevcount) /// @param[in] val Value to set to. void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_NUMBER; vimvars[idx].vv_nr = val; } @@ -19270,7 +18614,7 @@ void set_vim_var_nr(const VimVarIndex idx, const varnumber_T val) /// @param[in] val Value to set to. void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) { - clear_tv(&vimvars[idx].vv_tv); + tv_clear(&vimvars[idx].vv_tv); vimvars[idx].vv_type = VAR_SPECIAL; vimvars[idx].vv_special = val; } @@ -19284,7 +18628,7 @@ void set_vim_var_special(const VimVarIndex idx, const SpecialVarValue val) void set_vim_var_string(const VimVarIndex idx, const char *const val, const ptrdiff_t len) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_STRING; if (val == NULL) { vimvars[idx].vv_str = NULL; @@ -19301,7 +18645,7 @@ void set_vim_var_string(const VimVarIndex idx, const char *const val, /// @param[in,out] val Value to set to. Reference count will be incremented. void set_vim_var_list(const VimVarIndex idx, list_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { @@ -19316,7 +18660,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) /// Also keys of the dictionary will be made read-only. void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) { - clear_tv(&vimvars[idx].vv_di.di_tv); + tv_clear(&vimvars[idx].vv_di.di_tv); vimvars[idx].vv_type = VAR_DICT; vimvars[idx].vv_dict = val; @@ -19536,17 +18880,19 @@ handle_subscript( curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, pt, selfdict); - /* Clear the funcref afterwards, so that deleting it while - * evaluating the arguments is possible (see test55). */ - if (evaluate) - clear_tv(&functv); + // Clear the funcref afterwards, so that deleting it while + // evaluating the arguments is possible (see test55). + if (evaluate) { + tv_clear(&functv); + } /* Stop the expression evaluation when immediately aborting on * error, or when an interrupt occurred or an exception was thrown * but not caught. */ if (aborting()) { - if (ret == OK) - clear_tv(rettv); + if (ret == OK) { + tv_clear(rettv); + } ret = FAIL; } dict_unref(selfdict); @@ -19560,7 +18906,7 @@ handle_subscript( } else selfdict = NULL; if (eval_index((char_u **)arg, rettv, evaluate, verbose) == FAIL) { - clear_tv(rettv); + tv_clear(rettv); ret = FAIL; } } @@ -19667,7 +19013,7 @@ void free_tv(typval_T *varp) partial_unref(varp->vval.v_partial); break; case VAR_LIST: - list_unref(varp->vval.v_list); + tv_list_unref(varp->vval.v_list); break; case VAR_DICT: dict_unref(varp->vval.v_dict); @@ -19682,247 +19028,6 @@ void free_tv(typval_T *varp) } } -#define TYPVAL_ENCODE_ALLOW_SPECIALS false - -#define TYPVAL_ENCODE_CONV_NIL(tv) \ - do { \ - tv->vval.v_special = kSpecialVarFalse; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ - TYPVAL_ENCODE_CONV_NIL(tv) - -#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ - do { \ - (void)num; \ - tv->vval.v_number = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) - -#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ - do { \ - tv->vval.v_float = 0; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ - do { \ - xfree(buf); \ - tv->vval.v_string = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) - -#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) - -static inline int _nothing_conv_func_start(typval_T *const tv, - char_u *const fun) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) -{ - tv->v_lock = VAR_UNLOCKED; - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt_ = tv->vval.v_partial; - if (pt_ != NULL && pt_->pt_refcount > 1) { - pt_->pt_refcount--; - tv->vval.v_partial = NULL; - return OK; - } - } else { - func_unref(fun); - if (fun != empty_string) { - xfree(fun); - } - tv->vval.v_string = NULL; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ - do { \ - if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ - return OK; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) -#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) - -static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL -{ - if (tv->v_type == VAR_PARTIAL) { - partial_T *const pt = tv->vval.v_partial; - if (pt == NULL) { - return; - } - // Dictionary should already be freed by the time. - // If it was not freed then it is a part of the reference cycle. - assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); - pt->pt_dict = NULL; - // As well as all arguments. - pt->pt_argc = 0; - assert(pt->pt_refcount <= 1); - partial_unref(pt); - tv->vval.v_partial = NULL; - assert(tv->v_lock == VAR_UNLOCKED); - } -} -#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) - -#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ - do { \ - list_unref(tv->vval.v_list); \ - tv->vval.v_list = NULL; \ - tv->v_lock = VAR_UNLOCKED; \ - } while (0) - -#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ - do { \ - assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ - } while (0) - -static inline int _nothing_conv_real_list_after_start( - typval_T *const tv, MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - assert(tv != NULL); - tv->v_lock = VAR_UNLOCKED; - if (tv->vval.v_list->lv_refcount > 1) { - tv->vval.v_list->lv_refcount--; - tv->vval.v_list = NULL; - mpsv->data.l.li = NULL; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) - -#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ - do { \ - if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) - -static inline void _nothing_conv_list_end(typval_T *const tv) - FUNC_ATTR_ALWAYS_INLINE -{ - if (tv == NULL) { - return; - } - assert(tv->v_type == VAR_LIST); - list_T *const list = tv->vval.v_list; - list_unref(list); - tv->vval.v_list = NULL; -} -#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) - -static inline int _nothing_conv_real_dict_after_start( - typval_T *const tv, dict_T **const dictp, const void *const nodictvar, - MPConvStackVal *const mpsv) - FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv != NULL) { - tv->v_lock = VAR_UNLOCKED; - } - if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { - (*dictp)->dv_refcount--; - *dictp = NULL; - mpsv->data.d.todo = 0; - return OK; - } - return NOTDONE; -} -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) - -#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ - do { \ - if (_nothing_conv_real_dict_after_start( \ - tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ - &mpsv) != NOTDONE) { \ - goto typval_encode_stop_converting_one_item; \ - } \ - } while (0) - -#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) -#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) - -static inline void _nothing_conv_dict_end(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) - FUNC_ATTR_ALWAYS_INLINE -{ - if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); - *dictp = NULL; - } -} -#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ - _nothing_conv_dict_end(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) - -#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) - -#define TYPVAL_ENCODE_SCOPE static -#define TYPVAL_ENCODE_NAME nothing -#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 -#undef TYPVAL_ENCODE_FIRST_ARG_NAME - -#undef TYPVAL_ENCODE_ALLOW_SPECIALS -#undef TYPVAL_ENCODE_CONV_NIL -#undef TYPVAL_ENCODE_CONV_BOOL -#undef TYPVAL_ENCODE_CONV_NUMBER -#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER -#undef TYPVAL_ENCODE_CONV_FLOAT -#undef TYPVAL_ENCODE_CONV_STRING -#undef TYPVAL_ENCODE_CONV_STR_STRING -#undef TYPVAL_ENCODE_CONV_EXT_STRING -#undef TYPVAL_ENCODE_CONV_FUNC_START -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS -#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF -#undef TYPVAL_ENCODE_CONV_FUNC_END -#undef TYPVAL_ENCODE_CONV_EMPTY_LIST -#undef TYPVAL_ENCODE_CONV_EMPTY_DICT -#undef TYPVAL_ENCODE_CONV_LIST_START -#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START -#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_LIST_END -#undef TYPVAL_ENCODE_CONV_DICT_START -#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START -#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK -#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY -#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS -#undef TYPVAL_ENCODE_CONV_DICT_END -#undef TYPVAL_ENCODE_CONV_RECURSE - -/// Free memory for a variable value and set the value to NULL or 0 -/// -/// @param[in,out] varp Value to free. -void clear_tv(typval_T *varp) -{ - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "clear_tv argument"); - (void)evn_ret; - assert(evn_ret == OK); - } -} - /* * Set the value of a variable to NULL without freeing items. */ @@ -19932,69 +19037,25 @@ static void init_tv(typval_T *varp) memset(varp, 0, sizeof(typval_T)); } -/// Check that given value is a number or string +// TODO(ZyX-I): move to eval/typval + +/// Get the number value of a variable /// -/// Error messages are compatible with get_tv_number() previously used for the -/// same purpose in buf*() functions. Special values are not accepted (previous -/// behaviour: silently fail to find buffer). +/// @note Use get_tv_number_chk() if you need to determine whether there was an +/// error. /// -/// @param[in] tv Value to check. +/// @param[in] varp Variable to get value from. /// -/// @return true if everything is OK, false otherwise. -bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE -{ - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } - } - assert(false); - return false; -} - -/* - * Get the number value of a variable. - * If it is a String variable, uses vim_str2nr(). - * For incompatible types, return 0. - * get_tv_number_chk() is similar to get_tv_number(), but informs the - * caller of incompatible types: it sets *denote to TRUE if "denote" - * is not NULL or returns -1 otherwise. - */ -long get_tv_number(typval_T *varp) +/// @return Number value: vim_str2nr() output for VAR_STRING variables, value +/// for VAR_NUMBER variables, -1 for other types. +varnumber_T get_tv_number(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - int error = FALSE; - - return get_tv_number_chk(varp, &error); /* return 0L on error */ + bool error = false; + return get_tv_number_chk(varp, &error); } -long get_tv_number_chk(typval_T *varp, int *denote) +varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { long n = 0L; @@ -20087,7 +19148,7 @@ static linenr_T get_tv_lnum(typval_T *argvars) rettv.v_type = VAR_NUMBER; f_line(argvars, &rettv, NULL); lnum = rettv.vval.v_number; - clear_tv(&rettv); + tv_clear(&rettv); } return lnum; } @@ -20107,35 +19168,41 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) return get_tv_number_chk(&argvars[0], NULL); } -/* - * Get the string value of a variable. - * If it is a Number variable, the number is converted into a string. - * get_tv_string() uses a single, static buffer. YOU CAN ONLY USE IT ONCE! - * get_tv_string_buf() uses a given buffer. - * If the String variable has never been set, return an empty string. - * Never returns NULL; - * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return - * NULL on error. - */ -static char_u *get_tv_string(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +// TODO(ZyX-I): move to eval/typval + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use get_tv_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +char_u *get_tv_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); } -static char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *res = get_tv_string_buf_chk(varp, buf); + char_u *const res = get_tv_string_buf_chk(varp, buf); return res != NULL ? res : (char_u *)""; } /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { static char_u mybuf[NUMBUFLEN]; @@ -20143,7 +19210,7 @@ char_u *get_tv_string_chk(const typval_T *varp) } char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (varp->v_type) { case VAR_NUMBER: @@ -20438,7 +19505,7 @@ void new_script_vars(scid_T id) * Initialize dictionary "dict" as a scope and set variable "dict_var" to * point to it. */ -void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) +void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); dict->dv_lock = VAR_UNLOCKED; @@ -20494,7 +19561,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // later. v = HI2DI(hi); if (free_val) { - clear_tv(&v->di_tv); + tv_clear(&v->di_tv); } if (v->di_flags & DI_FLAGS_ALLOC) { xfree(v); @@ -20514,7 +19581,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) dictitem_T *di = HI2DI(hi); hash_remove(ht, hi); - clear_tv(&di->di_tv); + tv_clear(&di->di_tv); xfree(di); } @@ -20652,11 +19719,11 @@ set_var ( if (watched) { copy_tv(&v->di_tv, &oldtv); } - clear_tv(&v->di_tv); - } else { /* add a new variable */ - /* Can't add "v:" variable. */ + tv_clear(&v->di_tv); + } else { // Add a new variable. + // Can't add "v:" variable. if (ht == &vimvarht) { - EMSG2(_(e_illvar), name); + emsgf(_(e_illvar), name); return; } @@ -20686,7 +19753,7 @@ set_var ( dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); - clear_tv(&oldtv); + tv_clear(&oldtv); } } } @@ -20934,7 +20001,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_list = from->vval.v_list->lv_copylist; ++to->vval.v_list->lv_refcount; } else { - to->vval.v_list = list_copy(conv, from->vval.v_list, deep, copyID); + to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } if (to->vval.v_list == NULL) ret = FAIL; @@ -21036,7 +20103,7 @@ void ex_echo(exarg_T *eap) } xfree(tofree); } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } eap->nextcmd = check_nextcmd(arg); @@ -21111,7 +20178,7 @@ void ex_execute(exarg_T *eap) ga.ga_len += len; } - clear_tv(&rettv); + tv_clear(&rettv); arg = skipwhite(arg); } @@ -21670,9 +20737,10 @@ void ex_function(exarg_T *eap) xfree(fp); goto erret; } - } else - /* overwrite existing dict entry */ - clear_tv(&fudi.fd_di->di_tv); + } else { + // Overwrite existing dict entry. + tv_clear(&fudi.fd_di->di_tv); + } fudi.fd_di->di_tv.v_type = VAR_FUNC; fudi.fd_di->di_tv.v_lock = 0; fudi.fd_di->di_tv.vval.v_string = vim_strsave(name); @@ -21733,7 +20801,7 @@ ret_free: /// Advances "pp" to just after the function name (if no error). /// /// @return the function name in allocated memory, or NULL for failure. -char_u * +static char_u * trans_function_name( char_u **pp, int skip, /* only find the end, don't evaluate */ @@ -22720,9 +21788,9 @@ call_user_func( // Init l: variables. init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); if (selfdict != NULL) { - /* Set l:self to "selfdict". Use "name" to avoid a warning from - * some compiler that checks the destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + // Set l:self to "selfdict". Use "name" to avoid a warning from + // some compiler that checks the destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "self"); @@ -22741,11 +21809,11 @@ call_user_func( * Set a:000 to a list with room for the "..." arguments. */ init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", - (varnumber_T)(argcount - fp->uf_args.ga_len)); - /* Use "name" to avoid a warning from some compiler that checks the - * destination size. */ - v = &fc->fixvar[fixvar_idx++].var; + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + (varnumber_T)(argcount - fp->uf_args.ga_len)); + // Use "name" to avoid a warning from some compiler that checks the + // destination size. + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; #ifndef __clang_analyzer__ name = v->di_key; STRCPY(name, "000"); @@ -22762,10 +21830,10 @@ call_user_func( // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. // Set a:N to the "..." arguments. - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline", - (varnumber_T)firstline); - add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline", - (varnumber_T)lastline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "firstline", (varnumber_T)firstline); + add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + "lastline", (varnumber_T)lastline); for (int i = 0; i < argcount; i++) { bool addlocal = false; @@ -22782,7 +21850,7 @@ call_user_func( name = numbuf; } if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) { - v = &fc->fixvar[fixvar_idx++].var; + v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + STRLEN(name)); @@ -22805,7 +21873,7 @@ call_user_func( } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - list_append(&fc->l_varlist, &fc->l_listitems[ai]); + tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); fc->l_listitems[ai].li_tv = argvars[i]; fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; } @@ -22913,7 +21981,7 @@ call_user_func( // when the function was aborted because of an error, return -1 if ((did_emsg && (fp->uf_flags & FC_ABORT)) || rettv->v_type == VAR_UNKNOWN) { - clear_tv(rettv); + tv_clear(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; } @@ -23109,7 +22177,7 @@ free_funccal ( // Free the a:000 variables if they were allocated. if (free_val) { for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - clear_tv(&li->li_tv); + tv_clear(&li->li_tv); } } @@ -23152,10 +22220,11 @@ void ex_return(exarg_T *eap) eap->nextcmd = NULL; if ((*arg != NUL && *arg != '|' && *arg != '\n') && eval0(arg, &rettv, &eap->nextcmd, !eap->skip) != FAIL) { - if (!eap->skip) - returning = do_return(eap, FALSE, TRUE, &rettv); - else - clear_tv(&rettv); + if (!eap->skip) { + returning = do_return(eap, false, true, &rettv); + } else { + tv_clear(&rettv); + } } /* It's safer to return also on error. */ else if (!eap->skip) { @@ -23242,7 +22311,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) * a return immediately after reanimation, the value is already * there. */ if (!reanimate && rettv != NULL) { - clear_tv(current_funccal->rettv); + tv_clear(current_funccal->rettv); *current_funccal->rettv = *(typval_T *)rettv; if (!is_cmd) xfree(rettv); @@ -23652,12 +22721,12 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char_u *p = list_find_str(l, nr); + char *p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = expand_env_save(p); - eap->arg = p; + p = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)p; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); xfree(p); @@ -24148,7 +23217,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, JobEvent event_data; event_data.received = NULL; if (buf) { - event_data.received = list_alloc(); + event_data.received = tv_list_alloc(); char *ptr = buf; size_t remaining = count; size_t off = 0; @@ -24156,7 +23225,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, while (off < remaining) { // append the line if (ptr[off] == NL) { - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); size_t skip = off + 1; ptr += skip; remaining -= skip; @@ -24169,7 +23238,7 @@ static inline void process_job_event(TerminalJobData *data, Callback *callback, } off++; } - list_append_string(event_data.received, (uint8_t *)ptr, off); + tv_list_append_string(event_data.received, ptr, off); } else { event_data.status = status; } @@ -24318,7 +23387,7 @@ static void on_job_event(JobEvent *ev) typval_T rettv; init_tv(&rettv); callback_call(ev->callback, 3, argv, &rettv); - clear_tv(&rettv); + tv_clear(&rettv); } static TerminalJobData *find_job(uint64_t id) @@ -24341,8 +23410,8 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = list_alloc(); - list_append_string(args, argvars[0].vval.v_string, -1); + list_T *args = tv_list_alloc(); + tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } @@ -24387,7 +23456,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) NULL, NULL); - list_unref(arguments); + tv_list_unref(arguments); // Restore caller scope information restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; @@ -24479,12 +23548,12 @@ static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, watcher->busy = true; callback_call(&watcher->callback, 3, argv, &rettv); watcher->busy = false; - clear_tv(&rettv); + tv_clear(&rettv); } } for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - clear_tv(argv + i); + tv_clear(argv + i); } } diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 43e9f76c0f..1d30f51f55 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -2,7 +2,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" @@ -118,18 +118,18 @@ static inline int json_decoder_pop(ValuesStackItem obj, if (last_container.container.vval.v_list->lv_len != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = listitem_alloc(); + listitem_T *obj_li = tv_list_item_alloc(); obj_li->li_tv = obj.val; - list_append(last_container.container.vval.v_list, obj_li); + tv_list_append(last_container.container.vval.v_list, obj_li); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } ValuesStackItem key = kv_pop(*stack); @@ -139,33 +139,33 @@ static inline int json_decoder_pop(ValuesStackItem obj, || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); - clear_tv(&key.val); + tv_clear(&key.val); if (dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = list_alloc(); - list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(last_container.special_val, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv = key.val; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv = obj.val; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); } } else { // Object with key only if (!obj.is_special_string && obj.val.v_type != VAR_STRING) { EMSG2(_("E474: Expected string key: %s"), *pp); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } else if (!obj.didcomma && (last_container.special_val == NULL && (DICT_LEN(last_container.container.vval.v_dict) != 0))) { EMSG2(_("E474: Expected comma before dictionary key: %s"), val_location); - clear_tv(&obj.val); + tv_clear(&obj.val); return FAIL; } // Handle empty key and key represented as special dictionary @@ -175,14 +175,14 @@ static inline int json_decoder_pop(ValuesStackItem obj, || *obj.val.vval.v_string == NUL || dict_find(last_container.container.vval.v_dict, obj.val.vval.v_string, -1))) { - clear_tv(&obj.val); + tv_clear(&obj.val); // Restart (void) kv_pop(*container_stack); ValuesStackItem last_container_val = kv_A(*stack, last_container.stack_index); while (kv_size(*stack) > last_container.stack_index) { - clear_tv(&(kv_pop(*stack).val)); + tv_clear(&(kv_pop(*stack).val)); } *pp = last_container.s; *didcomma = last_container_val.didcomma; @@ -430,7 +430,7 @@ static inline int parse_json_string(vimconv_T *const conv, } if (hasnul) { typval_T obj; - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(&obj, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -439,7 +439,7 @@ static inline int parse_json_string(vimconv_T *const conv, })); if (encode_list_write((void *) list, str, (size_t) (str_end - str)) == -1) { - clear_tv(&obj); + tv_clear(&obj); goto parse_json_string_fail; } xfree(str); @@ -806,7 +806,7 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); list->lv_refcount++; typval_T tv = { .v_type = VAR_LIST, @@ -827,7 +827,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = list_alloc(); + val_list = tv_list_alloc(); val_list->lv_refcount++; create_special_dict(&tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -887,7 +887,7 @@ json_decode_string_after_cycle: json_decode_string_fail: ret = FAIL; while (kv_size(stack)) { - clear_tv(&(kv_pop(stack).val)); + tv_clear(&(kv_pop(stack).val)); } json_decode_string_ret: kv_destroy(stack); @@ -933,7 +933,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, @@ -941,10 +941,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, })); uint64_t n = mobj.via.u64; - list_append_number(list, 1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + tv_list_append_number(list, 1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -956,18 +956,18 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, })); - uint64_t n = -((uint64_t) mobj.via.i64); - list_append_number(list, -1); - list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); - list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); - list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + uint64_t n = -((uint64_t)mobj.via.i64); + tv_list_append_number(list, -1); + tv_list_append_number(list, (varnumber_T)((n >> 62) & 0x3)); + tv_list_append_number(list, (varnumber_T)((n >> 31) & 0x7FFFFFFF)); + tv_list_append_number(list, (varnumber_T)(n & 0x7FFFFFFF)); } break; } @@ -980,7 +980,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_STR: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -1002,7 +1002,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) }; break; } - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPBinary, ((typval_T) { .v_type = VAR_LIST, @@ -1016,7 +1016,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; *rettv = (typval_T) { .v_type = VAR_LIST, @@ -1024,9 +1024,9 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = listitem_alloc(); + listitem_T *const li = tv_list_item_alloc(); li->li_tv.v_type = VAR_UNKNOWN; - list_append(list, li); + tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { return FAIL; } @@ -1057,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) di->di_tv.v_type = VAR_UNKNOWN; if (dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map - clear_tv(rettv); + tv_clear(rettv); xfree(di); goto msgpack_to_vim_generic_map; } @@ -1067,7 +1067,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; create_special_dict(rettv, kMPMap, ((typval_T) { .v_type = VAR_LIST, @@ -1075,14 +1075,14 @@ msgpack_to_vim_generic_map: {} .vval = { .v_list = list }, })); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = list_alloc(); - list_append_list(list, kv_pair); - listitem_T *const key_li = listitem_alloc(); + list_T *const kv_pair = tv_list_alloc(); + tv_list_append_list(list, kv_pair); + listitem_T *const key_li = tv_list_item_alloc(); key_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, key_li); - listitem_T *const val_li = listitem_alloc(); + tv_list_append(kv_pair, key_li); + listitem_T *const val_li = tv_list_item_alloc(); val_li->li_tv.v_type = VAR_UNKNOWN; - list_append(kv_pair, val_li); + tv_list_append(kv_pair, val_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { return FAIL; } @@ -1093,11 +1093,11 @@ msgpack_to_vim_generic_map: {} break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = list_alloc(); + list_T *const list = tv_list_alloc(); list->lv_refcount++; - list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = list_alloc(); - list_append_list(list, ext_val_list); + tv_list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = tv_list_alloc(); + tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h index 5c25a64f7a..c8e7a189e3 100644 --- a/src/nvim/eval/decode.h +++ b/src/nvim/eval/decode.h @@ -5,7 +5,7 @@ #include -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/decode.h.generated.h" diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ee66b7cf09..1416806ca6 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -13,7 +13,7 @@ #include "nvim/eval/encode.h" #include "nvim/buffer_defs.h" // vimconv_T #include "nvim/eval.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/garray.h" #include "nvim/mbyte.h" #include "nvim/message.h" @@ -45,7 +45,8 @@ const char *const encode_special_var_names[] = { #endif /// Msgpack callback for writing to readfile()-style list -int encode_list_write(void *data, const char *buf, size_t len) +int encode_list_write(void *const data, const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ARG(1) { if (len == 0) { return 0; @@ -80,11 +81,11 @@ int encode_list_write(void *data, const char *buf, size_t len) str = xmemdupz(line_start, line_length); memchrsub(str, NUL, NL, line_length); } - list_append_allocated_string(list, str); + tv_list_append_allocated_string(list, str); line_end++; } if (line_end == end) { - list_append_allocated_string(list, NULL); + tv_list_append_allocated_string(list, NULL); } return 0; } diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c new file mode 100644 index 0000000000..ab48ace400 --- /dev/null +++ b/src/nvim/eval/executor.c @@ -0,0 +1,114 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/executor.h" +#include "nvim/eval.h" +#include "nvim/message.h" +#include "nvim/vim.h" +#include "nvim/globals.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.c.generated.h" +#endif + +static char *e_letwrong = N_("E734: Wrong variable type for %s="); + +char *e_listidx = N_("E684: list index out of range: %" PRId64); + +/// Hanle tv1 += tv2, -=, .= +/// +/// @param[in,out] tv1 First operand, modified typval. +/// @param[in] tv2 Second operand. +/// @param[in] op Used operator. +/// +/// @return OK or FAIL. +int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, + const char *const op) + FUNC_ATTR_NONNULL_ALL +{ + // Can't do anything with a Funcref, a Dict or special value on the right. + if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) { + switch (tv1->v_type) { + case VAR_DICT: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_LIST: { + if (*op != '+' || tv2->v_type != VAR_LIST) { + break; + } + // List += List + if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) { + tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); + } + return OK; + } + case VAR_NUMBER: + case VAR_STRING: { + if (tv2->v_type == VAR_LIST) { + break; + } + if (*op == '+' || *op == '-') { + // nr += nr or nr -= nr + varnumber_T n = get_tv_number(tv1); + if (tv2->v_type == VAR_FLOAT) { + float_T f = n; + + if (*op == '+') { + f += tv2->vval.v_float; + } else { + f -= tv2->vval.v_float; + } + tv_clear(tv1); + tv1->v_type = VAR_FLOAT; + tv1->vval.v_float = f; + } else { + if (*op == '+') { + n += get_tv_number(tv2); + } else { + n -= get_tv_number(tv2); + } + tv_clear(tv1); + tv1->v_type = VAR_NUMBER; + tv1->vval.v_number = n; + } + } else { + // str .= str + if (tv2->v_type == VAR_FLOAT) { + break; + } + char *s = (char *)get_tv_string(tv1); + char numbuf[NUMBUFLEN]; + s = (char *)concat_str((char_u *)s, + get_tv_string_buf(tv2, (char_u *)numbuf)); + tv_clear(tv1); + tv1->v_type = VAR_STRING; + tv1->vval.v_string = (char_u *)s; + } + return OK; + } + case VAR_FLOAT: { + if (*op == '.' || (tv2->v_type != VAR_FLOAT + && tv2->v_type != VAR_NUMBER + && tv2->v_type != VAR_STRING)) { + break; + } + const float_T f = (tv2->v_type == VAR_FLOAT + ? tv2->vval.v_float + : get_tv_number(tv2)); + if (*op == '+') { + tv1->vval.v_float += f; + } else { + tv1->vval.v_float -= f; + } + return OK; + } + case VAR_UNKNOWN: { + assert(false); + } + } + } + + EMSG2(_(e_letwrong), op); + return FAIL; +} diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h new file mode 100644 index 0000000000..19e2a75914 --- /dev/null +++ b/src/nvim/eval/executor.h @@ -0,0 +1,9 @@ +#ifndef NVIM_EVAL_EXECUTOR_H +#define NVIM_EVAL_EXECUTOR_H + +extern char *e_listidx; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/executor.h.generated.h" +#endif +#endif // NVIM_EVAL_EXECUTOR_H diff --git a/src/nvim/eval/gc.c b/src/nvim/eval/gc.c new file mode 100644 index 0000000000..5ce52ddd70 --- /dev/null +++ b/src/nvim/eval/gc.c @@ -0,0 +1,11 @@ +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.c.generated.h" +#endif + +/// Head of list of all dictionaries +dict_T *gc_first_dict = NULL; +/// Head of list of all lists +list_T *gc_first_list = NULL; diff --git a/src/nvim/eval/gc.h b/src/nvim/eval/gc.h new file mode 100644 index 0000000000..c2e862e469 --- /dev/null +++ b/src/nvim/eval/gc.h @@ -0,0 +1,12 @@ +#ifndef NVIM_EVAL_GC_H +#define NVIM_EVAL_GC_H + +#include "nvim/eval/typval.h" + +extern dict_T *gc_first_dict; +extern list_T *gc_first_list; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/gc.h.generated.h" +#endif +#endif // NVIM_EVAL_GC_H diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c new file mode 100644 index 0000000000..7726e106a1 --- /dev/null +++ b/src/nvim/eval/typval.c @@ -0,0 +1,1171 @@ +#include +#include +#include + +#include "nvim/eval/typval.h" +#include "nvim/eval/gc.h" +#include "nvim/eval/executor.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval_encode.h" +#include "nvim/eval.h" +#include "nvim/types.h" +#include "nvim/assert.h" +#include "nvim/memory.h" +#include "nvim/globals.h" +// TODO(ZyX-I): Move line_breakcheck out of misc1 +#include "nvim/misc1.h" // For line_breakcheck + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.c.generated.h" +#endif + +bool tv_in_free_unref_items = false; + +// TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead + +#define DICT_MAXNEST 100 + +const char *const tv_empty_string = ""; + +//{{{1 Lists +//{{{2 List item + +/// Allocate a list item +/// +/// @warning Allocated item is not initialized, do not forget to initialize it +/// and specifically set lv_lock. +/// +/// @return [allocated] new list item. +listitem_T *tv_list_item_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + return xmalloc(sizeof(listitem_T)); +} + +/// Free a list item +/// +/// Also clears the value. Does not touch watchers. +/// +/// @param[out] item Item to free. +void tv_list_item_free(listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->li_tv); + xfree(item); +} + +/// Remove a list item from a List and free it +/// +/// Also clears the value. +/// +/// @param[out] l List to remove item from. +/// @param[in,out] item Item to remove. +void tv_list_item_remove(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_list_remove_items(l, item, item); + tv_list_item_free(item); +} + +//{{{2 List watchers + +/// Add a watcher to a list +/// +/// @param[out] l List to add watcher to. +/// @param[in] lw Watcher to add. +void tv_list_watch_add(list_T *const l, listwatch_T *const lw) + FUNC_ATTR_NONNULL_ALL +{ + lw->lw_next = l->lv_watch; + l->lv_watch = lw; +} + +/// Remove a watcher from a list +/// +/// Does not give a warning if watcher was not found. +/// +/// @param[out] l List to remove watcher from. +/// @param[in] lwrem Watcher to remove. +void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem) + FUNC_ATTR_NONNULL_ALL +{ + listwatch_T **lwp = &l->lv_watch; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw == lwrem) { + *lwp = lw->lw_next; + break; + } + lwp = &lw->lw_next; + } +} + +/// Advance watchers to the next item +/// +/// Used just before removing an item from a list. +/// +/// @param[out] l List from which item is removed. +/// @param[in] item List item being removed. +void tv_list_watch_fix(list_T *const l, const listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + if (lw->lw_item == item) { + lw->lw_item = item->li_next; + } + } +} + +//{{{2 Lists +//{{{3 Alloc/free + +/// Allocate an empty list +/// +/// Caller should take care of the reference count. +/// +/// @return [allocated] new list. +list_T *tv_list_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC +{ + list_T *const list = xcalloc(1, sizeof(list_T)); + + // Prepend the list to the list of lists for garbage collection. + if (gc_first_list != NULL) { + gc_first_list->lv_used_prev = list; + } + list->lv_used_prev = NULL; + list->lv_used_next = gc_first_list; + gc_first_list = list; + return list; +} + +/// Free items contained in a list +/// +/// @param[in,out] l List to clear. +void tv_list_free_contents(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { + // Remove the item before deleting it. + l->lv_first = item->li_next; + tv_clear(&item->li_tv); + xfree(item); + } + l->lv_len = 0; + l->lv_idx_item = NULL; + for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { + lw->lw_item = NULL; + } +} + +/// Free a list itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] l List to free. +void tv_list_free_list(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the list from the list of lists for garbage collection. + if (l->lv_used_prev == NULL) { + gc_first_list = l->lv_used_next; + } else { + l->lv_used_prev->lv_used_next = l->lv_used_next; + } + if (l->lv_used_next != NULL) { + l->lv_used_next->lv_used_prev = l->lv_used_prev; + } + + xfree(l); +} + +/// Free a list, including all items it points to +/// +/// Ignores the reference count. Does not do anything if +/// tv_in_free_unref_items is true. +/// +/// @param[in,out] l List to free. +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); + } +} + +/// Unreference a list +/// +/// Decrements the reference count and frees when it becomes zero or less. +/// +/// @param[in,out] l List to unreference. +void tv_list_unref(list_T *const l) +{ + if (l != NULL && --l->lv_refcount <= 0) { + tv_list_free(l); + } +} + +//{{{3 Add/remove + +/// Remove items "item" to "item2" from list "l". +/// +/// @warning Does not free the listitem or the value! +/// +/// @param[out] l List to remove from. +/// @param[in] item First item to remove. +/// @param[in] item2 Last item to remove. +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) +{ + // notify watchers + for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + l->lv_len--; + tv_list_watch_fix(l, ip); + if (ip == item2) { + break; + } + } + + if (item2->li_next == NULL) { + l->lv_last = item->li_prev; + } else { + item2->li_next->li_prev = item->li_prev; + } + if (item->li_prev == NULL) { + l->lv_first = item2->li_next; + } else { + item->li_prev->li_next = item2->li_next; + } + l->lv_idx_item = NULL; +} + +/// Insert list item +/// +/// @param[out] l List to insert to. +/// @param[in,out] ni Item to insert. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert(list_T *const l, listitem_T *const ni, + listitem_T *const item) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + if (item == NULL) { + // Append new item at end of list. + tv_list_append(l, ni); + } else { + // Insert new item before existing item. + ni->li_prev = item->li_prev; + ni->li_next = item; + if (item->li_prev == NULL) { + l->lv_first = ni; + l->lv_idx++; + } else { + item->li_prev->li_next = ni; + l->lv_idx_item = NULL; + } + item->li_prev = ni; + l->lv_len++; + } +} + +/// Insert VimL value into a list +/// +/// @param[out] l List to insert to. +/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// allocated listitem_T and inserted. +/// @param[in] item Item to insert before. If NULL, inserts at the end of the +/// list. +void tv_list_insert_tv(list_T *const l, typval_T *const tv, + listitem_T *const item) +{ + listitem_T *const ni = tv_list_item_alloc(); + + copy_tv(tv, &ni->li_tv); + tv_list_insert(l, ni, item); +} + +/// Append item to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] item Item to append. +void tv_list_append(list_T *const l, listitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_last == NULL) { + // empty list + l->lv_first = item; + l->lv_last = item; + item->li_prev = NULL; + } else { + l->lv_last->li_next = item; + item->li_prev = l->lv_last; + l->lv_last = item; + } + l->lv_len++; + item->li_next = NULL; +} + +/// Append VimL value to the end of list +/// +/// @param[out] l List to append to. +/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// allocated listitem_T. +void tv_list_append_tv(list_T *const l, typval_T *const tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + copy_tv(tv, &li->li_tv); + tv_list_append(l, li); +} + +/// Append a list to a list as one item +/// +/// @param[out] l List to append to. +/// @param[in,out] itemlist List to append. Reference count is increased. +void tv_list_append_list(list_T *const list, list_T *const itemlist) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = itemlist; + tv_list_append(list, li); + if (itemlist != NULL) { + itemlist->lv_refcount++; + } +} + +/// Append a dictionary to a list +/// +/// @param[out] l List to append to. +/// @param[in,out] dict Dictionary to append. Reference count is increased. +void tv_list_append_dict(list_T *const list, dict_T *const dict) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + li->li_tv.v_type = VAR_DICT; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_dict = dict; + tv_list_append(list, li); + if (dict != NULL) { + dict->dv_refcount++; + } +} + +/// Make a copy of "str" and append it as an item to list "l" +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +/// @param[in] len Length of the appended string. May be -1, in this +/// case string is considered to be usual zero-terminated +/// string or NULL “empty” string. +void tv_list_append_string(list_T *const l, const char *const str, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (str == NULL) { + assert(len == 0 || len == -1); + tv_list_append_allocated_string(l, NULL); + } else { + tv_list_append_allocated_string(l, (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))); + } +} + +/// Append given string to the list +/// +/// Unlike list_append_string this function does not copy the string. +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +void tv_list_append_allocated_string(list_T *const l, char *const str) + FUNC_ATTR_NONNULL_ARG(1) +{ + listitem_T *const li = tv_list_item_alloc(); + + tv_list_append(l, li); + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = (char_u *)str; +} + +/// Append number to the list +/// +/// @param[out] l List to append to. +/// @param[in] n Number to append. Will be recorded in the allocated +/// listitem_T. +void tv_list_append_number(list_T *const l, const varnumber_T n) +{ + listitem_T *const li = tv_list_item_alloc(); + li->li_tv.v_type = VAR_NUMBER; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_number = n; + tv_list_append(l, li); +} + +//{{{3 Operations on the whole list + +/// Make a copy of list +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original list to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied list. May be NULL in case original list is NULL or some +/// failure happens. The refcount of the new list is set to 1. +list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, + const bool deep, const int copyID) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (orig == NULL) { + return NULL; + } + + list_T *copy = tv_list_alloc(); + if (copyID != 0) { + // Do this before adding the items, because one of the items may + // refer back to this list. + orig->lv_copyID = copyID; + orig->lv_copylist = copy; + } + listitem_T *item; + for (item = orig->lv_first; item != NULL && !got_int; + item = item->li_next) { + listitem_T *const ni = tv_list_item_alloc(); + if (deep) { + if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + xfree(ni); + break; + } + } else { + copy_tv(&item->li_tv, &ni->li_tv); + } + tv_list_append(copy, ni); + } + copy->lv_refcount++; + if (item != NULL) { + tv_list_unref(copy); + copy = NULL; + } + + return copy; +} + +/// Extend first list with the second +/// +/// @param[out] l1 List to extend. +/// @param[in] l2 List to extend with. +/// @param[in] bef If not NULL, extends before this item. +void tv_list_extend(list_T *const l1, list_T *const l2, + listitem_T *const bef) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + int todo = l2->lv_len; + // We also quit the loop when we have inserted the original item count of + // the list, avoid a hang when we extend a list with itself. + for (listitem_T *item = l2->lv_first + ; item != NULL && --todo >= 0 + ; item = item->li_next) { + tv_list_insert_tv(l1, &item->li_tv, bef); + } +} + +/// Concatenate lists into a new list +/// +/// @param[in] l1 First list. +/// @param[in] l2 Second list. +/// @param[out] ret_tv Location where new list is saved. +/// +/// @return OK or FAIL. +int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + return FAIL; + } + + // make a copy of the first list. + list_T *const l = tv_list_copy(NULL, l1, false, 0); + if (l == NULL) { + return FAIL; + } + tv->v_type = VAR_LIST; + tv->vval.v_list = l; + + // append all items from the second list + tv_list_extend(l, l2, NULL); + return OK; +} + +typedef struct { + char_u *s; + char_u *tofree; +} Join; + +/// Join list into a string, helper function +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l List to join. +/// @param[in] sep Used separator. +/// @param[in] join_gap Garray to keep each list item string. +/// +/// @return OK in case of success, FAIL otherwise. +static int list_join_inner(garray_T *const gap, list_T *const l, + const char *const sep, garray_T *const join_gap) + FUNC_ATTR_NONNULL_ALL +{ + size_t sumlen = 0; + bool first = true; + listitem_T *item; + + // Stringify each item in the list. + for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + char *s; + size_t len; + s = encode_tv2echo(&item->li_tv, &len); + if (s == NULL) { + return FAIL; + } + + sumlen += len; + + Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); + p->tofree = p->s = (char_u *)s; + + line_breakcheck(); + } + + // Allocate result buffer with its total size, avoid re-allocation and + // multiple copy operations. Add 2 for a tailing ']' and NUL. + if (join_gap->ga_len >= 2) { + sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); + } + ga_grow(gap, (int)sumlen + 2); + + for (int i = 0; i < join_gap->ga_len && !got_int; i++) { + if (first) { + first = false; + } else { + ga_concat(gap, (const char_u *)sep); + } + const Join *const p = ((const Join *)join_gap->ga_data) + i; + + if (p->s != NULL) { + ga_concat(gap, p->s); + } + line_breakcheck(); + } + + return OK; +} + +/// Join list into a string using given separator +/// +/// @param[out] gap Garray where result will be saved. +/// @param[in] l Joined list. +/// @param[in] sep Separator. +/// +/// @return OK in case of success, FAIL otherwise. +int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) + FUNC_ATTR_NONNULL_ALL +{ + if (l->lv_len < 1) { + return OK; + } + + garray_T join_ga; + int retval; + + ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + retval = list_join_inner(gap, l, sep, &join_ga); + +#define FREE_JOIN_TOFREE(join) xfree((join)->tofree) + GA_DEEP_CLEAR(&join_ga, Join, FREE_JOIN_TOFREE); +#undef FREE_JOIN_TOFREE + + return retval; +} + +/// Chech whether two lists are equal +/// +/// @param[in] l1 First list to compare. +/// @param[in] l2 Second list to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l1 == NULL || l2 == NULL) { + // FIXME? compare empty list with NULL list equal + return false; + } + if (l1 == l2) { + return true; + } + if (tv_list_len(l1) != tv_list_len(l2)) { + return false; + } + + listitem_T *item1 = l1->lv_first; + listitem_T *item2 = l2->lv_first; + for (; item1 != NULL && item2 != NULL + ; item1 = item1->li_next, item2 = item2->li_next) { + if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + return false; + } + } + assert(item1 == NULL && item2 == NULL); + return true; +} + +//{{{3 Indexing/searching + +/// Locate item with a given index in a list and return it +/// +/// @param[in] l List to index. +/// @param[in] n Index. Negative index is counted from the end, -1 is the last +/// item. +/// +/// @return Item at the given index or NULL if `n` is out of range. +listitem_T *tv_list_find(list_T *const l, int n) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + STATIC_ASSERT(sizeof(n) == sizeof(l->lv_idx), + "n and lv_idx sizes do not match"); + if (l == NULL) { + return NULL; + } + + // Negative index is relative to the end. + if (n < 0) { + n = l->lv_len + n; + } + + // Check for index out of range. + if (n < 0 || n >= l->lv_len) { + return NULL; + } + + int idx; + listitem_T *item; + + // When there is a cached index may start search from there. + if (l->lv_idx_item != NULL) { + if (n < l->lv_idx / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else if (n > (l->lv_idx + l->lv_len) / 2) { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } else { + // Closest to the cached index. + item = l->lv_idx_item; + idx = l->lv_idx; + } + } else { + if (n < l->lv_len / 2) { + // Closest to the start of the list. + item = l->lv_first; + idx = 0; + } else { + // Closest to the end of the list. + item = l->lv_last; + idx = l->lv_len - 1; + } + } + + while (n > idx) { + // Search forward. + item = item->li_next; + idx++; + } + while (n < idx) { + // Search backward. + item = item->li_prev; + idx--; + } + + assert(idx == n); + // Cache the used index. + l->lv_idx = idx; + l->lv_idx_item = item; + + return item; +} + +/// Get list item l[n] as a number +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// @param[out] ret_error Location where 1 will be saved if index was not +/// found. May be NULL. If everything is OK, +/// `*ret_error` is not touched. +/// +/// @return Integer value at the given index or -1. +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const listitem_T *const li = tv_list_find(l, n); + if (li == NULL) { + if (ret_error != NULL) { + *ret_error = true; + } + return -1; + } + return get_tv_number_chk(&li->li_tv, ret_error); +} + +/// Get list item l[n - 1] as a string +/// +/// @param[in] l List to index. +/// @param[in] n Index in a list. +/// +/// @return [allocated] Copy of the list item string value. +char *tv_list_find_str(list_T *l, int n) + FUNC_ATTR_MALLOC +{ + const listitem_T *const li = tv_list_find(l, n - 1); + if (li == NULL) { + EMSGN(_(e_listidx), n); + return NULL; + } + return (char *)get_tv_string(&li->li_tv); +} + +/// Locate item in a list and return its index +/// +/// @param[in] l List to search. +/// @param[in] item Item to search for. +/// +/// @return Index of an item or -1 if item is not in the list. +long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + if (l == NULL) { + return -1; + } + long idx = 0; + listitem_T *li; + for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + idx++; + } + if (li == NULL) { + return -1; + } + return idx; +} +//{{{1 Generic typval operations +//{{{2 Init/alloc/clear +//{{{3 Alloc + +/// Allocate an empty list for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where list is saved. +/// +/// @return [allocated] pointer to the created list. +list_T *tv_list_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC +{ + list_T *const l = tv_list_alloc(); + ret_tv->vval.v_list = l; + ret_tv->v_type = VAR_LIST; + ret_tv->v_lock = VAR_UNLOCKED; + l->lv_refcount++; + return l; +} + +//{{{3 Clear +#define TYPVAL_ENCODE_ALLOW_SPECIALS false + +#define TYPVAL_ENCODE_CONV_NIL(tv) \ + do { \ + tv->vval.v_special = kSpecialVarFalse; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ + TYPVAL_ENCODE_CONV_NIL(tv) + +#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ + do { \ + (void)num; \ + tv->vval.v_number = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) + +#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ + do { \ + tv->vval.v_float = 0; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ + do { \ + xfree(buf); \ + tv->vval.v_string = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) + +#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) + +static inline int _nothing_conv_func_start(typval_T *const tv, + char_u *const fun) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) +{ + tv->v_lock = VAR_UNLOCKED; + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt_ = tv->vval.v_partial; + if (pt_ != NULL && pt_->pt_refcount > 1) { + pt_->pt_refcount--; + tv->vval.v_partial = NULL; + return OK; + } + } else { + func_unref(fun); + if ((const char *)fun != tv_empty_string) { + xfree(fun); + } + tv->vval.v_string = NULL; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ + do { \ + if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ + return OK; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) +#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) + +static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL +{ + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt = tv->vval.v_partial; + if (pt == NULL) { + return; + } + // Dictionary should already be freed by the time. + // If it was not freed then it is a part of the reference cycle. + assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); + pt->pt_dict = NULL; + // As well as all arguments. + pt->pt_argc = 0; + assert(pt->pt_refcount <= 1); + partial_unref(pt); + tv->vval.v_partial = NULL; + assert(tv->v_lock == VAR_UNLOCKED); + } +} +#define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) + +#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ + do { \ + tv_list_unref(tv->vval.v_list); \ + tv->vval.v_list = NULL; \ + tv->v_lock = VAR_UNLOCKED; \ + } while (0) + +#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ + do { \ + assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ + dict_unref((dict_T *)dict); \ + *((dict_T **)&dict) = NULL; \ + if (tv != NULL) { \ + ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ + } \ + } while (0) + +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(tv != NULL); + tv->v_lock = VAR_UNLOCKED; + if (tv->vval.v_list->lv_refcount > 1) { + tv->vval.v_list->lv_refcount--; + tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ + do { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) + +static inline void _nothing_conv_list_end(typval_T *const tv) + FUNC_ATTR_ALWAYS_INLINE +{ + if (tv == NULL) { + return; + } + assert(tv->v_type == VAR_LIST); + list_T *const list = tv->vval.v_list; + tv_list_unref(list); + tv->vval.v_list = NULL; +} +#define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) + +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } + if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { + (*dictp)->dv_refcount--; + *dictp = NULL; + mpsv->data.d.todo = 0; + return OK; + } + return NOTDONE; +} +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ + do { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ + goto typval_encode_stop_converting_one_item; \ + } \ + } while (0) + +#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) +#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) + +static inline void _nothing_conv_dict_end(typval_T *const tv, + dict_T **const dictp, + const void *const nodictvar) + FUNC_ATTR_ALWAYS_INLINE +{ + if ((const void *)dictp != nodictvar) { + dict_unref(*dictp); + *dictp = NULL; + } +} +#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ + _nothing_conv_dict_end(tv, (dict_T **)&dict, \ + (void *)&TYPVAL_ENCODE_NODICT_VAR) + +#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) + +#define TYPVAL_ENCODE_SCOPE static +#define TYPVAL_ENCODE_NAME nothing +#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 +#undef TYPVAL_ENCODE_FIRST_ARG_NAME + +#undef TYPVAL_ENCODE_ALLOW_SPECIALS +#undef TYPVAL_ENCODE_CONV_NIL +#undef TYPVAL_ENCODE_CONV_BOOL +#undef TYPVAL_ENCODE_CONV_NUMBER +#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER +#undef TYPVAL_ENCODE_CONV_FLOAT +#undef TYPVAL_ENCODE_CONV_STRING +#undef TYPVAL_ENCODE_CONV_STR_STRING +#undef TYPVAL_ENCODE_CONV_EXT_STRING +#undef TYPVAL_ENCODE_CONV_FUNC_START +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS +#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF +#undef TYPVAL_ENCODE_CONV_FUNC_END +#undef TYPVAL_ENCODE_CONV_EMPTY_LIST +#undef TYPVAL_ENCODE_CONV_EMPTY_DICT +#undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_LIST_END +#undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK +#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY +#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS +#undef TYPVAL_ENCODE_CONV_DICT_END +#undef TYPVAL_ENCODE_CONV_RECURSE + +/// Free memory for a variable value and set the value to NULL or 0 +/// +/// @param[in,out] varp Value to free. +void tv_clear(typval_T *varp) +{ + if (varp != NULL && varp->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + (void)evn_ret; + assert(evn_ret == OK); + } +} + +//{{{2 Locks + +/// Lock or unlock an item +/// +/// @param[out] tv Item to (un)lock. +/// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. +/// @param[in] lock True if it is needed to lock an item, false to unlock. +void tv_item_lock(typval_T *const tv, const int deep, const bool lock) +{ + // TODO(ZyX-I): Make this not recursive + static int recurse = 0; + + if (recurse >= DICT_MAXNEST) { + emsgf(_("E743: variable nested too deep for (un)lock")); + return; + } + if (deep == 0) { + return; + } + recurse++; + + // lock/unlock the item itself +#define CHANGE_LOCK(lock, var) \ + do { \ + var = ((VarLockStatus[]) { \ + [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ + [VAR_FIXED] = VAR_FIXED, \ + })[var]; \ + } while (0) + CHANGE_LOCK(lock, tv->v_lock); + + switch (tv->v_type) { + case VAR_LIST: { + list_T *const l = tv->vval.v_list; + if (l != NULL) { + CHANGE_LOCK(lock, l->lv_lock); + if (deep < 0 || deep > 1) { + // Recursive: lock/unlock the items the List contains. + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + tv_item_lock(&li->li_tv, deep - 1, lock); + } + } + } + break; + } + case VAR_DICT: { + dict_T *const d = tv->vval.v_dict; + if (d != NULL) { + CHANGE_LOCK(lock, d->dv_lock); + if (deep < 0 || deep > 1) { + // recursive: lock/unlock the items the List contains + int todo = (int)d->dv_hashtab.ht_used; + for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { + if (!HASHITEM_EMPTY(hi)) { + todo--; + tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); + } + } + } + } + break; + } + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_STRING: + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_SPECIAL: { + break; + } + case VAR_UNKNOWN: { + assert(false); + } + } +#undef CHANGE_LOCK + recurse--; +} + +/// Check whether VimL value is locked itself or refers to a locked container +/// +/// @param[in] tv Value to check. +/// +/// @return True if value is locked, false otherwise. +bool tv_islocked(const typval_T *const tv) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return ((tv->v_lock & VAR_LOCKED) + || (tv->v_type == VAR_LIST + && tv->vval.v_list != NULL + && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + || (tv->v_type == VAR_DICT + && tv->vval.v_dict != NULL + && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); +} + +//{{{2 Type checks + +/// Check that given value is a number or string +/// +/// Error messages are compatible with get_tv_number() previously used for the +/// same purpose in buf*() functions. Special values are not accepted (previous +/// behaviour: silently fail to find buffer). +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str_or_nr(const typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; + } + case VAR_FLOAT: { + EMSG(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + EMSG(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + EMSG(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + EMSG(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h new file mode 100644 index 0000000000..cf83904ffc --- /dev/null +++ b/src/nvim/eval/typval.h @@ -0,0 +1,285 @@ +#ifndef NVIM_EVAL_TYPVAL_H +#define NVIM_EVAL_TYPVAL_H + +#include +#include +#include + +#include "nvim/hashtab.h" +#include "nvim/garray.h" +#include "nvim/mbyte.h" +#include "nvim/lib/queue.h" +#include "nvim/profile.h" // for proftime_T +#include "nvim/pos.h" // for linenr_T + +/// Type used for VimL VAR_NUMBER values +typedef int varnumber_T; + +/// Type used for VimL VAR_FLOAT values +typedef double float_T; + +/// Maximal possible value of varnumber_T variable +#define VARNUMBER_MAX INT_MAX + +/// Mimimal possible value of varnumber_T variable +#define VARNUMBER_MIN INT_MIN + +/// %d printf format specifier for varnumber_T +#define PRIdVARNUMBER "d" + +typedef struct listvar_S list_T; +typedef struct dictvar_S dict_T; +typedef struct partial_S partial_T; + +/// Special variable values +typedef enum { + kSpecialVarFalse, ///< v:false + kSpecialVarTrue, ///< v:true + kSpecialVarNull, ///< v:null +} SpecialVarValue; + +/// Variable lock status for typval_T.v_lock +typedef enum { + VAR_UNLOCKED = 0, ///< Not locked. + VAR_LOCKED = 1, ///< User lock, can be unlocked. + VAR_FIXED = 2, ///< Locked forever. +} VarLockStatus; + +/// VimL variable types, for use in typval_T.v_type +typedef enum { + VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. + VAR_NUMBER, ///< Number, .v_number is used. + VAR_STRING, ///< String, .v_string is used. + VAR_FUNC, ///< Function reference, .v_string is used as function name. + VAR_LIST, ///< List, .v_list is used. + VAR_DICT, ///< Dictionary, .v_dict is used. + VAR_FLOAT, ///< Floating-point value, .v_float is used. + VAR_SPECIAL, ///< Special value (true, false, null), .v_special + ///< is used. + VAR_PARTIAL, ///< Partial, .v_partial is used. +} VarType; + +/// Structure that holds an internal variable value +typedef struct { + VarType v_type; ///< Variable type. + VarLockStatus v_lock; ///< Variable lock status. + union typval_vval_union { + varnumber_T v_number; ///< Number, for VAR_NUMBER. + SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. + float_T v_float; ///< Floating-point number, for VAR_FLOAT. + char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. + list_T *v_list; ///< List for VAR_LIST, can be NULL. + dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. + partial_T *v_partial; ///< Closure: function with args. + } vval; ///< Actual value. +} typval_T; + +/// Values for (struct dictvar_S).dv_scope +typedef enum { + VAR_NO_SCOPE = 0, ///< Not a scope dictionary. + VAR_SCOPE = 1, ///< Scope dictionary which requires prefix (a:, v:, …). + VAR_DEF_SCOPE = 2, ///< Scope dictionary which may be accessed without prefix + ///< (l:, g:). +} ScopeType; + +/// Structure to hold an item of a list +typedef struct listitem_S listitem_T; + +struct listitem_S { + listitem_T *li_next; ///< Next item in list. + listitem_T *li_prev; ///< Previous item in list. + typval_T li_tv; ///< Item value. +}; + +/// Structure used by those that are using an item in a list +typedef struct listwatch_S listwatch_T; + +struct listwatch_S { + listitem_T *lw_item; ///< Item being watched. + listwatch_T *lw_next; ///< Next watcher. +}; + +/// Structure to hold info about a list +struct listvar_S { + listitem_T *lv_first; ///< First item, NULL if none. + listitem_T *lv_last; ///< Last item, NULL if none. + int lv_refcount; ///< Reference count. + int lv_len; ///< Number of items. + listwatch_T *lv_watch; ///< First watcher, NULL if none. + int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. + listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". + int lv_copyID; ///< ID used by deepcopy(). + list_T *lv_copylist; ///< Copied list used by deepcopy(). + VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. + list_T *lv_used_next; ///< next list in used lists list. + list_T *lv_used_prev; ///< Previous list in used lists list. +}; + +// Static list with 10 items. Use init_static_list() to initialize. +typedef struct { + list_T sl_list; // must be first + listitem_T sl_items[10]; +} staticList10_T; + +// Structure to hold an item of a Dictionary. +// Also used for a variable. +// The key is copied into "di_key" to avoid an extra alloc/free for it. +struct dictitem_S { + typval_T di_tv; ///< type and value of the variable + char_u di_flags; ///< flags (only used for variable) + char_u di_key[1]; ///< key (actually longer!) +}; + +#define TV_DICTITEM_STRUCT(KEY_LEN) \ + struct { \ + typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ + uint8_t di_flags; /* Flags. */ \ + char_u di_key[KEY_LEN]; /* NUL. */ \ + } + +/// Structure to hold a scope dictionary +/// +/// @warning Must be compatible with dictitem_T. +/// +/// For use in find_var_in_ht to pretend that it found dictionary item when it +/// finds scope dictionary. +typedef TV_DICTITEM_STRUCT(1) ScopeDictDictItem; + +/// Structure to hold an item of a Dictionary +/// +/// @warning Must be compatible with ScopeDictDictItem. +/// +/// Also used for a variable. +typedef TV_DICTITEM_STRUCT() dictitem_T; + +/// Flags for dictitem_T.di_flags +typedef enum { + DI_FLAGS_RO = 1, ///< Read-only value + DI_FLAGS_RO_SBX = 2, ///< Value, read-only in the sandbox + DI_FLAGS_FIX = 4, ///< Fixed value: cannot be :unlet or remove()d. + DI_FLAGS_LOCK = 8, ///< Locked value. + DI_FLAGS_ALLOC = 16, ///< Separately allocated. +} DictItemFlags; + +/// Structure representing a Dictionary +struct dictvar_S { + VarLockStatus dv_lock; ///< Whole dictionary lock status. + ScopeType dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if + ///< dictionary represents a scope (i.e. g:, l: …). + int dv_refcount; ///< Reference count. + int dv_copyID; ///< ID used when recursivery traversing a value. + hashtab_T dv_hashtab; ///< Hashtab containing all items. + dict_T *dv_copydict; ///< Copied dict used by deepcopy(). + dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. + dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. + QUEUE watchers; ///< Dictionary key watchers set by user code. +}; + +/// Type used for script ID +typedef int scid_T; + +// Structure to hold info for a function that is currently being executed. +typedef struct funccall_S funccall_T; + +// Structure to hold info for a user function. +typedef struct ufunc ufunc_T; + +struct ufunc { + int uf_varargs; ///< variable nr of arguments + int uf_flags; + int uf_calls; ///< nr of active calls + bool uf_cleared; ///< func_clear() was already called + garray_T uf_args; ///< arguments + garray_T uf_lines; ///< function lines + int uf_profiling; ///< true when func is being profiled + // Profiling the function as a whole. + int uf_tm_count; ///< nr of calls + proftime_T uf_tm_total; ///< time spent in function + children + proftime_T uf_tm_self; ///< time spent in function itself + proftime_T uf_tm_children; ///< time spent in children this call + // Profiling the function per line. + int *uf_tml_count; ///< nr of times line was executed + proftime_T *uf_tml_total; ///< time spent in a line + children + proftime_T *uf_tml_self; ///< time spent in a line itself + proftime_T uf_tml_start; ///< start time for current line + proftime_T uf_tml_children; ///< time spent in children for this line + proftime_T uf_tml_wait; ///< start wait time for current line + int uf_tml_idx; ///< index of line being timed; -1 if none + int uf_tml_execed; ///< line being timed was executed + scid_T uf_script_ID; ///< ID of script where function was defined, + // used for s: variables + int uf_refcount; ///< reference count, see func_name_refcount() + funccall_T *uf_scoped; ///< l: local variables for closure + char_u uf_name[1]; ///< name of function (actually longer); can + // start with 123_ ( is K_SPECIAL + // KS_EXTRA KE_SNR) +}; + +/// Maximum number of function arguments +#define MAX_FUNC_ARGS 20 + +struct partial_S { + int pt_refcount; ///< Reference count. + char_u *pt_name; ///< Function name; when NULL use pt_func->name. + ufunc_T *pt_func; ///< Function pointer; when NULL lookup function with + ///< pt_name. + bool pt_auto; ///< When true the partial was created by using dict.member + ///< in handle_subscript(). + int pt_argc; ///< Number of arguments. + typval_T *pt_argv; ///< Arguments in allocated array. + dict_T *pt_dict; ///< Dict for "self". +}; + +/// Structure used for explicit stack while garbage collecting hash tables +typedef struct ht_stack_S { + hashtab_T *ht; + struct ht_stack_S *prev; +} ht_stack_T; + +/// Structure used for explicit stack while garbage collecting lists +typedef struct list_stack_S { + list_T *list; + struct list_stack_S *prev; +} list_stack_T; + +// In a hashtab item "hi_key" points to "di_key" in a dictitem. +// This avoids adding a pointer to the hashtab item. + +/// Convert a dictitem pointer to a hashitem key pointer +#define DI2HIKEY(di) ((di)->di_key) + +/// Convert a hashitem key pointer to a dictitem pointer +#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) + +/// Convert a hashitem value pointer to a dictitem pointer +#define HIVAL2DI(p) \ + ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) + +/// Convert a hashitem pointer to a dictitem pointer +#define HI2DI(hi) HIKEY2DI((hi)->hi_key) + +/// Get the number of items in a list +/// +/// @param[in] l List to check. +static inline long tv_list_len(list_T *const l) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (l == NULL) { + return 0; + } + return l->lv_len; +} + +/// Empty string +/// +/// Needed for hack which allows not allocating empty string and still not +/// crashing when freeing it. +extern const char *const tv_empty_string; + +/// Specifies that free_unref_items() function has (not) been entered +extern bool tv_in_free_unref_items; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval/typval.h.generated.h" +#endif +#endif // NVIM_EVAL_TYPVAL_H diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 4ff5589887..eb89a601ff 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -242,7 +242,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/eval/encode.h" #include "nvim/func_attr.h" #include "nvim/eval/typval_encode.h" diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 46145c5d03..3475f6d8b3 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -11,7 +11,7 @@ #include #include "nvim/lib/kvec.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/func_attr.h" /// Type of the stack entry diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h deleted file mode 100644 index 8f5e1a897d..0000000000 --- a/src/nvim/eval_defs.h +++ /dev/null @@ -1,286 +0,0 @@ -#ifndef NVIM_EVAL_DEFS_H -#define NVIM_EVAL_DEFS_H - -#include -#include -#include - -#include "nvim/hashtab.h" -#include "nvim/lib/queue.h" -#include "nvim/garray.h" // for garray_T -#include "nvim/profile.h" // for proftime_T -#include "nvim/pos.h" // for linenr_T - -typedef int varnumber_T; -typedef double float_T; - -#define VARNUMBER_MAX INT_MAX -#define VARNUMBER_MIN INT_MIN - -typedef struct listvar_S list_T; -typedef struct dictvar_S dict_T; -typedef struct partial_S partial_T; - -/// Special variable values -typedef enum { - kSpecialVarFalse, ///< v:false - kSpecialVarTrue, ///< v:true - kSpecialVarNull, ///< v:null -} SpecialVarValue; - -/// Variable lock status for typval_T.v_lock -typedef enum { - VAR_UNLOCKED = 0, ///< Not locked. - VAR_LOCKED = 1, ///< User lock, can be unlocked. - VAR_FIXED = 2, ///< Locked forever. -} VarLockStatus; - -/// VimL variable types, for use in typval_T.v_type -typedef enum { - VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. - VAR_NUMBER, ///< Number, .v_number is used. - VAR_STRING, ///< String, .v_string is used. - VAR_FUNC, ///< Function reference, .v_string is used as function name. - VAR_LIST, ///< List, .v_list is used. - VAR_DICT, ///< Dictionary, .v_dict is used. - VAR_FLOAT, ///< Floating-point value, .v_float is used. - VAR_SPECIAL, ///< Special value (true, false, null), .v_special - ///< is used. - VAR_PARTIAL, ///< Partial, .v_partial is used. -} VarType; - -/// Structure that holds an internal variable value -typedef struct { - VarType v_type; ///< Variable type. - VarLockStatus v_lock; ///< Variable lock status. - union typval_vval_union { - varnumber_T v_number; ///< Number, for VAR_NUMBER. - SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL. - float_T v_float; ///< Floating-point number, for VAR_FLOAT. - char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL. - list_T *v_list; ///< List for VAR_LIST, can be NULL. - dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL. - partial_T *v_partial; ///< Closure: function with args. - } vval; ///< Actual value. -} typval_T; - -/* Values for "dv_scope". */ -#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ -#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not - allowed to mask existing functions */ - -/* - * Structure to hold an item of a list: an internal variable without a name. - */ -typedef struct listitem_S listitem_T; - -struct listitem_S { - listitem_T *li_next; /* next item in list */ - listitem_T *li_prev; /* previous item in list */ - typval_T li_tv; /* type and value of the variable */ -}; - -/* - * Struct used by those that are using an item in a list. - */ -typedef struct listwatch_S listwatch_T; - -struct listwatch_S { - listitem_T *lw_item; /* item being watched */ - listwatch_T *lw_next; /* next watcher */ -}; - -/* - * Structure to hold info about a list. - */ -struct listvar_S { - listitem_T *lv_first; ///< First item, NULL if none. - listitem_T *lv_last; ///< Last item, NULL if none. - int lv_refcount; ///< Reference count. - int lv_len; ///< Number of items. - listwatch_T *lv_watch; ///< First watcher, NULL if none. - int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx]. - listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx". - int lv_copyID; ///< ID used by deepcopy(). - list_T *lv_copylist; ///< Copied list used by deepcopy(). - VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED. - list_T *lv_used_next; ///< next list in used lists list. - list_T *lv_used_prev; ///< Previous list in used lists list. -}; - -// Static list with 10 items. Use init_static_list() to initialize. -typedef struct { - list_T sl_list; // must be first - listitem_T sl_items[10]; -} staticList10_T; - -// Structure to hold an item of a Dictionary. -// Also used for a variable. -// The key is copied into "di_key" to avoid an extra alloc/free for it. -struct dictitem_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[1]; ///< key (actually longer!) -}; - -typedef struct dictitem_S dictitem_T; - -/// A dictitem with a 16 character key (plus NUL) -struct dictitem16_S { - typval_T di_tv; ///< type and value of the variable - char_u di_flags; ///< flags (only used for variable) - char_u di_key[17]; ///< key -}; - -typedef struct dictitem16_S dictitem16_T; - - -#define DI_FLAGS_RO 1 // "di_flags" value: read-only variable -#define DI_FLAGS_RO_SBX 2 // "di_flags" value: read-only in the sandbox -#define DI_FLAGS_FIX 4 // "di_flags" value: fixed: no :unlet or remove() -#define DI_FLAGS_LOCK 8 // "di_flags" value: locked variable -#define DI_FLAGS_ALLOC 16 // "di_flags" value: separately allocated - -/// Structure representing a Dictionary -struct dictvar_S { - VarLockStatus dv_lock; ///< Whole dictionary lock status. - char dv_scope; ///< Non-zero (#VAR_SCOPE, #VAR_DEF_SCOPE) if - ///< dictionary represents a scope (i.e. g:, l: …). - int dv_refcount; ///< Reference count. - int dv_copyID; ///< ID used when recursivery traversing a value. - hashtab_T dv_hashtab; ///< Hashtab containing all items. - dict_T *dv_copydict; ///< Copied dict used by deepcopy(). - dict_T *dv_used_next; ///< Next dictionary in used dictionaries list. - dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list. - QUEUE watchers; ///< Dictionary key watchers set by user code. -}; - -typedef int scid_T; // script ID -typedef struct funccall_S funccall_T; - -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - -struct ufunc { - int uf_varargs; ///< variable nr of arguments - int uf_flags; - int uf_calls; ///< nr of active calls - bool uf_cleared; ///< func_clear() was already called - garray_T uf_args; ///< arguments - garray_T uf_lines; ///< function lines - int uf_profiling; ///< true when func is being profiled - // Profiling the function as a whole. - int uf_tm_count; ///< nr of calls - proftime_T uf_tm_total; ///< time spent in function + children - proftime_T uf_tm_self; ///< time spent in function itself - proftime_T uf_tm_children; ///< time spent in children this call - // Profiling the function per line. - int *uf_tml_count; ///< nr of times line was executed - proftime_T *uf_tml_total; ///< time spent in a line + children - proftime_T *uf_tml_self; ///< time spent in a line itself - proftime_T uf_tml_start; ///< start time for current line - proftime_T uf_tml_children; ///< time spent in children for this line - proftime_T uf_tml_wait; ///< start wait time for current line - int uf_tml_idx; ///< index of line being timed; -1 if none - int uf_tml_execed; ///< line being timed was executed - scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables - int uf_refcount; ///< reference count, see func_name_refcount() - funccall_T *uf_scoped; ///< l: local variables for closure - char_u uf_name[1]; ///< name of function (actually longer); can - // start with 123_ ( is K_SPECIAL - // KS_EXTRA KE_SNR) -}; - -/// Maximum number of function arguments -#define MAX_FUNC_ARGS 20 -#define VAR_SHORT_LEN 20 // short variable name length -#define FIXVAR_CNT 12 // number of fixed variables - -// structure to hold info for a function that is currently being executed. -struct funccall_S { - ufunc_T *func; ///< function being called - int linenr; ///< next line to be executed - int returned; ///< ":return" used - struct { ///< fixed variables for arguments - dictitem_T var; ///< variable (without room for name) - char_u room[VAR_SHORT_LEN]; ///< room for the name - } fixvar[FIXVAR_CNT]; - dict_T l_vars; ///< l: local function variables - dictitem_T l_vars_var; ///< variable for l: scope - dict_T l_avars; ///< a: argument variables - dictitem_T l_avars_var; ///< variable for a: scope - list_T l_varlist; ///< list for a:000 - listitem_T l_listitems[MAX_FUNC_ARGS]; ///< listitems for a:000 - typval_T *rettv; ///< return value - linenr_T breakpoint; ///< next line with breakpoint or zero - int dbg_tick; ///< debug_tick when breakpoint was set - int level; ///< top nesting level of executed function - proftime_T prof_child; ///< time spent in a child - funccall_T *caller; ///< calling function or NULL - int fc_refcount; ///< number of user functions that reference - // this funccal - int fc_copyID; ///< for garbage collection - garray_T fc_funcs; ///< list of ufunc_T* which keep a reference - // to "func" -}; - -// structure used by trans_function_name() -typedef struct { - dict_T *fd_dict; ///< Dictionary used. - char_u *fd_newkey; ///< New key in "dict" in allocated memory. - dictitem_T *fd_di; ///< Dictionary item used. -} funcdict_T; - -struct partial_S { - int pt_refcount; ///< Reference count. - char_u *pt_name; ///< Function name; when NULL use pt_func->name. - ufunc_T *pt_func; ///< Function pointer; when NULL lookup function - ///< with pt_name. - bool pt_auto; ///< when true the partial was created for using - ///< dict.member in handle_subscript(). - int pt_argc; ///< Number of arguments. - typval_T *pt_argv; ///< Arguments in allocated array. - dict_T *pt_dict; ///< Dict for "self". -}; - -// structure used for explicit stack while garbage collecting hash tables -typedef struct ht_stack_S { - hashtab_T *ht; - struct ht_stack_S *prev; -} ht_stack_T; - -// structure used for explicit stack while garbage collecting lists -typedef struct list_stack_S { - list_T *list; - struct list_stack_S *prev; -} list_stack_T; - -// In a hashtab item "hi_key" points to "di_key" in a dictitem. -// This avoids adding a pointer to the hashtab item. - -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - -/// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) - -/// Type of assert_* check being performed -typedef enum -{ - ASSERT_EQUAL, - ASSERT_NOTEQUAL, - ASSERT_MATCH, - ASSERT_NOTMATCH, - ASSERT_INRANGE, - ASSERT_OTHER, -} assert_type_T; - -#endif // NVIM_EVAL_DEFS_H diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 678102daf6..151a4d375f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -2958,7 +2958,7 @@ void sub_set_replacement(SubReplacementString sub) { xfree(old_sub.sub); if (sub.additional_elements != old_sub.additional_elements) { - list_unref(old_sub.additional_elements); + tv_list_unref(old_sub.additional_elements); } old_sub = sub; } diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index 243b11255e..65bbd8a99e 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -4,8 +4,8 @@ #include #include "nvim/os/time.h" -#include "nvim/eval_defs.h" #include "nvim/pos.h" +#include "nvim/eval/typval.h" // flags for do_ecmd() #define ECMD_HIDE 0x01 // don't free the current buffer diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 9fc4ef2a02..e59a87b335 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3698,12 +3698,12 @@ static void script_host_execute(char *name, exarg_T *eap) uint8_t *script = script_get(eap, eap->arg); if (!eap->skip) { - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // script - list_append_string(args, script ? script : eap->arg, -1); + tv_list_append_string(args, (const char *)(script ? script : eap->arg), -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute", args); } @@ -3715,21 +3715,21 @@ static void script_host_execute_file(char *name, exarg_T *eap) uint8_t buffer[MAXPATHL]; vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = list_alloc(); + list_T *args = tv_list_alloc(); // filename - list_append_string(args, buffer, -1); + tv_list_append_string(args, (const char *)buffer, -1); // current range - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); (void)eval_call_provider(name, "execute_file", args); } static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = list_alloc(); - list_append_number(args, (int)eap->line1); - list_append_number(args, (int)eap->line2); - list_append_string(args, eap->arg, -1); + list_T *args = tv_list_alloc(); + tv_list_append_number(args, (int)eap->line1); + tv_list_append_number(args, (int)eap->line2); + tv_list_append_string(args, (const char *)eap->arg, -1); (void)eval_call_provider(name, "do_range", args); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d1557f9c82..dc99c0771d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8416,8 +8416,8 @@ eval_vars ( *usedlen = 1; return NULL; } - result = list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), + (long)i); if (result == NULL) { *errormsg = (char_u *)""; return NULL; diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 4bb6f97035..7a34a181e2 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,6 +1,8 @@ -/* - * ex_eval.c: functions for Ex command line for the +eval feature. - */ +// TODO(ZyX-I): move to eval/executor + +/// @file ex_eval.c +/// +/// Functions for Ex command line for the +eval feature. #include #include #include @@ -779,7 +781,6 @@ void report_discard_pending(int pending, void *value) */ void ex_if(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -800,6 +801,7 @@ void ex_if(exarg_T *eap) 1] & CSF_ACTIVE)); + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); if (!skip && !error) { @@ -844,7 +846,6 @@ void ex_endif(exarg_T *eap) */ void ex_else(exarg_T *eap) { - int error; int skip; int result; struct condstack *cstack = eap->cstack; @@ -901,6 +902,7 @@ void ex_else(exarg_T *eap) } if (eap->cmdidx == CMD_elseif) { + bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); /* When throwing error exceptions, we want to throw always the first * of several errors in a row. This is what actually happens when @@ -925,7 +927,7 @@ void ex_else(exarg_T *eap) */ void ex_while(exarg_T *eap) { - int error; + bool error; int skip; int result; struct condstack *cstack = eap->cstack; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8758a63bce..9851ed5396 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -4249,7 +4249,7 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); } - list_unref(retlist); + tv_list_unref(retlist); *file = ga.ga_data; *num_file = ga.ga_len; @@ -4545,7 +4545,7 @@ static inline void hist_free_entry(histentry_T *hisptr) FUNC_ATTR_NONNULL_ALL { xfree(hisptr->hisstr); - list_unref(hisptr->additional_elements); + tv_list_unref(hisptr->additional_elements); clear_hist_entry(hisptr); } @@ -4601,7 +4601,7 @@ in_history ( history[type][last_i] = history[type][i]; last_i = i; } - list_unref(list); + tv_list_unref(list); history[type][i].hisnum = ++hisnum[type]; history[type][i].hisstr = str; history[type][i].timestamp = os_time(); diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index 24eebdc303..5a1ca5213a 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -1,7 +1,7 @@ #ifndef NVIM_EX_GETLN_H #define NVIM_EX_GETLN_H -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" /* Values for nextwild() and ExpandOne(). See ExpandOne() for meaning. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index de79ee2469..d87407f099 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -12,6 +12,7 @@ #include "nvim/syntax_defs.h" #include "nvim/types.h" #include "nvim/event/loop.h" +#include "nvim/os/os_defs.h" #define IOSIZE (1024+1) // file I/O and sprintf buffer size @@ -21,16 +22,6 @@ # define MSG_BUF_CLEN (MSG_BUF_LEN / 6) // cell length (worst case: utf-8 // takes 6 bytes for one cell) -// Maximum length of a file path. Make it a bit long, to stay -// on the safe side. But not too long to put on the stack. -#ifndef MAXPATHL -# ifdef MAXPATHLEN -# define MAXPATHL MAXPATHLEN -# else -# define MAXPATHL 256 -# endif -#endif - #ifdef WIN32 # define _PATHSEPSTR "\\" #else @@ -1226,11 +1217,6 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ EXTERN int ignored; EXTERN char *ignoredp; -EXTERN bool in_free_unref_items INIT(= false); - -// Used for checking if local variables or arguments used in a lambda. -EXTERN int *eval_lavars_used INIT(= NULL); - // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); diff --git a/src/nvim/main.c b/src/nvim/main.c index 7b1c912f4b..0c978dc47d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -391,9 +391,10 @@ int main(int argc, char **argv) shada_read_everything(NULL, false, true); TIME_MSG("reading ShaDa"); } - /* It's better to make v:oldfiles an empty list than NULL. */ - if (get_vim_var_list(VV_OLDFILES) == NULL) - set_vim_var_list(VV_OLDFILES, list_alloc()); + // It's better to make v:oldfiles an empty list than NULL. + if (get_vim_var_list(VV_OLDFILES) == NULL) { + set_vim_var_list(VV_OLDFILES, tv_list_alloc()); + } /* * "-q errorfile": Load the error file now. diff --git a/src/nvim/mark.c b/src/nvim/mark.c index de2fdd7f13..1d1e13e7e0 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -1431,3 +1431,26 @@ void free_all_marks(void) memset(&namedfm[0], 0, sizeof(namedfm)); } #endif + +/// Adjust position to point to the first byte of a multi-byte character +/// +/// If it points to a tail byte it is move backwards to the head byte. +/// +/// @param[in] buf Buffer to adjust position in. +/// @param[out] lp Position to adjust. +void mark_mb_adjustpos(buf_T *buf, pos_T *lp) + FUNC_ATTR_NONNULL_ALL +{ + if (lp->col > 0 || lp->coladd > 1) { + const char_u *const p = ml_get_buf(buf, lp->lnum, false); + lp->col -= (*mb_head_off)(p, p + lp->col); + // Reset "coladd" when the cursor would be on the right half of a + // double-wide character. + if (lp->coladd == 1 + && p[lp->col] != TAB + && vim_isprintc((*mb_ptr2char)(p + lp->col)) + && ptr2cells(p + lp->col) > 1) { + lp->coladd = 0; + } + } +} diff --git a/src/nvim/mark_defs.h b/src/nvim/mark_defs.h index 720b2475ed..2cb489501e 100644 --- a/src/nvim/mark_defs.h +++ b/src/nvim/mark_defs.h @@ -3,7 +3,7 @@ #include "nvim/pos.h" #include "nvim/os/time.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" /* * marks: positions in a file diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 0ab133a545..57518a9535 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -50,6 +50,7 @@ #include "nvim/strings.h" #include "nvim/os/os.h" #include "nvim/arabic.h" +#include "nvim/mark.h" typedef struct { int rangeStart; @@ -375,16 +376,18 @@ void remove_bom(char_u *s) */ int mb_get_class(const char_u *p) { - return mb_get_class_buf(p, curbuf); + return mb_get_class_tab(p, curbuf->b_chartab); } -int mb_get_class_buf(const char_u *p, buf_T *buf) +int mb_get_class_tab(const char_u *p, const uint64_t *const chartab) { if (MB_BYTE2LEN(p[0]) == 1) { - if (p[0] == NUL || ascii_iswhite(p[0])) + if (p[0] == NUL || ascii_iswhite(p[0])) { return 0; - if (vim_iswordc_buf(p[0], buf)) + } + if (vim_iswordc_tab(p[0], chartab)) { return 2; + } return 1; } return utf_class(utf_ptr2char(p)); @@ -1639,38 +1642,16 @@ theend: */ void mb_adjust_cursor(void) { - mb_adjustpos(curbuf, &curwin->w_cursor); -} - -/* - * Adjust position "*lp" to point to the first byte of a multi-byte character. - * If it points to a tail byte it's moved backwards to the head byte. - */ -void mb_adjustpos(buf_T *buf, pos_T *lp) -{ - char_u *p; - - if (lp->col > 0 - || lp->coladd > 1 - ) { - p = ml_get_buf(buf, lp->lnum, FALSE); - lp->col -= (*mb_head_off)(p, p + lp->col); - /* Reset "coladd" when the cursor would be on the right half of a - * double-wide character. */ - if (lp->coladd == 1 - && p[lp->col] != TAB - && vim_isprintc((*mb_ptr2char)(p + lp->col)) - && ptr2cells(p + lp->col) > 1) - lp->coladd = 0; - } + mark_mb_adjustpos(curbuf, &curwin->w_cursor); } /// Checks and adjusts cursor column. Not mode-dependent. /// @see check_cursor_col_win /// -/// @param win Places cursor on a valid column for this window. -void mb_check_adjust_col(win_T *win) +/// @param win_ Places cursor on a valid column for this window. +void mb_check_adjust_col(void *win_) { + win_T *win = (win_T *)win_; colnr_T oldcol = win->w_cursor.col; // Column 0 is always valid. diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 2c92a0fbb2..5f5bab9fcd 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -3,6 +3,8 @@ #include +#include "nvim/iconv.h" + /* * Return byte length of character that starts with byte "b". * Returns 1 for a single-byte character. @@ -40,6 +42,27 @@ #define mb_ptr2char utf_ptr2char #define mb_head_off utf_head_off +/// Flags for vimconv_T +typedef enum { + CONV_NONE = 0, + CONV_TO_UTF8 = 1, + CONV_9_TO_UTF8 = 2, + CONV_TO_LATIN1 = 3, + CONV_TO_LATIN9 = 4, + CONV_ICONV = 5, +} ConvFlags; + +/// Structure used for string conversions +typedef struct { + int vc_type; ///< Zero or more ConvFlags. + int vc_factor; ///< Maximal expansion factor. +# ifdef USE_ICONV + iconv_t vc_fd; ///< Value for CONV_ICONV. +# endif + bool vc_fail; ///< What to do with invalid characters: if true, fail, + ///< otherwise use '?'. +} vimconv_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 58c01fbe7a..b4fdd86a6d 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -430,6 +430,19 @@ char *xstrdup(const char *str) return xmemdupz(str, strlen(str)); } +/// strdup() wrapper +/// +/// Unlike xstrdup() allocates a new empty string if it receives NULL. +char *xstrdupnul(const char *const str) + FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + if (str == NULL) { + return xmallocz(0); + } else { + return xstrdup(str); + } +} + /// A version of memchr that starts the search at `src + len`. /// /// Based on glibc's memrchr. diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 2ade9cb87d..4cca5ec948 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2465,7 +2465,7 @@ do_mouse ( &rettv, ARRAY_SIZE(argv), argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &doesrange, true, NULL, NULL); - clear_tv(&rettv); + tv_clear(&rettv); break; } } @@ -7290,11 +7290,11 @@ static bool unadjust_for_sel(void) pp = &curwin->w_cursor; else pp = &VIsual; - if (pp->coladd > 0) - --pp->coladd; - else if (pp->col > 0) { - --pp->col; - mb_adjustpos(curbuf, pp); + if (pp->coladd > 0) { + pp->coladd--; + } else if (pp->col > 0) { + pp->col--; + mark_mb_adjustpos(curbuf, pp); } else if (pp->lnum > 1) { --pp->lnum; pp->col = (colnr_T)STRLEN(ml_get(pp->lnum)); @@ -7829,7 +7829,7 @@ static void get_op_vcol( // prevent from moving onto a trail byte if (has_mbyte) { - mb_adjustpos(curwin->w_buffer, &oap->end); + mark_mb_adjustpos(curwin->w_buffer, &oap->end); } getvvcol(curwin, &(oap->start), &oap->start_vcol, NULL, &oap->end_vcol); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index c13b6f736a..85cef59aec 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2555,9 +2555,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) dict_T *dict = get_vim_var_dict(VV_EVENT); // the yanked text - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; dict_add_list(dict, "regcontents", list); @@ -4844,8 +4844,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = list_alloc(); - list_append_string(list, NULL, -1); + list_T *list = tv_list_alloc(); + tv_list_append_string(list, NULL, 0); list->lv_first->li_tv.vval.v_string = s; return list; } @@ -4895,9 +4895,9 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = list_alloc(); + list_T *list = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(list, reg->y_array[i], -1); + tv_list_append_string(list, (const char *)reg->y_array[i], -1); } return list; @@ -5570,9 +5570,9 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *args = list_alloc(); - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + list_T *const args = tv_list_alloc(); + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); typval_T result = eval_call_provider("clipboard", "get", args); @@ -5584,7 +5584,8 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) goto err; } - list_T *res = result.vval.v_list, *lines = NULL; + list_T *res = result.vval.v_list; + list_T *lines = NULL; if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { lines = res->lv_first->li_tv.vval.v_list; if (res->lv_last->li_tv.v_type != VAR_STRING) { @@ -5628,7 +5629,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) if (li->li_tv.v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (uint8_t *)xstrdup((char *)li->li_tv.vval.v_string); + reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); } if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { @@ -5686,35 +5687,39 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = list_alloc(); + list_T *lines = tv_list_alloc(); for (size_t i = 0; i < reg->y_size; i++) { - list_append_string(lines, reg->y_array[i], -1); + tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = list_alloc(); - list_append_list(args, lines); + list_T *args = tv_list_alloc(); + tv_list_append_list(args, lines); - char_u regtype; + char regtype; switch (reg->y_type) { - case kMTLineWise: - regtype = 'V'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTCharWise: - regtype = 'v'; - break; - case kMTBlockWise: - regtype = 'b'; - list_append_string(lines, (char_u*)"", 0); - break; - case kMTUnknown: - assert(false); - } - list_append_string(args, ®type, 1); - - char_u regname = (char_u)name; - list_append_string(args, ®name, 1); + case kMTLineWise: { + regtype = 'V'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTCharWise: { + regtype = 'v'; + break; + } + case kMTBlockWise: { + regtype = 'b'; + tv_list_append_string(lines, NULL, 0); + break; + } + case kMTUnknown: { + assert(false); + } + } + tv_list_append_string(args, ®type, 1); + + const char regname = (char)name; + tv_list_append_string(args, ®name, 1); (void)eval_call_provider("clipboard", "set", args); } diff --git a/src/nvim/ops.h b/src/nvim/ops.h index 44df2e9e0c..13d0142343 100644 --- a/src/nvim/ops.h +++ b/src/nvim/ops.h @@ -6,7 +6,7 @@ #include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/os/time.h" typedef int (*Indenter)(void); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 8d8c20c1d0..323503c4f5 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4003,7 +4003,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) bufnum = 0; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 1cd334abcd..9c6f02f778 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -3650,9 +3650,11 @@ static long regtry(bt_regprog_T *prog, colnr_T col) */ static int reg_prev_class(void) { - if (reginput > regline) - return mb_get_class_buf(reginput - 1 - - (*mb_head_off)(regline, reginput - 1), reg_buf); + if (reginput > regline) { + return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline, + reginput - 1), + reg_buf->b_chartab); + } return -1; } @@ -3918,12 +3920,13 @@ regmatch ( else if (has_mbyte) { int this_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); - if (this_class <= 1) - status = RA_NOMATCH; /* not on a word at all */ - else if (reg_prev_class() == this_class) - status = RA_NOMATCH; /* previous char is in same word */ + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + if (this_class <= 1) { + status = RA_NOMATCH; // Not on a word at all. + } else if (reg_prev_class() == this_class) { + status = RA_NOMATCH; // Previous char is in same word. + } } else { if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline && vim_iswordc_buf(reginput[-1 @@ -3938,8 +3941,8 @@ regmatch ( else if (has_mbyte) { int this_class, prev_class; - /* Get class of current and previous char (if it exists). */ - this_class = mb_get_class_buf(reginput, reg_buf); + // Get class of current and previous char (if it exists). + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) @@ -6617,7 +6620,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } - clear_tv(&rettv); + tv_clear(&rettv); } else { eval_result = eval_to_string(source + 2, NULL, true); } @@ -6976,7 +6979,7 @@ list_T *reg_submatch_list(int no) linenr_T slnum; linenr_T elnum; list_T *list; - char_u *s; + const char *s; if (submatch_match == NULL) { slnum = submatch_mmatch->startpos[no].lnum; @@ -6988,27 +6991,27 @@ list_T *reg_submatch_list(int no) colnr_T scol = submatch_mmatch->startpos[no].col; colnr_T ecol = submatch_mmatch->endpos[no].col; - list = list_alloc(); + list = tv_list_alloc(); - s = reg_getline_submatch(slnum) + scol; + s = (const char *)reg_getline_submatch(slnum) + scol; if (slnum == elnum) { - list_append_string(list, s, ecol - scol); + tv_list_append_string(list, s, ecol - scol); } else { - list_append_string(list, s, -1); + tv_list_append_string(list, s, -1); for (int i = 1; i < elnum - slnum; i++) { - s = reg_getline_submatch(slnum + i); - list_append_string(list, s, -1); + s = (const char *)reg_getline_submatch(slnum + i); + tv_list_append_string(list, s, -1); } - s = reg_getline_submatch(elnum); - list_append_string(list, s, ecol); + s = (const char *)reg_getline_submatch(elnum); + tv_list_append_string(list, s, ecol); } } else { - s = submatch_match->startp[no]; + s = (const char *)submatch_match->startp[no]; if (s == NULL || submatch_match->endp[no] == NULL) { return NULL; } - list = list_alloc(); - list_append_string(list, s, (int)(submatch_match->endp[no] - s)); + list = tv_list_alloc(); + tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s); } return list; diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 3f4e12af4a..5b49ab38f0 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -5410,7 +5410,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); if (this_class <= 1) { result = false; } else if (reg_prev_class() == this_class) { @@ -5435,7 +5435,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_buf(reginput, reg_buf); + this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 197b029591..c550cb0888 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -30,7 +30,7 @@ #include "nvim/ex_getln.h" #include "nvim/search.h" #include "nvim/regexp.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #include "nvim/version.h" #include "nvim/path.h" #include "nvim/fileio.h" @@ -1223,7 +1223,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = list_alloc(); + oldfiles_list = tv_list_alloc(); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1435,8 +1435,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) fname = xstrdup(fname); } int kh_ret; - (void) kh_put(strset, &oldfiles_set, fname, &kh_ret); - list_append_allocated_string(oldfiles_list, fname); + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { // Avoid free because this string was already used. cur_entry.data.filemark.fname = NULL; @@ -1573,7 +1573,9 @@ static char *shada_filename(const char *file) do { \ const String s_ = (s); \ msgpack_pack_str(spacker, s_.size); \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ } while (0) #define PACK_BIN(s) \ do { \ @@ -1965,7 +1967,7 @@ static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer, typval_T tgttv; var_item_copy(sd_conv, &entry.data.data.global_var.value, &tgttv, true, 0); - clear_tv(&entry.data.data.global_var.value); + tv_clear(&entry.data.data.global_var.value); entry.data.data.global_var.value = tgttv; } ret = shada_pack_entry(packer, entry.data, max_kbyte); @@ -2573,13 +2575,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } } }, max_kbyte)) == kSDWriteFailed) { - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); ret = kSDWriteFailed; goto shada_write_exit; } - clear_tv(&vartv); - clear_tv(&tgttv); + tv_clear(&vartv); + tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); @@ -3172,18 +3174,18 @@ static void shada_free_shada_entry(ShadaEntry *const entry) break; } case kSDItemHistoryEntry: { - list_unref(entry->data.history_item.additional_elements); + tv_list_unref(entry->data.history_item.additional_elements); xfree(entry->data.history_item.string); break; } case kSDItemVariable: { - list_unref(entry->data.global_var.additional_elements); + tv_list_unref(entry->data.global_var.additional_elements); xfree(entry->data.global_var.name); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); break; } case kSDItemSubString: { - list_unref(entry->data.sub_string.additional_elements); + tv_list_unref(entry->data.sub_string.additional_elements); xfree(entry->data.sub_string.sub); break; } @@ -3451,7 +3453,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, "cannot be converted to a VimL dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ - clear_tv(&adtv); \ + tv_clear(&adtv); \ goto shada_read_next_item_error; \ } \ tgt = adtv.vval.v_dict; \ @@ -3474,7 +3476,7 @@ static inline char *get_converted_string(const vimconv_T *const sd_conv, if (msgpack_to_vim(obj, &aetv) == FAIL) { \ emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ initial_fpos); \ - clear_tv(&aetv); \ + tv_clear(&aetv); \ goto shada_read_next_item_error; \ } \ assert(aetv.v_type == VAR_LIST); \ @@ -3866,7 +3868,7 @@ shada_read_next_item_hist_no_conv: &tgttv, true, 0); - clear_tv(&entry->data.global_var.value); + tv_clear(&entry->data.global_var.value); entry->data.global_var.value = tgttv; } SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 3b891d998f..5ca5ab3339 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3248,7 +3248,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) add_suggestion(su, &su->su_ga, p, su->su_badlen, score, 0, true, su->su_sallang, false); } - list_unref(list); + tv_list_unref(list); } // Remove bogus suggestions, sort and truncate at "maxcount". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 267832ed2d..b964fed35a 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -571,7 +571,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) EMSG(_(e_printf)); } else { (*idxp)++; - int err = false; + bool err = false; n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); if (err) { n = 0; diff --git a/src/nvim/strings.h b/src/nvim/strings.h index 8aea374b96..59b8701a3f 100644 --- a/src/nvim/strings.h +++ b/src/nvim/strings.h @@ -5,7 +5,7 @@ #include #include "nvim/types.h" -#include "nvim/eval_defs.h" +#include "nvim/eval/typval.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "strings.h.generated.h" diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 7bcaff662c..b0ffc12b5f 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -674,7 +674,7 @@ do_tag ( fname = xmalloc(MAXPATHL + 1); cmd = xmalloc(CMDBUFFSIZE + 1); - list = list_alloc(); + list = tv_list_alloc(); for (i = 0; i < num_matches; ++i) { int len, cmd_len; @@ -774,7 +774,7 @@ do_tag ( } dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); dict_add_nr_str(dict, "filename", 0L, fname); @@ -786,7 +786,7 @@ do_tag ( vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); set_errorlist(curwin, list, ' ', IObuff, NULL); - list_free(list); + tv_list_free(list); xfree(fname); xfree(cmd); @@ -2825,7 +2825,7 @@ int get_tags(list_T *list, char_u *pat) continue; dict = dict_alloc(); - list_append_dict(list, dict); + tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c95a795587..729cf03e15 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2952,14 +2952,14 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = list_alloc(); + list_T *alt_list = tv_list_alloc(); /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); dict_add_list(dict, "alt", alt_list); } - list_append_dict(list, dict); + tv_list_append_dict(list, dict); uhp = uhp->uh_prev.ptr; } } diff --git a/src/nvim/window.c b/src/nvim/window.c index 4fac730f02..47a97da0cf 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5582,7 +5582,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int len = 1; list_T *subl; listitem_T *subli; - int error = false; + bool error = false; if (li->li_tv.v_type == VAR_LIST) { subl = li->li_tv.vval.v_list; @@ -5594,7 +5594,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, goto fail; } lnum = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } if (lnum == 0) { @@ -5605,12 +5605,13 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, subli = subli->li_next; if (subli != NULL) { col = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) + if (error) { goto fail; + } subli = subli->li_next; if (subli != NULL) { len = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + if (error) { goto fail; } } @@ -5881,8 +5882,8 @@ void win_id2tabwin(typval_T *argvars, list_T *list) int id = get_tv_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); - list_append_number(list, tabnr); - list_append_number(list, winnr); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); } win_T * win_id2wp(typval_T *argvars) @@ -5918,7 +5919,7 @@ void win_findbuf(typval_T *argvars, list_T *list) FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { - list_append_number(list, wp->handle); + tv_list_append_number(list, wp->handle); } } } diff --git a/test/unit/eval/decode_spec.lua b/test/unit/eval/decode_spec.lua index 2d7597c0f4..0b2a423cd6 100644 --- a/test/unit/eval/decode_spec.lua +++ b/test/unit/eval/decode_spec.lua @@ -7,7 +7,7 @@ local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval_defs.h', +local decode = cimport('./src/nvim/eval/decode.h', './src/nvim/eval/typval.h', './src/nvim/globals.h', './src/nvim/memory.h', './src/nvim/message.h') diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 1377d5b501..49f929937f 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -5,7 +5,7 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq -local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h', +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/hashtab.h') local null_string = {[true]='NULL string'} @@ -33,7 +33,7 @@ local function li_alloc(nogc) end local function list(...) - local ret = ffi.gc(eval.list_alloc(), eval.list_unref) + local ret = ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref) eq(0, ret.lv_refcount) ret.lv_refcount = 1 for i = 1, select('#', ...) do @@ -241,7 +241,7 @@ local typvalt = function(typ, vval) elseif type(typ) == 'string' then typ = eval[typ] end - return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.clear_tv) + return ffi.gc(ffi.new('typval_T', {v_type=typ, vval=vval}), eval.tv_clear) end local lua2typvalt_type_tab = { @@ -256,14 +256,14 @@ local lua2typvalt_type_tab = { processed[l].lv_refcount = processed[l].lv_refcount + 1 return typvalt(eval.VAR_LIST, {v_list=processed[l]}) end - local lst = eval.list_alloc() + local lst = eval.tv_list_alloc() lst.lv_refcount = 1 processed[l] = lst local ret = typvalt(eval.VAR_LIST, {v_list=lst}) for i = 1, #l do local item_tv = ffi.gc(lua2typvalt(l[i], processed), nil) - eval.list_append_tv(lst, item_tv) - eval.clear_tv(item_tv) + eval.tv_list_append_tv(lst, item_tv) + eval.tv_clear(item_tv) end return ret end, @@ -281,7 +281,7 @@ local lua2typvalt_type_tab = { local di = eval.dictitem_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) eval.copy_tv(val_tv, di.di_tv) - eval.clear_tv(val_tv) + eval.tv_clear(val_tv) eval.dict_add(dct, di) end end @@ -301,7 +301,7 @@ local lua2typvalt_type_tab = { for i, arg in ipairs(l.args) do local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) eval.copy_tv(arg_tv, argv[i - 1]) - eval.clear_tv(arg_tv) + eval.tv_clear(arg_tv) end end local dict = nil diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index ec79a9cad5..7f0a445f2c 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -10,7 +10,7 @@ local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h') local eval_expr = function(expr) return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv) - eval.clear_tv(tv) + eval.tv_clear(tv) eval.xfree(tv) end) end diff --git a/test/unit/eval/tv_clear_spec.lua b/test/unit/eval/tv_clear_spec.lua index 47d4661ad8..ca37301b32 100644 --- a/test/unit/eval/tv_clear_spec.lua +++ b/test/unit/eval/tv_clear_spec.lua @@ -14,7 +14,7 @@ local list_items = eval_helpers.list_items local dict_items = eval_helpers.dict_items local lua2typvalt = eval_helpers.lua2typvalt -local lib = cimport('./src/nvim/eval_defs.h', './src/nvim/eval.h') +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/eval.h') local alloc_log = alloc_log_new() @@ -26,7 +26,7 @@ after_each(function() alloc_log:after_each() end) -describe('clear_tv()', function() +describe('tv_clear()', function() itp('successfully frees all lists in [&l [1], *l, *l]', function() local l_inner = {1} local list = {l_inner, l_inner, l_inner} @@ -44,7 +44,7 @@ describe('clear_tv()', function() a.li(lis[3]), }) eq(3, list_inner_p.lv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(lis_inner[1]), a.freed(list_inner_p), @@ -69,7 +69,7 @@ describe('clear_tv()', function() a.li(lis[3]), }) eq(3, list_inner_p.lv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(list_inner_p), a.freed(lis[1]), @@ -92,7 +92,7 @@ describe('clear_tv()', function() a.li(lis[2]), }) eq(2, dict_inner_p.dv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(dict_inner_p), a.freed(lis[1]), @@ -116,7 +116,7 @@ describe('clear_tv()', function() a.li(lis[2]), }) eq(2, dict_inner_p.dv_refcount) - lib.clear_tv(list_tv) + lib.tv_clear(list_tv) alloc_log:check({ a.freed(dis.a), a.freed(dict_inner_p), -- cgit From 50a48f2a0ecf7f767df961f7f5060505cf28e331 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 30 Jul 2016 01:39:32 +0300 Subject: functests: Add tests for some *buf* functions --- test/functional/eval/buf_functions_spec.lua | 300 ++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 test/functional/eval/buf_functions_spec.lua diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua new file mode 100644 index 0000000000..a130da4452 --- /dev/null +++ b/test/functional/eval/buf_functions_spec.lua @@ -0,0 +1,300 @@ +local helpers = require('test.functional.helpers')(after_each) + +local lfs = require('lfs') + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local meths = helpers.meths +local command = helpers.command +local exc_exec = helpers.exc_exec +local bufmeths = helpers.bufmeths +local winmeths = helpers.winmeths +local curbufmeths = helpers.curbufmeths +local curwinmeths = helpers.curwinmeths +local curtabmeths = helpers.curtabmeths + +local fname = 'Xtest-functional-eval-buf_functions' +local fname2 = fname .. '.2' +local dirname = fname .. '.d' + +before_each(clear) + +for _, func in ipairs({'bufname(%s)', 'bufnr(%s)', 'bufwinnr(%s)', + 'getbufline(%s, 1)', 'getbufvar(%s, "changedtick")', + 'setbufvar(%s, "f", 0)'}) do + local funcname = func:match('%w+') + describe(funcname .. '() function', function() + it('errors out when receives v:true/v:false/v:null', function() + -- Not compatible with Vim: in Vim it always results in buffer not found + -- without any error messages. + for _, var in ipairs({'v:true', 'v:false', 'v:null'}) do + eq('Vim(call):E5300: Expected a Number or a String', + exc_exec('call ' .. func:format(var))) + end + end) + it('errors out when receives invalid argument', function() + eq('Vim(call):E745: Expected a Number or a String, List found', + exc_exec('call ' .. func:format('[]'))) + eq('Vim(call):E728: Expected a Number or a String, Dictionary found', + exc_exec('call ' .. func:format('{}'))) + eq('Vim(call):E805: Expected a Number or a String, Float found', + exc_exec('call ' .. func:format('0.0'))) + eq('Vim(call):E703: Expected a Number or a String, Funcref found', + exc_exec('call ' .. func:format('function("tr")'))) + end) + end) +end + +describe('bufname() function', function() + it('returns empty string when buffer was not found', function() + command('file ' .. fname) + eq('', funcs.bufname(2)) + eq('', funcs.bufname('non-existent-buffer')) + eq('', funcs.bufname('#')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('', funcs.bufname('X')) + end) + before_each(function() + lfs.mkdir(dirname) + end) + after_each(function() + lfs.rmdir(dirname) + end) + it('returns expected buffer name', function() + eq('', funcs.bufname('%')) -- Buffer has no name yet + command('file ' .. fname) + local wd = lfs.currentdir() + local curdirname = funcs.fnamemodify(wd, ':t') + for _, arg in ipairs({'%', 1, 'X', wd}) do + eq(fname, funcs.bufname(arg)) + meths.set_current_dir('..') + eq(curdirname .. '/' .. fname, funcs.bufname(arg)) + meths.set_current_dir(curdirname) + meths.set_current_dir(dirname) + eq(wd .. '/' .. fname, funcs.bufname(arg)) + meths.set_current_dir('..') + eq(fname, funcs.bufname(arg)) + command('enew') + end + eq('', funcs.bufname('%')) + eq('', funcs.bufname('$')) + eq(2, funcs.bufnr('%')) + end) +end) + +describe('bufnr() function', function() + it('returns -1 when buffer was not found', function() + command('file ' .. fname) + eq(-1, funcs.bufnr(2)) + eq(-1, funcs.bufnr('non-existent-buffer')) + eq(-1, funcs.bufnr('#')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq(-1, funcs.bufnr('X')) + end) + it('returns expected buffer number', function() + eq(1, funcs.bufnr('%')) + command('file ' .. fname) + local wd = lfs.currentdir() + local curdirname = funcs.fnamemodify(wd, ':t') + eq(1, funcs.bufnr(fname)) + eq(1, funcs.bufnr(wd)) + eq(1, funcs.bufnr(curdirname)) + eq(1, funcs.bufnr('X')) + end) + it('returns number of last buffer with "$"', function() + eq(1, funcs.bufnr('$')) + command('new') + eq(2, funcs.bufnr('$')) + command('new') + eq(3, funcs.bufnr('$')) + command('only') + eq(3, funcs.bufnr('$')) + eq(3, funcs.bufnr('%')) + command('buffer 1') + eq(3, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('bwipeout 2') + eq(3, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('bwipeout 3') + eq(1, funcs.bufnr('$')) + eq(1, funcs.bufnr('%')) + command('new') + eq(4, funcs.bufnr('$')) + end) +end) + +describe('bufwinnr() function', function() + it('returns -1 when buffer was not found', function() + command('file ' .. fname) + eq(-1, funcs.bufwinnr(2)) + eq(-1, funcs.bufwinnr('non-existent-buffer')) + eq(-1, funcs.bufwinnr('#')) + command('split ' .. fname2) -- It would be OK if there was one window + eq(2, funcs.bufnr('%')) + eq(-1, funcs.bufwinnr('X')) + end) + before_each(function() + lfs.mkdir(dirname) + end) + after_each(function() + lfs.rmdir(dirname) + end) + it('returns expected window number', function() + eq(1, funcs.bufwinnr('%')) + command('file ' .. fname) + command('vsplit') + command('split ' .. fname2) + eq(2, funcs.bufwinnr(fname)) + eq(1, funcs.bufwinnr(fname2)) + eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) + meths.set_current_dir(dirname) + eq(2, funcs.bufwinnr(fname)) + eq(1, funcs.bufwinnr(fname2)) + eq(-1, funcs.bufwinnr(fname:sub(1, #fname - 1))) + eq(1, funcs.bufwinnr('%')) + eq(2, funcs.bufwinnr(1)) + eq(1, funcs.bufwinnr(2)) + eq(-1, funcs.bufwinnr(3)) + eq(1, funcs.bufwinnr('$')) + end) +end) + +describe('getbufline() function', function() + it('returns empty list when buffer was not found', function() + command('file ' .. fname) + eq({}, funcs.getbufline(2, 1)) + eq({}, funcs.getbufline('non-existent-buffer', 1)) + eq({}, funcs.getbufline('#', 1)) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq({}, funcs.getbufline('X', 1)) + end) + it('returns empty list when range is invalid', function() + eq({}, funcs.getbufline(1, 0)) + curbufmeths.set_lines(0, 1, false, {'foo', 'bar', 'baz'}) + eq({}, funcs.getbufline(1, 2, 1)) + eq({}, funcs.getbufline(1, -10, -20)) + eq({}, funcs.getbufline(1, -2, -1)) + eq({}, funcs.getbufline(1, -1, 9999)) + end) + it('returns expected lines', function() + meths.set_option('hidden', true) + command('file ' .. fname) + curbufmeths.set_lines(0, 1, false, {'foo\0', '\0bar', 'baz'}) + command('edit ' .. fname2) + curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) + eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, 9999)) + eq({'abc\n', '\ndef', 'ghi'}, funcs.getbufline(2, 1, 9999)) + eq({'foo\n', '\nbar', 'baz'}, funcs.getbufline(1, 1, '$')) + eq({'baz'}, funcs.getbufline(1, '$', '$')) + eq({'baz'}, funcs.getbufline(1, '$', 9999)) + end) +end) + +describe('getbufvar() function', function() + it('returns empty list when buffer was not found', function() + command('file ' .. fname) + eq('', funcs.getbufvar(2, '&autoindent')) + eq('', funcs.getbufvar('non-existent-buffer', '&autoindent')) + eq('', funcs.getbufvar('#', '&autoindent')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('', funcs.getbufvar('X', '&autoindent')) + end) + it('returns empty list when variable/option/etc was not found', function() + command('file ' .. fname) + eq('', funcs.getbufvar(1, '&autondent')) + eq('', funcs.getbufvar(1, 'changedtic')) + end) + it('returns expected option value', function() + eq(0, funcs.getbufvar(1, '&autoindent')) + eq(0, funcs.getbufvar(1, '&l:autoindent')) + eq(0, funcs.getbufvar(1, '&g:autoindent')) + -- Also works with global-only options + eq(0, funcs.getbufvar(1, '&hidden')) + eq(0, funcs.getbufvar(1, '&l:hidden')) + eq(0, funcs.getbufvar(1, '&g:hidden')) + -- Also works with window-local options + eq(0, funcs.getbufvar(1, '&number')) + eq(0, funcs.getbufvar(1, '&l:number')) + eq(0, funcs.getbufvar(1, '&g:number')) + command('new') + -- But with window-local options it probably does not what you expect + curwinmeths.set_option('number', true) + -- (note that current window’s buffer is 2, but getbufvar() receives 1) + eq(2, bufmeths.get_number(curwinmeths.get_buf())) + eq(1, funcs.getbufvar(1, '&number')) + eq(1, funcs.getbufvar(1, '&l:number')) + -- You can get global value though, if you find this useful. + eq(0, funcs.getbufvar(1, '&g:number')) + end) + it('returns expected variable value', function() + eq(2, funcs.getbufvar(1, 'changedtick')) + curbufmeths.set_lines(0, 1, false, {'abc\0', '\0def', 'ghi'}) + eq(3, funcs.getbufvar(1, 'changedtick')) + curbufmeths.set_var('test', true) + eq(true, funcs.getbufvar(1, 'test')) + eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) + command('new') + eq(3, funcs.getbufvar(1, 'changedtick')) + eq(true, funcs.getbufvar(1, 'test')) + eq({test=true, changedtick=3}, funcs.getbufvar(1, '')) + end) +end) + +describe('setbufvar() function', function() + it('throws the error or ignores the input when buffer was not found', function() + command('file ' .. fname) + eq(0, + exc_exec('call setbufvar(2, "&autoindent", 0)')) + eq('Vim(call):E94: No matching buffer for non-existent-buffer', + exc_exec('call setbufvar("non-existent-buffer", "&autoindent", 0)')) + eq(0, + exc_exec('call setbufvar("#", "&autoindent", 0)')) + command('edit ' .. fname2) + eq(2, funcs.bufnr('%')) + eq('Vim(call):E93: More than one match for X', + exc_exec('call setbufvar("X", "&autoindent", 0)')) + end) + it('may set options, including window-local and global values', function() + local buf1 = meths.get_current_buf() + eq(false, curwinmeths.get_option('number')) + command('split') + command('new') + eq(2, bufmeths.get_number(curwinmeths.get_buf())) + funcs.setbufvar(1, '&number', true) + local windows = curtabmeths.list_wins() + eq(false, winmeths.get_option(windows[1], 'number')) + eq(true, winmeths.get_option(windows[2], 'number')) + eq(false, winmeths.get_option(windows[3], 'number')) + eq(false, winmeths.get_option(meths.get_current_win(), 'number')) + + eq(false, meths.get_option('hidden')) + funcs.setbufvar(1, '&hidden', true) + eq(true, meths.get_option('hidden')) + + eq(false, bufmeths.get_option(buf1, 'autoindent')) + funcs.setbufvar(1, '&autoindent', true) + eq(true, bufmeths.get_option(buf1, 'autoindent')) + eq('Vim(call):E355: Unknown option: xxx', + exc_exec('call setbufvar(1, "&xxx", 0)')) + end) + it('may set variables', function() + local buf1 = meths.get_current_buf() + command('split') + command('new') + eq(2, curbufmeths.get_number()) + funcs.setbufvar(1, 'number', true) + eq(true, bufmeths.get_var(buf1, 'number')) + eq('Vim(call):E461: Illegal variable name: b:', + exc_exec('call setbufvar(1, "", 0)')) + eq(true, bufmeths.get_var(buf1, 'number')) + funcs.setbufvar(1, 'changedtick', true) + -- eq(true, bufmeths.get_var(buf1, 'changedtick')) + eq(2, funcs.getbufvar(1, 'changedtick')) + end) +end) -- cgit From e18a5783080f7c94f408ec5f53dedffdb69789e1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:24:34 +0300 Subject: *: Move some dictionary functions to typval.h and use char* Also fixes buffer reusage in setmatches() and complete(). --- src/.asan-blacklist | 2 +- src/nvim/api/private/helpers.c | 33 +- src/nvim/api/vim.c | 2 +- src/nvim/ascii.h | 8 +- src/nvim/buffer.c | 12 +- src/nvim/buffer.h | 5 +- src/nvim/charset.h | 10 + src/nvim/diff.c | 2 +- src/nvim/edit.c | 186 +- src/nvim/eval.c | 1780 ++++++-------------- src/nvim/eval.h | 8 +- src/nvim/eval/decode.c | 26 +- src/nvim/eval/encode.c | 4 +- src/nvim/eval/typval.c | 788 ++++++++- src/nvim/eval/typval.h | 128 +- src/nvim/eval/typval_encode.c.h | 10 +- src/nvim/event/process.h | 2 +- src/nvim/ex_cmds.c | 4 +- src/nvim/ex_cmds2.c | 65 +- src/nvim/ex_docmd.c | 28 +- src/nvim/ex_getln.c | 4 +- src/nvim/file_search.c | 2 +- src/nvim/getchar.c | 5 +- src/nvim/hashtab.c | 7 +- src/nvim/hashtab.h | 19 + src/nvim/macros.h | 9 + src/nvim/main.c | 45 +- src/nvim/mark.c | 2 +- src/nvim/mbyte.c | 36 +- src/nvim/mbyte.h | 15 + src/nvim/memline.c | 2 +- src/nvim/ops.c | 5 +- src/nvim/option.c | 364 ++-- src/nvim/path.c | 73 +- src/nvim/popupmnu.c | 11 +- src/nvim/quickfix.c | 69 +- src/nvim/search.c | 9 +- src/nvim/shada.c | 14 +- src/nvim/spell.c | 9 +- src/nvim/spellfile.c | 2 +- src/nvim/syntax.c | 30 +- src/nvim/tag.c | 8 +- src/nvim/terminal.c | 10 +- src/nvim/undo.c | 4 +- src/nvim/vim.h | 38 +- src/nvim/window.c | 20 +- test/functional/eval/match_functions_spec.lua | 39 + test/functional/eval/string_spec.lua | 10 +- .../functional/ex_cmds/dict_notifications_spec.lua | 4 +- test/functional/ex_cmds/quickfix_commands_spec.lua | 83 + test/functional/viml/completion_spec.lua | 36 + test/unit/eval/helpers.lua | 6 +- 52 files changed, 2232 insertions(+), 1861 deletions(-) create mode 100644 test/functional/eval/match_functions_spec.lua create mode 100644 test/functional/ex_cmds/quickfix_commands_spec.lua diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 7636f8fa82..928d81bd5a 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,3 @@ # multiqueue.h pointer arithmetic is not accepted by asan fun:multiqueue_node_data -fun:dictwatcher_node_data +fun:tv_dict_watcher_node_data diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff45cad8f5..6b17ba1a11 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -88,14 +88,13 @@ bool try_end(Error *err) /// @param[out] err Details of an error that may have occurred Object dict_get_value(dict_T *dict, String key, Error *err) { - hashitem_T *hi = hash_find(&dict->dv_hashtab, (char_u *)key.data); + dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); - if (HASHITEM_EMPTY(hi)) { + if (di == NULL) { api_set_error(err, Validation, _("Key not found")); return (Object) OBJECT_INIT; } - dictitem_T *di = dict_lookup(hi); return vim_to_object(&di->di_tv); } @@ -130,7 +129,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, return rv; } - dictitem_T *di = dict_find(dict, (char_u *)key.data, (int)key.size); + dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di != NULL) { if (di->di_flags & DI_FLAGS_RO) { @@ -156,9 +155,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, rv = vim_to_object(&di->di_tv); } // Delete the entry - hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key); - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(di); + tv_dict_item_remove(dict, di); } } else { // Update the key @@ -171,8 +168,8 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, if (di == NULL) { // Need to create an entry - di = dictitem_alloc((uint8_t *) key.data); - dict_add(dict, di); + di = tv_dict_item_alloc_len(key.data, key.size); + tv_dict_add(dict, di); } else { // Return the old value if (retval) { @@ -706,7 +703,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) } case kObjectTypeDictionary: { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); for (uint32_t i = 0; i < obj.data.dictionary.size; i++) { KeyValuePair item = obj.data.dictionary.items[i]; @@ -716,20 +713,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) api_set_error(err, Validation, _("Empty dictionary keys aren't allowed")); // cleanup - dict_free(dict); + tv_dict_free(dict); return false; } - dictitem_T *di = dictitem_alloc((uint8_t *)key.data); + dictitem_T *const di = tv_dict_item_alloc(key.data); if (!object_to_vim(item.value, &di->di_tv, err)) { // cleanup - dictitem_free(di); - dict_free(dict); + tv_dict_item_free(di); + tv_dict_free(dict); return false; } - dict_add(dict, di); + tv_dict_add(dict, di); } dict->dv_refcount++; @@ -960,11 +957,7 @@ static void set_option_value_err(char *key, { char *errmsg; - if ((errmsg = (char *)set_option_value((uint8_t *)key, - numval, - (uint8_t *)stringval, - opt_flags))) - { + if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { if (try_end(err)) { return; } diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 59c0200395..db2f25a2a6 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -185,7 +185,7 @@ Object nvim_eval(String expr, Error *err) typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); if (!expr_result) { - api_set_error(err, Exception, _("Failed to evaluate expression")); + api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 44ff540b40..31851a84e6 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -8,9 +8,11 @@ // Definitions of various common control characters. -#define CharOrd(x) ((x) < 'a' ? (x) - 'A' : (x) - 'a') -#define CharOrdLow(x) ((x) - 'a') -#define CharOrdUp(x) ((x) - 'A') +#define CharOrd(x) ((uint8_t)(x) < 'a' \ + ? (uint8_t)(x) - 'A'\ + : (uint8_t)(x) - 'a') +#define CharOrdLow(x) ((uint8_t)(x) - 'a') +#define CharOrdUp(x) ((uint8_t)(x) - 'A') #define ROT13(c, a) (((((c) - (a)) + 13) % 26) + (a)) #define NUL '\000' diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f7333fead4..0ce9ce073e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -661,7 +661,7 @@ static void free_buffer(buf_T *buf) free_buffer_stuff(buf, true); unref_var_dict(buf->b_vars); aubuflocal_remove(buf); - dict_unref(buf->additional_data); + tv_dict_unref(buf->additional_data); clear_fmark(&buf->b_last_cursor); clear_fmark(&buf->b_last_insert); clear_fmark(&buf->b_last_change); @@ -1481,7 +1481,7 @@ static inline void buf_init_changedtick(buf_T *const buf) }, .di_key = "changedtick", }; - dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); + tv_dict_add(buf->b_vars, (dictitem_T *)&buf->changedtick_di); } /// Add a file name to the buffer list. @@ -1573,7 +1573,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) if (buf != curbuf || curbuf == NULL) { buf = xcalloc(1, sizeof(buf_T)); // init b: variables - buf->b_vars = dict_alloc(); + buf->b_vars = tv_dict_alloc(); init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); buf_init_changedtick(buf); } @@ -5443,8 +5443,8 @@ void buf_open_scratch(handle_T bufnr, char *bufname) { (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL); (void)setfname(curbuf, (char_u *)bufname, NULL, true); - set_option_value((char_u *)"bh", 0L, (char_u *)"hide", OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); + set_option_value("bh", 0L, "hide", OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); RESET_BINDING(curwin); } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ed3e6ab6cc..c915d373aa 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -7,6 +7,7 @@ #include "nvim/screen.h" // for StlClickRecord #include "nvim/func_attr.h" #include "nvim/eval.h" +#include "nvim/macros.h" // Values for buflist_getfile() enum getf_values { @@ -91,8 +92,8 @@ static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) static inline void buf_set_changedtick(buf_T *const buf, const int changedtick) { #ifndef NDEBUG - dictitem_T *const changedtick_di = dict_find( - buf->b_vars, (char_u *)"changedtick", sizeof("changedtick") - 1); + dictitem_T *const changedtick_di = tv_dict_find( + buf->b_vars, S_LEN("changedtick")); assert(changedtick_di != NULL); assert(changedtick_di->di_tv.v_type == VAR_NUMBER); assert(changedtick_di->di_tv.v_lock == VAR_FIXED); diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 8d25b828e5..c69582c4c6 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -5,6 +5,16 @@ #include "nvim/pos.h" #include "nvim/buffer_defs.h" +/// Return the folded-case equivalent of the given character +/// +/// @param[in] c Character to transform. +/// +/// @return Folded variant. +#define CH_FOLD(c) \ + utf_fold((sizeof(c) == sizeof(char)) \ + ?((int)(uint8_t)(c)) \ + :((int)(c))) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 11ade20c1c..ff76abc01f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1586,7 +1586,7 @@ static int diff_cmp(char_u *s1, char_u *s2) } if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { - return mb_stricmp(s1, s2); + return mb_stricmp((const char *)s1, (const char *)s2); } // Ignore white space changes and possibly ignore case. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 77d5b2c816..53b196c61f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -2094,38 +2094,56 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int xfree(wca); - return ins_compl_add(IObuff, len, icase, fname, NULL, dir, - flags, FALSE); + return ins_compl_add(IObuff, len, icase, fname, NULL, true, dir, flags, + false); } - return ins_compl_add(str, len, icase, fname, NULL, dir, flags, FALSE); + return ins_compl_add(str, len, icase, fname, NULL, false, dir, flags, false); } -/* - * Add a match to the list of matches. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -static int -ins_compl_add ( - char_u *str, - int len, - int icase, - char_u *fname, - char_u **cptext, /* extra text for popup menu or NULL */ - int cdir, - int flags, - int adup /* accept duplicate match */ -) +/// Add a match to the list of matches +/// +/// @param[in] str Match to add. +/// @param[in] len Match length, -1 to use #STRLEN. +/// @param[in] icase Whether case is to be ignored. +/// @param[in] fname File name match comes from. May be NULL. +/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL, +/// must have exactly #CPT_COUNT items. +/// @param[in] cptext_allocated If true, will not copy cptext strings. +/// +/// @note Will free strings in case of error. +/// cptext itself will not be freed. +/// @param[in] cdir Completion direction. +/// @param[in] adup True if duplicate matches are to be accepted. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +static int ins_compl_add(char_u *const str, int len, + const bool icase, char_u *const fname, + char_u *const *const cptext, + const bool cptext_allocated, + const Direction cdir, int flags, const bool adup) + FUNC_ATTR_NONNULL_ARG(1) { compl_T *match; - int dir = (cdir == 0 ? compl_direction : cdir); + int dir = (cdir == kDirectionNotSet ? compl_direction : cdir); os_breakcheck(); - if (got_int) +#define FREE_CPTEXT(cptext, cptext_allocated) \ + do { \ + if (cptext_allocated) { \ + for (size_t i = 0; i < CPT_COUNT; i++) { \ + xfree(cptext[i]); \ + } \ + } \ + } while (0) + if (got_int) { + FREE_CPTEXT(cptext, cptext_allocated); return FAIL; - if (len < 0) + } + if (len < 0) { len = (int)STRLEN(str); + } /* * If the same match is already present, don't add it. @@ -2133,10 +2151,12 @@ ins_compl_add ( if (compl_first_match != NULL && !adup) { match = compl_first_match; do { - if ( !(match->cp_flags & ORIGINAL_TEXT) - && STRNCMP(match->cp_str, str, len) == 0 - && match->cp_str[len] == NUL) + if (!(match->cp_flags & ORIGINAL_TEXT) + && STRNCMP(match->cp_str, str, len) == 0 + && match->cp_str[len] == NUL) { + FREE_CPTEXT(cptext, cptext_allocated); return NOTDONE; + } match = match->cp_next; } while (match != NULL && match != compl_first_match); } @@ -2167,16 +2187,26 @@ ins_compl_add ( else if (fname != NULL) { match->cp_fname = vim_strsave(fname); flags |= FREE_FNAME; - } else + } else { match->cp_fname = NULL; + } match->cp_flags = flags; if (cptext != NULL) { int i; - for (i = 0; i < CPT_COUNT; ++i) - if (cptext[i] != NULL && *cptext[i] != NUL) - match->cp_text[i] = vim_strsave(cptext[i]); + for (i = 0; i < CPT_COUNT; i++) { + if (cptext[i] == NULL) { + continue; + } + if (*cptext[i] != NUL) { + match->cp_text[i] = (cptext_allocated + ? cptext[i] + : (char_u *)xstrdup((char *)cptext[i])); + } else if (cptext_allocated) { + xfree(cptext[i]); + } + } } /* @@ -2299,9 +2329,10 @@ static void ins_compl_add_matches(int num_matches, char_u **matches, int icase) for (i = 0; i < num_matches && add_r != FAIL; i++) if ((add_r = ins_compl_add(matches[i], -1, icase, - NULL, NULL, dir, 0, FALSE)) == OK) - /* if dir was BACKWARD then honor it just once */ + NULL, NULL, false, dir, 0, false)) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; + } FreeWild(num_matches, matches); } @@ -2365,8 +2396,8 @@ void set_completion(colnr_T startcol, list_T *list) /* compl_pattern doesn't need to be set */ compl_orig_text = vim_strnsave(get_cursor_line_ptr() + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { return; } @@ -2888,7 +2919,7 @@ static void ins_compl_clear(void) compl_orig_text = NULL; compl_enter_selects = FALSE; // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } /// Check that Insert completion is active. @@ -3486,7 +3517,7 @@ expand_by_function ( theend: if (matchdict != NULL) { - dict_unref(matchdict); + tv_dict_unref(matchdict); } if (matchlist != NULL) { tv_list_unref(matchlist); @@ -3519,53 +3550,60 @@ static void ins_compl_add_dict(dict_T *dict) dictitem_T *di_refresh; dictitem_T *di_words; - /* Check for optional "refresh" item. */ - compl_opt_refresh_always = FALSE; - di_refresh = dict_find(dict, (char_u *)"refresh", 7); + // Check for optional "refresh" item. + compl_opt_refresh_always = false; + di_refresh = tv_dict_find(dict, S_LEN("refresh")); if (di_refresh != NULL && di_refresh->di_tv.v_type == VAR_STRING) { - char_u *v = di_refresh->di_tv.vval.v_string; + const char *v = (const char *)di_refresh->di_tv.vval.v_string; - if (v != NULL && STRCMP(v, (char_u *)"always") == 0) - compl_opt_refresh_always = TRUE; + if (v != NULL && strcmp(v, "always") == 0) { + compl_opt_refresh_always = true; + } } - /* Add completions from a "words" list. */ - di_words = dict_find(dict, (char_u *)"words", 5); - if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) + // Add completions from a "words" list. + di_words = tv_dict_find(dict, S_LEN("words")); + if (di_words != NULL && di_words->di_tv.v_type == VAR_LIST) { ins_compl_add_list(di_words->di_tv.vval.v_list); + } } -/* - * Add a match to the list of matches from a typeval_T. - * If the given string is already in the list of completions, then return - * NOTDONE, otherwise add it to the list and return OK. If there is an error - * then FAIL is returned. - */ -int ins_compl_add_tv(typval_T *tv, int dir) -{ - char_u *word; - int icase = FALSE; - int adup = FALSE; - int aempty = FALSE; - char_u *(cptext[CPT_COUNT]); +/// Add a match to the list of matches from VimL object +/// +/// @param[in] tv Object to get matches from. +/// @param[in] dir Completion direction. +/// +/// @return NOTDONE if the given string is already in the list of completions, +/// otherwise it is added to the list and OK is returned. FAIL will be +/// returned in case of error. +int ins_compl_add_tv(typval_T *const tv, const Direction dir) + FUNC_ATTR_NONNULL_ALL +{ + const char *word; + bool icase = false; + bool adup = false; + bool aempty = false; + char *(cptext[CPT_COUNT]); if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - word = get_dict_string(tv->vval.v_dict, "word", false); - cptext[CPT_ABBR] = get_dict_string(tv->vval.v_dict, "abbr", false); - cptext[CPT_MENU] = get_dict_string(tv->vval.v_dict, "menu", false); - cptext[CPT_KIND] = get_dict_string(tv->vval.v_dict, "kind", false); - cptext[CPT_INFO] = get_dict_string(tv->vval.v_dict, "info", false); - - icase = get_dict_number(tv->vval.v_dict, "icase"); - adup = get_dict_number(tv->vval.v_dict, "dup"); - aempty = get_dict_number(tv->vval.v_dict, "empty"); + word = tv_dict_get_string(tv->vval.v_dict, "word", false); + cptext[CPT_ABBR] = tv_dict_get_string(tv->vval.v_dict, "abbr", true); + cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); + cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); + cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + + icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); + adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); + aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = get_tv_string_chk(tv); + word = (const char *)get_tv_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } - if (word == NULL || (!aempty && *word == NUL)) + if (word == NULL || (!aempty && *word == NUL)) { return FAIL; - return ins_compl_add(word, -1, icase, NULL, cptext, dir, 0, adup); + } + return ins_compl_add((char_u *)word, -1, icase, NULL, + (char_u **)cptext, true, dir, 0, adup); } /* @@ -3980,7 +4018,7 @@ static void ins_compl_delete(void) // causes flicker, thus we can't do that. changed_cline_bef_curs(); // clear v:completed_item - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); } // Insert the new text being completed. @@ -3995,7 +4033,7 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict_add_nr_str(dict, "word", 0L, EMPTY_IF_NULL(compl_shown_match->cp_str)); dict_add_nr_str(dict, "abbr", 0L, @@ -4667,8 +4705,8 @@ static int ins_complete(int c, bool enable_pum) /* Always add completion for the original text. */ xfree(compl_orig_text); compl_orig_text = vim_strnsave(line + compl_col, compl_length); - if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, 0, - ORIGINAL_TEXT, FALSE) != OK) { + if (ins_compl_add(compl_orig_text, -1, p_ic, NULL, NULL, false, 0, + ORIGINAL_TEXT, false) != OK) { xfree(compl_pattern); compl_pattern = NULL; xfree(compl_orig_text); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 03d6eef9e3..5015deead7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -97,6 +97,7 @@ #include "nvim/eval/typval.h" #include "nvim/eval/executor.h" #include "nvim/eval/gc.h" +#include "nvim/macros.h" // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead @@ -432,21 +433,6 @@ static ScopeDictDictItem vimvars_var; /// v: hashtab #define vimvarht vimvardict.dv_hashtab -typedef enum { - kCallbackNone, - kCallbackFuncref, - kCallbackPartial, -} CallbackType; - -typedef struct { - union { - char_u *funcref; - partial_T *partial; - } data; - CallbackType type; -} Callback; -#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) - typedef struct { union { LibuvProcess uv; @@ -464,13 +450,6 @@ typedef struct { MultiQueue *events; } TerminalJobData; -typedef struct dict_watcher { - Callback callback; - char *key_pattern; - QUEUE node; - bool busy; // prevent recursion if the dict is changed in the callback -} DictWatcher; - typedef struct { TerminalJobData *data; Callback *callback; @@ -596,19 +575,19 @@ void eval_init(void) } vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - dict_T *const msgpack_types_dict = dict_alloc(); + dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { list_T *const type_list = tv_list_alloc(); type_list->lv_lock = VAR_FIXED; type_list->lv_refcount = 1; - dictitem_T *const di = dictitem_alloc((char_u *)msgpack_type_names[i]); + dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { .v_type = VAR_LIST, .vval = { .v_list = type_list, }, }; eval_msgpack_type_lists[i] = type_list; - if (dict_add(msgpack_types_dict, di) == FAIL) { + if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. assert(false); } @@ -616,9 +595,9 @@ void eval_init(void) msgpack_types_dict->dv_lock = VAR_FIXED; set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); - set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); + set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc()); - dict_T *v_event = dict_alloc(); + dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); set_vim_var_list(VV_ERRORS, tv_list_alloc()); @@ -745,7 +724,7 @@ void set_internal_string_var(char_u *name, char_u *value) tvp->v_type = VAR_STRING; tvp->vval.v_string = val; - set_var(name, tvp, FALSE); + set_var((const char *)name, tvp, false); free_tv(tvp); } @@ -1689,7 +1668,7 @@ static void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, for (hi = ht->ht_array; todo > 0 && !got_int; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (empty || di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string != NULL) { list_one_var(di, prefix, first); @@ -1951,7 +1930,7 @@ ex_let_one ( } } if (s != NULL) { - set_option_value(arg, n, s, opt_flags); + set_option_value((const char *)arg, n, (char *)s, opt_flags); arg_end = (char_u *)p; } *p = c1; @@ -2220,7 +2199,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; - lp->ll_di = dict_find(lp->ll_dict, key, len); + lp->ll_di = tv_dict_find(lp->ll_dict, (const char *)key, len); /* When assigning to a scope dictionary check that a function and * variable name is valid (only variable name unless it is l: or @@ -2232,16 +2211,19 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len != -1) { prevval = key[len]; key[len] = NUL; - } else - prevval = 0; /* avoid compiler warning */ - wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC - && var_check_func_name(key, lp->ll_di == NULL)) - || !valid_varname(key); - if (len != -1) + } else { + prevval = 0; // Avoid compiler warning. + } + wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && rettv->v_type == VAR_FUNC + && !var_check_func_name((const char *)key, lp->ll_di == NULL)) + || !valid_varname((const char *)key)); + if (len != -1) { key[len] = prevval; - if (wrong) + } + if (wrong) { return NULL; + } } if (lp->ll_di == NULL) { @@ -2391,12 +2373,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var(lp->ll_name, &tv, false); + set_var((const char *)lp->ll_name, &tv, false); } tv_clear(&tv); } } else { - set_var(lp->ll_name, rettv, copy); + set_var((const char *)lp->ll_name, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2450,26 +2432,20 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch : lp->ll_n1 != lp->ll_n2) EMSG(_("E711: List value has not enough items")); } else { - typval_T oldtv; + typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + bool watched = tv_dict_is_watched(dict); - /* - * Assign to a List or Dictionary item. - */ + // Assign to a List or Dictionary item. if (lp->ll_newkey != NULL) { if (op != NULL && *op != '=') { EMSG2(_(e_letwrong), op); return; } - /* Need to add an item to the Dictionary. */ - di = dictitem_alloc(lp->ll_newkey); - if (dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { + // Need to add an item to the Dictionary. + di = tv_dict_item_alloc((const char *)lp->ll_newkey); + if (tv_dict_add(lp->ll_tv->vval.v_dict, di) == FAIL) { xfree(di); return; } @@ -2493,16 +2469,16 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, ch } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; - init_tv(rettv); + tv_init(rettv); } notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); + tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; - dictwatcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } } @@ -2806,7 +2782,7 @@ void ex_call(exarg_T *eap) } end: - dict_unref(fudi.fd_dict); + tv_dict_unref(fudi.fd_dict); xfree(tofree); } @@ -2944,7 +2920,7 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) // unlet a Dictionary item. dict_T *d = lp->ll_dict; dictitem_T *di = lp->ll_di; - bool watched = is_watched(d); + bool watched = tv_dict_is_watched(d); char *key = NULL; typval_T oldtv; @@ -2954,10 +2930,10 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) key = xstrdup((char *)di->di_key); } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); if (watched) { - dictwatcher_notify(d, key, NULL, &oldtv); + tv_dict_watcher_notify(d, key, NULL, &oldtv); tv_clear(&oldtv); xfree(key); } @@ -3005,7 +2981,7 @@ int do_unlet(char_u *name, int forceit) hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3018,7 +2994,7 @@ int do_unlet(char_u *name, int forceit) } typval_T oldtv; - bool watched = is_watched(dict); + bool watched = tv_dict_is_watched(dict); if (watched) { copy_tv(&di->di_tv, &oldtv); @@ -3027,7 +3003,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - dictwatcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3102,18 +3078,12 @@ static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, */ void del_menutrans_vars(void) { - hashitem_T *hi; - int todo; - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); + HASHTAB_ITER(&globvarht, hi, { + if (STRNCMP(hi->hi_key, "menutrans_", 10) == 0) { + delete_var(&globvarht, hi); } - } + }); hash_unlock(&globvarht); } @@ -3659,11 +3629,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) tv_clear(&var2); return FAIL; } else { - /* Compare two Dictionaries for being equal or unequal. */ - n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, - ic, FALSE); - if (type == TYPE_NEQUAL) + // Compare two Dictionaries for being equal or unequal. + n1 = tv_dict_equal(rettv->vval.v_dict, var2.vval.v_dict, + ic, false); + if (type == TYPE_NEQUAL) { n1 = !n1; + } } } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL @@ -3749,11 +3720,12 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) } else { s1 = get_tv_string_buf(rettv, buf1); s2 = get_tv_string_buf(&var2, buf2); - if (type != TYPE_MATCH && type != TYPE_NOMATCH) - i = ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2); - else + if (type != TYPE_MATCH && type != TYPE_NOMATCH) { + i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + } else { i = 0; - n1 = FALSE; + } + n1 = false; switch (type) { case TYPE_EQUAL: n1 = (i == 0); break; case TYPE_NEQUAL: n1 = (i != 0); break; @@ -4355,11 +4327,11 @@ eval_index ( int verbose /* give error messages */ ) { - int empty1 = FALSE, empty2 = FALSE; - typval_T var1, var2; + bool empty1 = false; + bool empty2 = false; long n1, n2 = 0; long len = -1; - int range = FALSE; + int range = false; char_u *s; char_u *key = NULL; @@ -4397,8 +4369,8 @@ eval_index ( } } - init_tv(&var1); - init_tv(&var2); + typval_T var1 = TV_INITIAL_VALUE; + typval_T var2 = TV_INITIAL_VALUE; if (**arg == '.') { /* * dict.name @@ -4576,7 +4548,7 @@ eval_index ( } } - item = dict_find(rettv->vval.v_dict, key, (int)len); + item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4868,7 +4840,7 @@ static void partial_free(partial_T *pt) tv_clear(&pt->pt_argv[i]); } xfree(pt->pt_argv); - dict_unref(pt->pt_dict); + tv_dict_unref(pt->pt_dict); if (pt->pt_name != NULL) { func_unref(pt->pt_name); xfree(pt->pt_name); @@ -4940,64 +4912,7 @@ failret: return OK; } - -// TODO(ZyX-I): move to eval/typval - -/* - * Return the dictitem that an entry in a hashtable points to. - */ -dictitem_T *dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -// TODO(ZyX-I): move to eval/typval - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ -static int -dict_equal ( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive /* TRUE when used recursively */ -) -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL && d2 == NULL) { - return true; - } - if (d1 == NULL || d2 == NULL) { - return false; - } - if (d1 == d2) { - return true; - } - if (dict_len(d1) != dict_len(d2)) { - return false; - } - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - -static int tv_equal_recurse_limit; - -static bool func_equal( +bool func_equal( typval_T *tv1, typval_T *tv2, bool ic // ignore case @@ -5032,7 +4947,7 @@ static bool func_equal( if (d1 != d2) { return false; } - } else if (!dict_equal(d1, d2, ic, true)) { + } else if (!tv_dict_equal(d1, d2, ic, true)) { return false; } @@ -5051,90 +4966,6 @@ static bool func_equal( return true; } -/* - * Return TRUE if "tv1" and "tv2" have the same value. - * Compares the items just like "==" would compare them, but strings and - * numbers are different. Floats and numbers are also different. - */ -int tv_equal( - typval_T *tv1, - typval_T *tv2, - int ic, /* ignore case */ - int recursive /* TRUE when used recursively */ -) -{ - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; - char_u *s1, *s2; - static int recursive_cnt = 0; /* catch recursive loops */ - int r; - - /* Catch lists and dicts that have an endless loop by limiting - * recursiveness to a limit. We guess they are equal then. - * A fixed limit has the problem of still taking an awful long time. - * Reduce the limit every time running into it. That should work fine for - * deeply linked structures that are not recursively linked and catch - * recursiveness quickly. */ - if (!recursive) - tv_equal_recurse_limit = 1000; - if (recursive_cnt >= tv_equal_recurse_limit) { - --tv_equal_recurse_limit; - return TRUE; - } - - // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and - // arguments. - if ((tv1->v_type == VAR_FUNC - || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL)) - && (tv2->v_type == VAR_FUNC - || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) { - recursive_cnt++; - r = func_equal(tv1, tv2, ic); - recursive_cnt--; - return r; - } - if (tv1->v_type != tv2->v_type) { - return false; - } - - switch (tv1->v_type) { - case VAR_LIST: - recursive_cnt++; - r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, true); - recursive_cnt--; - return r; - - case VAR_DICT: - ++recursive_cnt; - r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE); - --recursive_cnt; - return r; - - case VAR_NUMBER: - return tv1->vval.v_number == tv2->vval.v_number; - - case VAR_FLOAT: - return tv1->vval.v_float == tv2->vval.v_float; - - case VAR_STRING: - s1 = get_tv_string_buf(tv1, buf1); - s2 = get_tv_string_buf(tv2, buf2); - return (ic ? mb_stricmp(s1, s2) : STRCMP(s1, s2)) == 0; - - case VAR_SPECIAL: - return tv1->vval.v_special == tv2->vval.v_special; - - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_UNKNOWN: - // VAR_UNKNOWN can be the result of an invalid expression, let’s say it does - // not equal anything, not even self. - return false; - } - - assert(false); - return false; -} - /// Get next (unique) copy ID /// /// Used for traversing nested structures e.g. when serializing them or garbage @@ -5400,7 +5231,7 @@ static int free_unref_items(int copyID) // Free the Dictionary and ordinary items it contains, but don't // recurse into Lists and Dictionaries, they will be in the list // of dicts or list of lists. - dict_free_contents(dd); + tv_dict_free_contents(dd); did_free = true; } } @@ -5423,7 +5254,7 @@ static int free_unref_items(int copyID) for (dd = gc_first_dict; dd != NULL; dd = dd_next) { dd_next = dd->dv_used_next; if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - dict_free_dict(dd); + tv_dict_free_dict(dd); } } @@ -5460,14 +5291,10 @@ bool set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack) // Mark each item in the hashtab. If the item contains a hashtab // it is added to ht_stack, if it contains a list it is added to // list_stack. - int todo = (int)cur_ht->ht_used; - for (hashitem_T *hi = cur_ht->ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, &ht_stack, - list_stack); - } - } + HASHTAB_ITER(cur_ht, hi, { + abort = abort || set_ref_in_item( + &TV_DICT_HI2DI(hi)->di_tv, copyID, &ht_stack, list_stack); + }); } if (ht_stack == NULL) { @@ -5559,7 +5386,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, QUEUE *w = NULL; DictWatcher *watcher = NULL; QUEUE_FOREACH(w, &dd->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack); } } @@ -5704,265 +5531,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/// Allocate an empty header for a dictionary. -dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET -{ - dict_T *d = xmalloc(sizeof(dict_T)); - - // Add the dict to the list of dicts for garbage collection. - if (gc_first_dict != NULL) { - gc_first_dict->dv_used_prev = d; - } - d->dv_used_next = gc_first_dict; - d->dv_used_prev = NULL; - gc_first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = VAR_UNLOCKED; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - QUEUE_INIT(&d->watchers); - - return d; -} - -/* - * Allocate an empty dict for a return value. - */ -static void rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - rettv->v_lock = VAR_UNLOCKED; - d->dv_refcount++; -} - -/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -/// -/// @param d The Dictionary to clear -void dict_clear(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - - size_t todo = d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - dictitem_free(HI2DI(hi)); - hash_remove(&d->dv_hashtab, hi); - todo--; - } - } - - hash_unlock(&d->dv_hashtab); -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ -void dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) { - dict_free(d); - } -} - -/// Free a Dictionary, including all items it contains. -/// Ignores the reference count. -static void dict_free_contents(dict_T *const d) - FUNC_ATTR_NONNULL_ALL -{ - // Lock the hashtab, we don't want it to resize while freeing items. - hash_lock(&d->dv_hashtab); - assert(d->dv_hashtab.ht_locked > 0); - size_t todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - // Remove the item before deleting it, just in case there is - // something recursive causing trouble. - dictitem_T *const di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - dictitem_free(di); - todo--; - } - } - - while (!QUEUE_EMPTY(&d->watchers)) { - QUEUE *w = QUEUE_HEAD(&d->watchers); - DictWatcher *watcher = dictwatcher_node_data(w); - QUEUE_REMOVE(w); - dictwatcher_free(watcher); - } - - hash_clear(&d->dv_hashtab); -} - -static void dict_free_dict(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - // Remove the dict from the list of dicts for garbage collection. - if (d->dv_used_prev == NULL) { - gc_first_dict = d->dv_used_next; - } else { - d->dv_used_prev->dv_used_next = d->dv_used_next; - } - if (d->dv_used_next != NULL) { - d->dv_used_next->dv_used_prev = d->dv_used_prev; - } - - xfree(d); -} - -void dict_free(dict_T *d) - FUNC_ATTR_NONNULL_ALL -{ - if (!tv_in_free_unref_items) { - dict_free_contents(d); - dict_free_dict(d); - } -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - */ -dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); -#ifndef __clang_analyzer__ - STRCPY(di->di_key, key); -#endif - di->di_flags = DI_FLAGS_ALLOC; - return di; -} - -/* - * Make a copy of a Dictionary item. - */ -static dictitem_T *dictitem_copy(dictitem_T *org) FUNC_ATTR_NONNULL_RET -{ - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(org->di_key)); - - STRCPY(di->di_key, org->di_key); - di->di_flags = DI_FLAGS_ALLOC; - copy_tv(&org->di_tv, &di->di_tv); - - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ -static void dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) { - EMSG2(_(e_intern2), "dictitem_remove()"); - } else { - hash_remove(&dict->dv_hashtab, hi); - } - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ -void dictitem_free(dictitem_T *item) -{ - tv_clear(&item->di_tv); - if (item->di_flags & DI_FLAGS_ALLOC) { - xfree(item); - } -} - -/// Make a copy of dictionary -/// -/// @param[in] conv If non-NULL, then all internal strings will be converted. -/// @param[in] orig Original dictionary to copy. -/// @param[in] deep If false, then shallow copy will be done. -/// @param[in] copyID See var_item_copy(). -/// -/// @return Copied dictionary. May be NULL in case original dictionary is NULL -/// or some failure happens. The refcount of the new dictionary is set -/// to 1. -static dict_T *dict_copy(const vimconv_T *const conv, - dict_T *const orig, - const bool deep, - const int copyID) -{ - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - dict_T *copy = dict_alloc(); - { - if (copyID != 0) { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - - if (conv == NULL || conv->vc_type == CONV_NONE) { - di = dictitem_alloc(hi->hi_key); - } else { - char *const key = (char *) string_convert((vimconv_T *) conv, - hi->hi_key, NULL); - if (key == NULL) { - di = dictitem_alloc(hi->hi_key); - } else { - di = dictitem_alloc((char_u *) key); - xfree(key); - } - } - if (deep) { - if (var_item_copy(conv, &HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) { - xfree(di); - break; - } - } else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - /* * Add a number or string entry to dictionary "d". * When "str" is NULL use number "nr", otherwise use "str". @@ -5972,7 +5540,7 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) { dictitem_T *item; - item = dictitem_alloc((char_u *)key); + item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; if (str == NULL) { item->di_tv.v_type = VAR_NUMBER; @@ -5981,8 +5549,8 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) item->di_tv.v_type = VAR_STRING; item->di_tv.vval.v_string = vim_strsave(str); } - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } return OK; @@ -5994,13 +5562,13 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) */ int dict_add_list(dict_T *d, char *key, list_T *list) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } ++list->lv_refcount; @@ -6011,13 +5579,13 @@ int dict_add_list(dict_T *d, char *key, list_T *list) /// Returns FAIL when out of memory and when key already exists. int dict_add_dict(dict_T *d, char *key, dict_T *dict) { - dictitem_T *item = dictitem_alloc((char_u *)key); + dictitem_T *const item = tv_dict_item_alloc(key); item->di_tv.v_lock = 0; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); return FAIL; } dict->dv_refcount++; @@ -6029,60 +5597,12 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) /// This does not protect against adding new keys to the Dictionary. /// /// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *dict) +void dict_set_keys_readonly(dict_T *const dict) FUNC_ATTR_NONNULL_ALL { - size_t todo = dict->dv_hashtab.ht_used; - for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) { - if (HASHITEM_EMPTY(hi)) { - continue; - } - todo--; - HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - } -} - -/* - * Get the number of items in a Dictionary. - */ -static long dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ -dictitem_T *dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (d == NULL) { - return NULL; - } - if (len < 0) { - akey = key; - } else if (len >= AKEYLEN) { - tofree = akey = vim_strnsave(key, len); - } else { - /* Avoid a malloc/free by using buf[]. */ - STRLCPY(buf, key, len + 1); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - xfree(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); } /// Get a function from a dictionary @@ -6091,7 +5611,7 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len) /// @return true/false on success/failure. static bool get_dict_callback(dict_T *d, char *key, Callback *result) { - dictitem_T *di = dict_find(d, (uint8_t *)key, -1); + dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { result->type = kCallbackNone; @@ -6113,40 +5633,6 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) return res; } -/// Get a string item from a dictionary. -/// -/// @param save whether memory should be allocated for the return value -/// when false a shared buffer is used, can only be used once! -/// -/// @return the entry or NULL if the entry doesn't exist. -char_u *get_dict_string(dict_T *d, char *key, bool save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return NULL; - } - s = get_tv_string(&di->di_tv); - if (save) { - s = vim_strsave(s); - } - return s; -} - -/// Get a number item from a dictionary. -/// -/// @return the entry or 0 if the entry doesn't exist. -long get_dict_number(dict_T *d, char *key) -{ - dictitem_T *di = dict_find(d, (char_u *)key, -1); - if (di == NULL) { - return 0; - } - return get_tv_number(&di->di_tv); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -6176,7 +5662,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) } if (evaluate) { - d = dict_alloc(); + d = tv_dict_alloc(); } tvkey.v_type = VAR_UNKNOWN; tv.v_type = VAR_UNKNOWN; @@ -6207,19 +5693,19 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - item = dict_find(d, key, -1); + item = tv_dict_find(d, (const char *)key, -1); if (item != NULL) { EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); tv_clear(&tvkey); tv_clear(&tv); goto failret; } - item = dictitem_alloc(key); + item = tv_dict_item_alloc((const char *)key); tv_clear(&tvkey); item->di_tv = tv; item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) { - dictitem_free(item); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); } } @@ -6236,7 +5722,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); failret: if (evaluate) { - dict_free(d); + tv_dict_free(d); } return FAIL; } @@ -8152,9 +7638,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) todo = error ? 0 : (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE)) - ++n; + todo--; + if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) { + n++; + } } } } @@ -8381,7 +7868,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) DictWatcher *watcher = NULL; bool matched = false; QUEUE_FOREACH(w, &dict->watchers) { - watcher = dictwatcher_node_data(w); + watcher = tv_dict_watcher_node_data(w); if (callback_equal(&watcher->callback, &callback) && !strcmp(watcher->key_pattern, key_pattern)) { matched = true; @@ -8397,7 +7884,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } QUEUE_REMOVE(w); - dictwatcher_free(watcher); + tv_dict_watcher_free(watcher); } /* @@ -8750,92 +8237,24 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN && get_tv_number_chk(&argvars[1], &error)) options |= WILD_KEEP_ALL; - if (!error) { - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) - options += WILD_ICASE; - if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); - } else { - tv_list_alloc_ret(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); - for (int i = 0; i < xpc.xp_numfiles; i++) { - tv_list_append_string(rettv->vval.v_list, - (const char *)xpc.xp_files[i], -1); - } - ExpandCleanup(&xpc); - } - } else - rettv->vval.v_string = NULL; - } -} - -/* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ -void dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - bool watched = is_watched(d1); - const char *const arg_errmsg = _("extend() argument"); - const size_t arg_errmsg_len = strlen(arg_errmsg); - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { - if (!HASHITEM_EMPTY(hi2)) { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) { - /* 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 - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) { - di1 = dictitem_copy(HI2DI(hi2)); - if (dict_add(d1, di1) == FAIL) { - dictitem_free(di1); - } - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, NULL); - } - } else if (*action == 'e') { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } else if (*action == 'f' && HI2DI(hi2) != di1) { - typval_T oldtv; - - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) - || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { - break; - } - - if (watched) { - copy_tv(&di1->di_tv, &oldtv); - } - - tv_clear(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - - if (watched) { - dictwatcher_notify(d1, (char *)di1->di_key, &di1->di_tv, &oldtv); - tv_clear(&oldtv); + if (!error) { + ExpandInit(&xpc); + xpc.xp_context = EXPAND_FILES; + if (p_wic) + options += WILD_ICASE; + if (rettv->v_type == VAR_STRING) { + rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + } else { + tv_list_alloc_ret(rettv); + ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + for (int i = 0; i < xpc.xp_numfiles; i++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)xpc.xp_files[i], -1); } + ExpandCleanup(&xpc); } - } + } else + rettv->vval.v_string = NULL; } } @@ -8880,37 +8299,41 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1, *d2; - char_u *action; - int i; + dict_T *d1; + dict_T *d2; d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) && d2 != NULL) { - /* Check the third argument. */ + const char *action = "force"; + // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { - static char *(av[]) = {"keep", "force", "error"}; + const char *const av[] = { "keep", "force", "error" }; - action = get_tv_string_chk(&argvars[2]); - if (action == NULL) - return; /* type error; errmsg already given */ - for (i = 0; i < 3; ++i) - if (STRCMP(action, av[i]) == 0) + action = (const char *)get_tv_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { break; + } + } if (i == 3) { EMSG2(_(e_invarg2), action); return; } - } else - action = (char_u *)"force"; + } - dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); copy_tv(&argvars[0], rettv); } - } else + } else { EMSG2(_(e_listdictarg), "extend()"); + } } /* @@ -9076,7 +8499,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) if (!HASHITEM_EMPTY(hi)) { --todo; - di = HI2DI(hi); + di = TV_DICT_HI2DI(hi); if (map && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len))) { @@ -9094,7 +8517,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) || var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { break; } - dictitem_remove(d, di); + tv_dict_item_remove(d, di); } } } @@ -9639,9 +9062,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = dict_find(d, get_tv_string(&argvars[1]), -1); - if (di != NULL) + di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + if (di != NULL) { tv = &di->di_tv; + } } } else if (argvars[0].v_type == VAR_PARTIAL || argvars[0].v_type == VAR_FUNC) { @@ -9704,7 +9128,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_buffer_signs(buf_T *buf, list_T *l) { for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *const d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); dict_add_nr_str(d, "id", sign->id, NULL); dict_add_nr_str(d, "lnum", sign->lnum, NULL); @@ -9717,7 +9141,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) /// Returns buffer options, variables and other attributes in a dictionary. static dict_T *get_buffer_info(buf_T *buf) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); dict_add_nr_str(dict, "name", 0L, @@ -9772,12 +9196,12 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; - di = dict_find(sel_d, (char_u *)"buflisted", -1); + di = tv_dict_find(sel_d, S_LEN("buflisted")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_buflisted = true; } - di = dict_find(sel_d, (char_u *)"bufloaded", -1); + di = tv_dict_find(sel_d, S_LEN("bufloaded")); if (di != NULL && get_tv_number(&di->di_tv)) { sel_bufloaded = true; } @@ -10047,7 +9471,7 @@ static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; @@ -10467,7 +9891,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, (void)get_errorlist(wp, -1, rettv->vval.v_list); } } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (is_qf || wp != NULL) { if (what_arg->v_type == VAR_DICT) { dict_T *d = what_arg->vval.v_dict; @@ -10499,7 +9923,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); while (cur != NULL) { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { @@ -10678,7 +10102,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// as a dictionary. static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tp_idx, NULL); @@ -10776,7 +10200,7 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// Returns information about a window as a dictionary. static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { - dict_T *dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict_add_nr_str(dict, "tabnr", tpnr, NULL); dict_add_nr_str(dict, "winnr", winnr, NULL); @@ -11286,8 +10710,9 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].vval.v_dict == NULL) return; - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - get_tv_string(&argvars[1]), -1) != NULL; + rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, + (const char *)get_tv_string(&argvars[1]), + -1) != NULL; } /// `haslocaldir([{win}[, {tab}]])` function @@ -11912,60 +11337,48 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void dict_list(typval_T *argvars, typval_T *rettv, int what) { - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); return; } - if ((d = argvars[0].vval.v_dict) == NULL) + dict_T *const d = argvars[0].vval.v_dict; + if (d == NULL) { return; + } tv_list_alloc_ret(rettv); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - di = HI2DI(hi); - - li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + TV_DICT_ITER(d, di, { + listitem_T *const li = tv_list_item_alloc(); + tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - /* keys() */ - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - ++l2->lv_refcount; + if (what == 0) { + // keys() + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + } else if (what == 1) { + // values() + copy_tv(&di->di_tv, &li->li_tv); + } else { + // items() + list_T *const l2 = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_list = l2; + l2->lv_refcount++; - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = 0; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - li2 = tv_list_item_alloc(); - tv_list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); - } + sub_li = tv_list_item_alloc(); + tv_list_append(l2, sub_li); + copy_tv(&di->di_tv, &sub_li->li_tv); } - } + }); } /// "id()" function @@ -12230,23 +11643,26 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *job_opts = NULL; - bool detach = false, rpc = false, pty = false; - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + bool detach = false; + bool rpc = false; + bool pty = false; + Callback on_stdout = CALLBACK_NONE; + Callback on_stderr = CALLBACK_NONE; + Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - detach = get_dict_number(job_opts, "detach") != 0; - rpc = get_dict_number(job_opts, "rpc") != 0; - pty = get_dict_number(job_opts, "pty") != 0; + detach = tv_dict_get_number(job_opts, "detach") != 0; + rpc = tv_dict_get_number(job_opts, "rpc") != 0; + pty = tv_dict_get_number(job_opts, "pty") != 0; if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); return; } - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); + char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; // The new cwd must be a directory. @@ -12268,15 +11684,15 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Process *proc = (Process *)&data->proc; if (pty) { - uint16_t width = get_dict_number(job_opts, "width"); + uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); if (width > 0) { data->proc.pty.width = width; } - uint16_t height = get_dict_number(job_opts, "height"); + uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); if (height > 0) { data->proc.pty.height = height; } - char *term = (char *)get_dict_string(job_opts, "TERM", true); + char *term = tv_dict_get_string(job_opts, "TERM", true); if (term) { data->proc.pty.term_name = term; } @@ -12537,7 +11953,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); break; case VAR_DICT: - rettv->vval.v_number = dict_len(argvars[0].vval.v_dict); + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); break; case VAR_UNKNOWN: case VAR_SPECIAL: @@ -12714,7 +12130,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) rettv->vval.v_string = str2special_save(rhs, FALSE); } else { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); if (rhs != NULL) { // Return a dictionary. char_u *lhs = str2special_save(mp->m_keys, true); @@ -12980,10 +12396,10 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -12996,8 +12412,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)grp, + (const char *)pat, prio, id, + NULL, (const char *)conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -13005,8 +12422,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; char_u buf[NUMBUFLEN]; - char_u *group; - group = get_tv_string_buf_chk(&argvars[0], buf); + const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -13036,10 +12452,10 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_dictreq)); return; } - if (dict_find(argvars[4].vval.v_dict, - (char_u *)"conceal", -1) != NULL) { - conceal_char = get_dict_string(argvars[4].vval.v_dict, - "conceal", false); + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = get_tv_string(&di->di_tv); } } } @@ -13054,8 +12470,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, - conceal_char); + rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, + l, (const char *)conceal_char); } /* @@ -13156,8 +12572,8 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) todo = (int)d->dv_hashtab.ht_used; for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { - --todo; - i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error); + todo--; + i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -13845,7 +13261,6 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) listitem_T *li; long idx; long end; - char_u *key; dict_T *d; dictitem_T *di; const char *const arg_errmsg = _("remove() argument"); @@ -13856,18 +13271,18 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - key = get_tv_string_chk(&argvars[1]); + const char *key = (const char *)get_tv_string_chk(&argvars[1]); if (key != NULL) { - di = dict_find(d, key, -1); + di = tv_dict_find(d, key, -1); if (di == NULL) { EMSG2(_(e_dictkey), key); } else if (!var_check_fixed(di->di_flags, arg_errmsg, arg_errmsg_len) && !var_check_ro(di->di_flags, arg_errmsg, arg_errmsg_len)) { *rettv = di->di_tv; - init_tv(&di->di_tv); - dictitem_remove(d, di); - if (is_watched(d)) { - dictwatcher_notify(d, (char *)key, NULL, rettv); + di->di_tv = TV_INITIAL_VALUE; + tv_dict_item_remove(d, di); + if (tv_dict_is_watched(d)) { + tv_dict_watcher_notify(d, key, NULL, rettv); } } } @@ -14967,7 +14382,6 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buf != NULL && varname != NULL && varp != NULL) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; aco_save_T aco; @@ -14976,9 +14390,9 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { - set_option_value((char_u *)varname, numval, strval, OPT_LOCAL); + set_option_value(varname, numval, strval, OPT_LOCAL); } // reset notion of buffer @@ -14987,7 +14401,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf_T *save_curbuf = curbuf; const size_t varname_len = STRLEN(varname); - char_u *const bufvarname = xmalloc(STRLEN(varname) + 3); + char *const bufvarname = xmalloc(varname_len + 3); curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); @@ -15002,7 +14416,6 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *d; dictitem_T *di; - char_u *csearch; if (argvars[0].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -15010,7 +14423,7 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if ((d = argvars[0].vval.v_dict) != NULL) { - csearch = get_dict_string(d, "char", false); + char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false); if (csearch != NULL) { if (enc_utf8) { int pcc[MAX_MCO]; @@ -15022,13 +14435,15 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) csearch, MB_PTR2LEN(csearch)); } - di = dict_find(d, (char_u *)"forward", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("forward")); + if (di != NULL) { set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + } - di = dict_find(d, (char_u *)"until", -1); - if (di != NULL) + di = tv_dict_find(d, S_LEN("until")); + if (di != NULL) { set_csearch_until(!!get_tv_number(&di->di_tv)); + } } } @@ -15252,11 +14667,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } - if (!(dict_find(d, (char_u *)"group", -1) != NULL - && (dict_find(d, (char_u *)"pattern", -1) != NULL - || dict_find(d, (char_u *)"pos1", -1) != NULL) - && dict_find(d, (char_u *)"priority", -1) != NULL - && dict_find(d, (char_u *)"id", -1) != NULL)) { + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { EMSG(_(e_invarg)); return; } @@ -15267,11 +14682,10 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; while (li != NULL) { int i = 0; - char_u buf[5]; - dictitem_T *di; d = li->li_tv.vval.v_dict; - if (dict_find(d, (char_u *)"pattern", -1) == NULL) { + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { if (s == NULL) { s = tv_list_alloc(); if (s == NULL) { @@ -15280,14 +14694,16 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // match from matchaddpos() - for (i = 1; i < 9; ++i) { - snprintf((char *)buf, sizeof(buf), (char *)"pos%d", i); - if ((di = dict_find(d, (char_u *)buf, -1)) != NULL) { - if (di->di_tv.v_type != VAR_LIST) { + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { return; } - tv_list_append_tv(s, &di->di_tv); + tv_list_append_tv(s, &pos_di->di_tv); s->lv_refcount++; } else { break; @@ -15295,23 +14711,31 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", true); - int priority = get_dict_number(d, "priority"); - int id = get_dict_number(d, "id"); - char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", true) - : NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in get_tv_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? (const char *)get_tv_string( + &conceal_di->di_tv) + : NULL); if (i == 0) { match_add(curwin, group, - get_dict_string(d, "pattern", false), + tv_dict_get_string(d, "pattern", false), priority, id, NULL, conceal); } else { match_add(curwin, group, NULL, priority, id, s, conceal); tv_list_unref(s); s = NULL; } - xfree(group); - xfree(conceal); li = li->li_next; } rettv->vval.v_number = 0; @@ -15471,35 +14895,31 @@ free_lstval: */ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *save_curtab; - tabpage_T *tp; - char_u *varname, *tabvarname; - typval_T *varp; - rettv->vval.v_number = 0; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - varname = get_tv_string_chk(&argvars[1]); - varp = &argvars[2]; - - if (varname != NULL && varp != NULL - && tp != NULL - ) { - save_curtab = curtab; - goto_tabpage_tp(tp, FALSE, FALSE); - - tabvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(tabvarname, "t:"); - STRCPY(tabvarname + 2, varname); - set_var(tabvarname, varp, TRUE); + tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + typval_T *const varp = &argvars[2]; + + if (varname != NULL && varp != NULL && tp != NULL) { + tabpage_T *const save_curtab = curtab; + goto_tabpage_tp(tp, false, false); + + const size_t varname_len = strlen(varname); + char *const tabvarname = xmalloc(varname_len + 3); + memcpy(tabvarname, "t:", 2); + memcpy(tabvarname + 2, varname, varname_len + 1); + set_var(tabvarname, varp, true); xfree(tabvarname); - /* Restore current tabpage */ - if (valid_tabpage(save_curtab)) - goto_tabpage_tp(save_curtab, FALSE, FALSE); + // Restore current tabpage. + if (valid_tabpage(save_curtab)) { + goto_tabpage_tp(save_curtab, false, false); + } } } @@ -15525,44 +14945,42 @@ static void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void setwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win; - win_T *save_curwin; - tabpage_T *save_curtab; - char_u *varname, *winvarname; - typval_T *varp; - char_u nbuf[NUMBUFLEN]; - tabpage_T *tp = NULL; - - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { return; + } - if (off == 1) + tabpage_T *tp = NULL; + if (off == 1) { tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + } else { tp = curtab; - win = find_win_by_nr(&argvars[off], tp); - varname = get_tv_string_chk(&argvars[off + 1]); - varp = &argvars[off + 2]; + } + win_T *const win = find_win_by_nr(&argvars[off], tp); + const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { + win_T *save_curwin; + tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; if (!need_switch_win || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { if (*varname == '&') { long numval; - char_u *strval; bool error = false; - ++varname; + varname++; numval = get_tv_number_chk(varp, &error); - strval = get_tv_string_buf_chk(varp, nbuf); + char_u nbuf[NUMBUFLEN]; + char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } } else { - winvarname = xmalloc(STRLEN(varname) + 3); - STRCPY(winvarname, "w:"); - STRCPY(winvarname + 2, varname); + const size_t varname_len = strlen(varname); + char *const winvarname = xmalloc(varname_len + 3); + memcpy(winvarname, "w:", 2); + memcpy(winvarname + 2, varname, varname_len + 1); set_var(winvarname, varp, true); xfree(winvarname); } @@ -17086,15 +16504,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; - char *cwd = "."; + const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; - char *new_cwd = (char *)get_dict_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false); + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((char_u *)cwd)) { + if (!os_isdir((const char_u *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -17131,7 +16549,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) // at this point the buffer has no terminal instance associated yet, so unset // the 'swapfile' option to ensure no swap file will be created curbuf->b_p_swf = false; - (void)setfname(curbuf, (uint8_t *)buf, NULL, true); + (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err; dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), @@ -17177,23 +16595,25 @@ static bool callback_from_typval(Callback *callback, typval_T *arg) /// Unref/free callback -static void callback_free(Callback *callback) +void callback_free(Callback *const callback) + FUNC_ATTR_NONNULL_ALL { switch (callback->type) { - case kCallbackFuncref: + case kCallbackFuncref: { func_unref(callback->data.funcref); xfree(callback->data.funcref); break; - - case kCallbackPartial: + } + case kCallbackPartial: { partial_unref(callback->data.partial); break; - - case kCallbackNone: + } + case kCallbackNone: { break; - - default: + } + default: { abort(); + } } callback->type = kCallbackNone; } @@ -17220,8 +16640,9 @@ static bool callback_equal(Callback *cb1, Callback *cb2) } } -static bool callback_call(Callback *callback, int argcount_in, - typval_T *argvars_in, typval_T *rettv) +bool callback_call(Callback *const callback, const int argcount_in, + typval_T *const argvars_in, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL { partial_T *partial; char_u *name; @@ -17290,8 +16711,9 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); return; } - if (dict_find(dict, (char_u *)"repeat", -1) != NULL) { - repeat = get_dict_number(dict, "repeat"); + dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); + if (di != NULL) { + repeat = get_tv_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -17353,13 +16775,11 @@ static void timer_due_cb(TimeWatcher *tw, void *data) timer_stop(timer); } - typval_T argv[2]; - init_tv(argv); + typval_T argv[2] = { TV_INITIAL_VALUE, TV_INITIAL_VALUE }; argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; - typval_T rettv; + typval_T rettv = TV_INITIAL_VALUE; - init_tv(&rettv); callback_call(&timer->callback, 1, argv, &rettv); tv_clear(&rettv); @@ -17614,7 +17034,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; list_T *list; @@ -17802,36 +17222,37 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; + dict_T *dict; if (argvars[0].v_type != VAR_DICT - || (dict = argvars[0].vval.v_dict) == NULL) - EMSG(_(e_invarg)); - else { - if (dict_find(dict, (char_u *)"lnum", -1) != NULL) { - curwin->w_cursor.lnum = get_dict_number(dict, "lnum"); + || (dict = argvars[0].vval.v_dict) == NULL) { + emsgf(_(e_invarg)); + } else { + dictitem_T *di; + if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { + curwin->w_cursor.lnum = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"col", -1) != NULL) { - curwin->w_cursor.col = get_dict_number(dict, "col"); + if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { + curwin->w_cursor.col = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"coladd", -1) != NULL) { - curwin->w_cursor.coladd = get_dict_number(dict, "coladd"); + if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { + curwin->w_cursor.coladd = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"curswant", -1) != NULL) { - curwin->w_curswant = get_dict_number(dict, "curswant"); + if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { + curwin->w_curswant = get_tv_number(&di->di_tv); curwin->w_set_curswant = false; } - if (dict_find(dict, (char_u *)"topline", -1) != NULL) { - set_topline(curwin, get_dict_number(dict, "topline")); + if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { + set_topline(curwin, get_tv_number(&di->di_tv)); } - if (dict_find(dict, (char_u *)"topfill", -1) != NULL) { - curwin->w_topfill = get_dict_number(dict, "topfill"); + if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { + curwin->w_topfill = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"leftcol", -1) != NULL) { - curwin->w_leftcol = get_dict_number(dict, "leftcol"); + if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { + curwin->w_leftcol = get_tv_number(&di->di_tv); } - if (dict_find(dict, (char_u *)"skipcol", -1) != NULL) { - curwin->w_skipcol = get_dict_number(dict, "skipcol"); + if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { + curwin->w_skipcol = get_tv_number(&di->di_tv); } check_cursor(); @@ -17854,7 +17275,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) { dict_T *dict; - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); @@ -18027,7 +17448,7 @@ static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "wordcount()" function static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv_dict_alloc(rettv); + tv_dict_alloc_ret(rettv); cursor_pos_info(rettv->vval.v_dict); } @@ -18895,10 +18316,10 @@ handle_subscript( } ret = FAIL; } - dict_unref(selfdict); + tv_dict_unref(selfdict); selfdict = NULL; - } else { /* **arg == '[' || **arg == '.' */ - dict_unref(selfdict); + } else { // **arg == '[' || **arg == '.' + tv_dict_unref(selfdict); if (rettv->v_type == VAR_DICT) { selfdict = rettv->vval.v_dict; if (selfdict != NULL) @@ -18919,7 +18340,7 @@ handle_subscript( set_selfdict(rettv, selfdict); } - dict_unref(selfdict); + tv_dict_unref(selfdict); return ret; } @@ -19016,7 +18437,7 @@ void free_tv(typval_T *varp) tv_list_unref(varp->vval.v_list); break; case VAR_DICT: - dict_unref(varp->vval.v_dict); + tv_dict_unref(varp->vval.v_dict); break; case VAR_SPECIAL: case VAR_NUMBER: @@ -19028,15 +18449,6 @@ void free_tv(typval_T *varp) } } -/* - * Set the value of a variable to NULL without freeing items. - */ -static void init_tv(typval_T *varp) -{ - if (varp != NULL) - memset(varp, 0, sizeof(typval_T)); -} - // TODO(ZyX-I): move to eval/typval /// Get the number value of a variable @@ -19057,44 +18469,53 @@ varnumber_T get_tv_number(const typval_T *const varp) varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) { - long n = 0L; + varnumber_T n = 0; switch (varp->v_type) { - case VAR_NUMBER: - return (long)(varp->vval.v_number); - case VAR_FLOAT: - EMSG(_("E805: Using a Float as a Number")); - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E703: Using a Funcref as a Number")); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) { - vim_str2nr(varp->vval.v_string, NULL, NULL, - STR2NR_ALL, &n, NULL, 0); + case VAR_NUMBER: { + return varp->vval.v_number; } - return n; - case VAR_LIST: - EMSG(_("E745: Using a List as a Number")); - break; - case VAR_DICT: - EMSG(_("E728: Using a Dictionary as a Number")); - break; - case VAR_SPECIAL: - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; + case VAR_FLOAT: { + EMSG(_("E805: Using a Float as a Number")); + break; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E703: Using a Funcref as a Number")); + break; + } + case VAR_STRING: { + if (varp->vval.v_string != NULL) { + long nr; + vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; + return n; + } + case VAR_LIST: { + EMSG(_("E745: Using a List as a Number")); + break; + } + case VAR_DICT: { + EMSG(_("E728: Using a Dictionary as a Number")); + break; + } + case VAR_SPECIAL: { + switch (varp->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); + break; } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; } if (denote == NULL) { // useful for values that must be unsigned @@ -19254,16 +18675,17 @@ static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { const char *varname; - hashtab_T *ht = find_var_ht(name, name_len, &varname); + hashtab_T *const ht = find_var_ht(name, name_len, &varname); if (htp != NULL) { *htp = ht; } if (ht == NULL) { return NULL; } - dictitem_T *ret = find_var_in_ht(ht, *name, - varname, name_len - (size_t)(varname - name), - no_autoload || htp != NULL); + dictitem_T *const ret = find_var_in_ht(ht, *name, + varname, + name_len - (size_t)(varname - name), + no_autoload || htp != NULL); if (ret != NULL) { return ret; } @@ -19327,7 +18749,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, return NULL; } } - return HI2DI(hi); + return TV_DICT_HI2DI(hi); } // Get function call environment based on backtrace debug level @@ -19528,7 +18950,7 @@ void unref_var_dict(dict_T *dict) /* Now the dict needs to be freed if no one else is using it, go back to * normal reference counting. */ dict->dv_refcount -= DO_NOT_FREE_CNT - 1; - dict_unref(dict); + tv_dict_unref(dict); } /* @@ -19559,7 +18981,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) // Free the variable. Don't remove it from the hashtab, // ht_array might change then. hash_clear() takes care of it // later. - v = HI2DI(hi); + v = TV_DICT_HI2DI(hi); if (free_val) { tv_clear(&v->di_tv); } @@ -19578,7 +19000,7 @@ static void vars_clear_ext(hashtab_T *ht, int free_val) */ static void delete_var(hashtab_T *ht, hashitem_T *hi) { - dictitem_T *di = HI2DI(hi); + dictitem_T *di = TV_DICT_HI2DI(hi); hash_remove(ht, hi); tv_clear(&di->di_tv); @@ -19637,40 +19059,31 @@ static void list_one_var_a(const char *prefix, const char *name, } } -/* - * Set variable "name" to value in "tv". - * If the variable already exists, the value is updated. - * Otherwise the variable is created. - */ -static void -set_var ( - char_u *name, - typval_T *tv, - int copy /* make copy of value in "tv" */ -) +/// Set variable to the given value +/// +/// If the variable already exists, the value is updated. Otherwise the variable +/// is created. +/// +/// @param[in] name Variable name to set. +/// @param tv Variable value. +/// @param[in] copy True if value in tv is to be copied. +static void set_var(const char *name, typval_T *const tv, const bool copy) + FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; - typval_T oldtv; dict_T *dict; - const size_t name_len = STRLEN(name); - char_u *varname; - ht = find_var_ht_dict((const char *)name, name_len, (const char **)&varname, - &dict); - bool watched = is_watched(dict); - - if (watched) { - init_tv(&oldtv); - } + const size_t name_len = strlen(name); + const char *varname; + ht = find_var_ht_dict(name, name_len, &varname, &dict); + const bool watched = tv_dict_is_watched(dict); if (ht == NULL || *varname == NUL) { EMSG2(_(e_illvar), name); return; } - v = find_var_in_ht(ht, 0, - (const char *)varname, name_len - (size_t)(varname - name), - true); + v = find_var_in_ht(ht, 0, varname, name_len - (size_t)(varname - name), true); // Search in parent scope which is possible to reference from lambda if (v == NULL) { @@ -19678,10 +19091,11 @@ set_var ( } if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && var_check_func_name(name, v == NULL)) { + && !var_check_func_name(name, v == NULL)) { return; } + typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value if (var_check_ro(v->di_flags, (const char *)name, name_len) @@ -19704,9 +19118,9 @@ set_var ( return; } else if (v->di_tv.v_type == VAR_NUMBER) { v->di_tv.vval.v_number = get_tv_number(tv); - if (STRCMP(varname, "searchforward") == 0) + if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); - else if (STRCMP(varname, "hlsearch") == 0) { + } else if (strcmp(varname, "hlsearch") == 0) { no_hlsearch = !v->di_tv.vval.v_number; redraw_all_later(SOME_VALID); } @@ -19727,13 +19141,14 @@ set_var ( return; } - /* Make sure the variable name is valid. */ - if (!valid_varname(varname)) + // Make sure the variable name is valid. + if (!valid_varname(varname)) { return; + } - v = xmalloc(sizeof(dictitem_T) + STRLEN(varname)); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); - if (hash_add(ht, DI2HIKEY(v)) == FAIL) { + if (tv_dict_add(dict, v) == FAIL) { xfree(v); return; } @@ -19745,14 +19160,14 @@ set_var ( } else { v->di_tv = *tv; v->di_tv.v_lock = 0; - init_tv(tv); + tv_init(tv); } if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, NULL); } else { - dictwatcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); + tv_dict_watcher_notify(dict, (char *)v->di_key, &v->di_tv, &oldtv); tv_clear(&oldtv); } } @@ -19768,8 +19183,8 @@ set_var ( /// /// @return True if variable is read-only: either always or in sandbox when /// sandbox is enabled, false otherwise. -static bool var_check_ro(const int flags, const char *const name, - const size_t name_len) +bool var_check_ro(const int flags, const char *const name, + const size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (flags & DI_FLAGS_RO) { @@ -19804,22 +19219,24 @@ static bool var_check_fixed(const int flags, const char *const name, return false; } -/* - * Check if a funcref is assigned to a valid variable name. - * Return TRUE and give an error if not. - */ -static int -var_check_func_name ( - char_u *name, /* points to start of variable name */ - int new_var /* TRUE when creating the variable */ -) +// TODO(ZyX-I): move to eval/expressions + +/// Check if name is a valid name to assign funcref to +/// +/// @param[in] name Possible function/funcref name. +/// @param[in] new_var True if it is a name for a variable. +/// +/// @return false in case of error, true in case of success. Also gives an +/// error message if appropriate. +bool var_check_func_name(const char *const name, const bool new_var) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] : name[0])) { EMSG2(_("E704: Funcref variable name must start with a capital: %s"), name); - return TRUE; + return false; } // Don't allow hiding a function. When "v" is not NULL we might be // assigning another function to the same var, the type is checked @@ -19827,63 +19244,30 @@ var_check_func_name ( if (new_var && function_exists((const char *)name, false)) { EMSG2(_("E705: Variable name conflicts with existing function: %s"), name); - return true; + return false; } - return false; + return true; } -/* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ -static int valid_varname(char_u *varname) -{ - char_u *p; - - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !ascii_isdigit(*p)) - && *p != AUTOLOAD_CHAR) { - EMSG2(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} +// TODO(ZyX-I): move to eval/expressions -/// Check whether typval is locked -/// -/// Also gives an error message. +/// Check if a variable name is valid /// -/// @param[in] lock Lock status. -/// @param[in] name Value name, for use in error message. -/// @param[in] name_len Value name length. +/// @param[in] varname Variable name to check. /// -/// @return True if value is locked. -static bool tv_check_lock(const VarLockStatus lock, - const char *const name, - const size_t name_len) - FUNC_ATTR_WARN_UNUSED_RESULT +/// @return false when variable name is not valid, true when it is. Also gives +/// an error message if appropriate. +bool valid_varname(const char *varname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - const char *error_message = NULL; - switch (lock) { - case VAR_UNLOCKED: { + for (const char *p = varname; *p != NUL; p++) { + if (!eval_isnamec1((int)(uint8_t)(*p)) + && (p == varname || !ascii_isdigit(*p)) + && *p != AUTOLOAD_CHAR) { + emsgf(_(e_illvar), varname); return false; } - case VAR_LOCKED: { - error_message = N_("E741: Value is locked: %.*s"); - break; - } - case VAR_FIXED: { - error_message = N_("E742: Cannot change value of %.*s"); - break; - } } - assert(error_message != NULL); - - const char *const unknown_name = _("Unknown"); - - emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), - (name != NULL ? name : unknown_name)); - return true; } @@ -20016,7 +19400,7 @@ int var_item_copy(const vimconv_T *const conv, to->vval.v_dict = from->vval.v_dict->dv_copydict; ++to->vval.v_dict->dv_refcount; } else { - to->vval.v_dict = dict_copy(conv, from->vval.v_dict, deep, copyID); + to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } if (to->vval.v_dict == NULL) ret = FAIL; @@ -20730,9 +20114,9 @@ void ex_function(exarg_T *eap) if (fudi.fd_dict != NULL) { if (fudi.fd_di == NULL) { - /* add new dict entry */ - fudi.fd_di = dictitem_alloc(fudi.fd_newkey); - if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { + // Add new dict entry + fudi.fd_di = tv_dict_item_alloc((const char *)fudi.fd_newkey); + if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { xfree(fudi.fd_di); xfree(fp); goto erret; @@ -21002,7 +20386,7 @@ theend: */ static int eval_fname_script(const char *const p) { - // Use mb_stricmp() because in Turkish comparing the "I" may not work with + // Use mb_strnicmp() because in Turkish comparing the "I" may not work with // the standard library function. if (p[0] == '<' && (mb_strnicmp((char_u *)p + 1, (char_u *)"SID>", 4) == 0 @@ -21558,9 +20942,9 @@ void ex_delfunction(exarg_T *eap) } if (fudi.fd_dict != NULL) { - /* Delete the dict item that refers to the function, it will - * invoke func_unref() and possibly delete the function. */ - dictitem_remove(fudi.fd_dict, fudi.fd_di); + // Delete the dict item that refers to the function, it will + // invoke func_unref() and possibly delete the function. + tv_dict_item_remove(fudi.fd_dict, fudi.fd_di); } else { // A normal function (not a numbered function or lambda) has a // refcount of 1 for the entry in the hashtable. When deleting @@ -21675,6 +21059,11 @@ void func_unref(char_u *name) /// Unreference a Function: decrement the reference count and free it when it /// becomes zero. +/// Unreference user function, freeing it if needed +/// +/// Decrements the reference count and frees when it becomes zero. +/// +/// @param fp Function to unreference. void func_ptr_unref(ufunc_T *fp) { if (fp != NULL && --fp->uf_refcount <= 0) { @@ -21712,17 +21101,19 @@ void func_ptr_ref(ufunc_T *fp) } } -/// Call a user function. -static void -call_user_func( - ufunc_T *fp, // pointer to function - int argcount, // nr of args - typval_T *argvars, // arguments - typval_T *rettv, // return value - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - dict_T *selfdict // Dictionary for "self" -) +/// Call a user function +/// +/// @param fp Function to call. +/// @param[in] argcount Number of arguments. +/// @param argvars Arguments. +/// @param[out] rettv Return value. +/// @param[in] firstline First line of range. +/// @param[in] lastline Last line of range. +/// @param selfdict Dictionary for "self" for dictionary functions. +void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, + typval_T *rettv, linenr_T firstline, linenr_T lastline, + dict_T *selfdict) + FUNC_ATTR_NONNULL_ARG(1, 3, 4) { char_u *save_sourcing_name; linenr_T save_sourcing_lnum; @@ -21796,7 +21187,7 @@ call_user_func( STRCPY(name, "self"); #endif v->di_flags = DI_FLAGS_RO + DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = 0; v->di_tv.vval.v_dict = selfdict; @@ -21819,7 +21210,7 @@ call_user_func( STRCPY(name, "000"); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; @@ -21867,9 +21258,9 @@ call_user_func( // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. copy_tv(&v->di_tv, &v->di_tv); - hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_vars, v); } else { - hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v)); + tv_dict_add(&fc->l_avars, v); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { @@ -22065,29 +21456,22 @@ call_user_func( && fc->fc_refcount <= 0) { free_funccal(fc, false); } else { - hashitem_T *hi; - listitem_T *li; - int todo; - // "fc" is still in use. This can happen when returning "a:000", // assigning "l:" to a global variable or defining a closure. // Link "fc" in the list for garbage collection later. fc->caller = previous_funccal; previous_funccal = fc; - /* Make a copy of the a: variables, since we didn't do that above. */ - todo = (int)fc->l_avars.dv_hashtab.ht_used; - for (hi = fc->l_avars.dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - v = HI2DI(hi); - copy_tv(&v->di_tv, &v->di_tv); - } - } + // Make a copy of the a: variables, since we didn't do that above. + TV_DICT_ITER(&fc->l_avars, di, { + copy_tv(&di->di_tv, &di->di_tv); + }); - /* Make a copy of the a:000 items, since we didn't do that above. */ - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) + // Make a copy of the a:000 items, since we didn't do that above. + for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; + li = li->li_next) { copy_tv(&li->li_tv, &li->li_tv); + } } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -22194,7 +21578,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) STRCPY(v->di_key, name); #endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, DI2HIKEY(v)); + tv_dict_add(dp, v); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -22590,7 +21974,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = globvarht.ht_array; while ((size_t) (hi - hifirst) < hinum && (HASHITEM_EMPTY(hi) - || var_flavour(HI2DI(hi)->di_key) != VAR_FLAVOUR_SHADA)) { + || var_flavour(hi->hi_key) != VAR_FLAVOUR_SHADA)) { hi++; } if ((size_t) (hi - hifirst) == hinum) { @@ -22599,11 +21983,10 @@ const void *var_shada_iter(const void *const iter, const char **const name, } else { hi = (const hashitem_T *) iter; } - *name = (char *) HI2DI(hi)->di_key; - copy_tv(&(HI2DI(hi)->di_tv), rettv); - while ((size_t) (++hi - hifirst) < hinum) { - if (!HASHITEM_EMPTY(hi) - && var_flavour(HI2DI(hi)->di_key) == VAR_FLAVOUR_SHADA) { + *name = (char *)TV_DICT_HI2DI(hi)->di_key; + copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + while ((size_t)(++hi - hifirst) < hinum) { + if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; } } @@ -22614,62 +21997,54 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var((char_u *) name, &vartv, false); + set_var(name, &vartv, false); current_funccal = saved_current_funccal; } int store_session_globals(FILE *fd) { - hashitem_T *hi; - dictitem_T *this_var; - int todo; - char_u *p, *t; - - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - --todo; - this_var = HI2DI(hi); - if ((this_var->di_tv.v_type == VAR_NUMBER - || this_var->di_tv.v_type == VAR_STRING) - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - /* Escape special characters with a backslash. Turn a LF and - * CR into \n and \r. */ - p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); - for (t = p; *t != NUL; ++t) - if (*t == '\n') - *t = 'n'; - else if (*t == '\r') - *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", - this_var->di_key, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ', - p, - (this_var->di_tv.v_type == VAR_STRING) ? '"' - : ' ') < 0) - || put_eol(fd) == FAIL) { - xfree(p); - return FAIL; + TV_DICT_ITER(&globvardict, this_var, { + if ((this_var->di_tv.v_type == VAR_NUMBER + || this_var->di_tv.v_type == VAR_STRING) + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + // Escape special characters with a backslash. Turn a LF and + // CR into \n and \r. + char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), + (char_u *)"\\\"\n\r"); + for (char_u *t = p; *t != NUL; t++) { + if (*t == '\n') { + *t = 'n'; + } else if (*t == '\r') { + *t = 'r'; } + } + if ((fprintf(fd, "let %s = %c%s%c", + this_var->di_key, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' '), + p, + ((this_var->di_tv.v_type == VAR_STRING) ? '"' + : ' ')) < 0) + || put_eol(fd) == FAIL) { xfree(p); - } else if (this_var->di_tv.v_type == VAR_FLOAT - && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { - float_T f = this_var->di_tv.vval.v_float; - int sign = ' '; - - if (f < 0) { - f = -f; - sign = '-'; - } - if ((fprintf(fd, "let %s = %c%f", - this_var->di_key, sign, f) < 0) - || put_eol(fd) == FAIL) - return FAIL; + return FAIL; + } + xfree(p); + } else if (this_var->di_tv.v_type == VAR_FLOAT + && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { + float_T f = this_var->di_tv.vval.v_float; + int sign = ' '; + + if (f < 0) { + f = -f; + sign = '-'; + } + if ((fprintf(fd, "let %s = %c%f", this_var->di_key, sign, f) < 0) + || put_eol(fd) == FAIL) { + return FAIL; } } - } + }); return OK; } @@ -23094,7 +22469,7 @@ static inline TerminalJobData *common_job_init(char **argv, bool pty, bool rpc, bool detach, - char *cwd) + const char *cwd) { TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); data->stopped = false; @@ -23384,8 +22759,7 @@ static void on_job_event(JobEvent *ev) argv[2].v_lock = 0; argv[2].vval.v_string = (uint8_t *)ev->type; - typval_T rettv; - init_tv(&rettv); + typval_T rettv = TV_INITIAL_VALUE; callback_call(ev->callback, 3, argv, &rettv); tv_clear(&rettv); } @@ -23499,91 +22873,3 @@ bool eval_has_provider(char *name) return false; } - -// Compute the `DictWatcher` address from a QUEUE node. This only exists for -// .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer arithmetic). -static DictWatcher *dictwatcher_node_data(QUEUE *q) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET -{ - return QUEUE_DATA(q, DictWatcher, node); -} - -// Send a change notification to all `dict` watchers that match `key`. -static void dictwatcher_notify(dict_T *dict, const char *key, typval_T *newtv, - typval_T *oldtv) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) -{ - typval_T argv[4]; - for (size_t i = 0; i < ARRAY_SIZE(argv); i++) { - init_tv(argv + i); - } - - argv[0].v_type = VAR_DICT; - argv[0].vval.v_dict = dict; - argv[1].v_type = VAR_STRING; - argv[1].vval.v_string = (char_u *)xstrdup(key); - argv[2].v_type = VAR_DICT; - argv[2].vval.v_dict = dict_alloc(); - argv[2].vval.v_dict->dv_refcount++; - - if (newtv) { - dictitem_T *v = dictitem_alloc((char_u *)"new"); - copy_tv(newtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - if (oldtv) { - dictitem_T *v = dictitem_alloc((char_u *)"old"); - copy_tv(oldtv, &v->di_tv); - dict_add(argv[2].vval.v_dict, v); - } - - typval_T rettv; - - QUEUE *w; - QUEUE_FOREACH(w, &dict->watchers) { - DictWatcher *watcher = dictwatcher_node_data(w); - if (!watcher->busy && dictwatcher_matches(watcher, key)) { - init_tv(&rettv); - watcher->busy = true; - callback_call(&watcher->callback, 3, argv, &rettv); - watcher->busy = false; - tv_clear(&rettv); - } - } - - for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { - tv_clear(argv + i); - } -} - -// Test if `key` matches with with `watcher->key_pattern` -static bool dictwatcher_matches(DictWatcher *watcher, const char *key) - FUNC_ATTR_NONNULL_ALL -{ - // For now only allow very simple globbing in key patterns: a '*' at the end - // of the string means it should match everything up to the '*' instead of the - // whole string. - char *nul = strchr(watcher->key_pattern, NUL); - size_t len = nul - watcher->key_pattern; - if (*(nul - 1) == '*') { - return !strncmp(key, watcher->key_pattern, len - 1); - } else { - return !strcmp(key, watcher->key_pattern); - } -} - -// Perform all necessary cleanup for a `DictWatcher` instance. -static void dictwatcher_free(DictWatcher *watcher) - FUNC_ATTR_NONNULL_ALL -{ - callback_free(&watcher->callback); - xfree(watcher->key_pattern); - xfree(watcher); -} - -// Check if `d` has at least one watcher. -static bool is_watched(dict_T *d) -{ - return d && !QUEUE_EMPTY(&d->watchers); -} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 57fee5c5a2..7f6fd76c46 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -1,21 +1,23 @@ #ifndef NVIM_EVAL_H #define NVIM_EVAL_H -#include "nvim/profile.h" #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/garray.h" // For garray_T #include "nvim/buffer_defs.h" // For scid_T #include "nvim/ex_cmds_defs.h" // For exarg_T +#include "nvim/eval/typval.h" +#include "nvim/profile.h" +#include "nvim/garray.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) // All user-defined functions are found in this hashtable. extern hashtab_T func_hashtab; + // From user function to hashitem and back. EXTERN ufunc_T dumuf; #define UF2HIKEY(fp) ((fp)->uf_name) -#define HIKEY2UF(p) ((ufunc_T *)(p - (dumuf.uf_name - (char_u *)&dumuf))) +#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name))) #define HI2UF(hi) HIKEY2UF((hi)->hi_key) /// Defines for Vim variables diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 1d30f51f55..3cb68e093b 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -6,6 +6,7 @@ #include "nvim/eval.h" #include "nvim/eval/encode.h" #include "nvim/ascii.h" +#include "nvim/macros.h" #include "nvim/message.h" #include "nvim/charset.h" // vim_str2nr #include "nvim/lib/kvec.h" @@ -51,16 +52,16 @@ static inline void create_special_dict(typval_T *const rettv, typval_T val) FUNC_ATTR_NONNULL_ALL { - dict_T *const dict = dict_alloc(); - dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); + dict_T *const dict = tv_dict_alloc(); + dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; type_di->di_tv.vval.v_list->lv_refcount++; - dict_add(dict, type_di); - dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); + tv_dict_add(dict, type_di); + dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; - dict_add(dict, val_di); + tv_dict_add(dict, val_di); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -138,9 +139,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, assert(!(key.is_special_string || key.val.vval.v_string == NULL || *key.val.vval.v_string == NUL)); - dictitem_T *obj_di = dictitem_alloc(key.val.vval.v_string); + dictitem_T *const obj_di = tv_dict_item_alloc( + (const char *)key.val.vval.v_string); tv_clear(&key.val); - if (dict_add(last_container.container.vval.v_dict, obj_di) + if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { assert(false); } @@ -173,8 +175,8 @@ static inline int json_decoder_pop(ValuesStackItem obj, && (obj.is_special_string || obj.val.vval.v_string == NULL || *obj.val.vval.v_string == NUL - || dict_find(last_container.container.vval.v_dict, - obj.val.vval.v_string, -1))) { + || tv_dict_find(last_container.container.vval.v_dict, + (const char *)obj.val.vval.v_string, -1))) { tv_clear(&obj.val); // Restart @@ -835,7 +837,7 @@ json_decode_string_cycle_start: .vval = { .v_list = val_list }, })); } else { - dict_T *dict = dict_alloc(); + dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; tv = (typval_T) { .v_type = VAR_DICT, @@ -1042,7 +1044,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) goto msgpack_to_vim_generic_map; } } - dict_T *const dict = dict_alloc(); + dict_T *const dict = tv_dict_alloc(); dict->dv_refcount++; *rettv = (typval_T) { .v_type = VAR_DICT, @@ -1055,7 +1057,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, mobj.via.map.ptr[i].key.via.str.size); di->di_tv.v_type = VAR_UNKNOWN; - if (dict_add(dict, di) == FAIL) { + if (tv_dict_add(dict, di) == FAIL) { // Duplicate key: fallback to generic map tv_clear(rettv); xfree(di); diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 1416806ca6..26f9aaa27d 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -744,11 +744,11 @@ bool encode_check_json_key(const typval_T *const tv) } const dictitem_T *type_di; const dictitem_T *val_di; - if ((type_di = dict_find((dict_T *) spdict, (char_u *) "_TYPE", -1)) == NULL + if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL || type_di->di_tv.v_type != VAR_LIST || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString] && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary]) - || (val_di = dict_find((dict_T *) spdict, (char_u *) "_VAL", -1)) == NULL + || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL || val_di->di_tv.v_type != VAR_LIST) { return false; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 7726e106a1..bb7baed7d2 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2,6 +2,7 @@ #include #include +#include "nvim/lib/queue.h" #include "nvim/eval/typval.h" #include "nvim/eval/gc.h" #include "nvim/eval/executor.h" @@ -12,6 +13,9 @@ #include "nvim/assert.h" #include "nvim/memory.h" #include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/vim.h" +#include "nvim/ascii.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -115,8 +119,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) } } -//{{{2 Lists -//{{{3 Alloc/free +//{{{2 Alloc/free /// Allocate an empty list /// @@ -205,7 +208,7 @@ void tv_list_unref(list_T *const l) } } -//{{{3 Add/remove +//{{{2 Add/remove /// Remove items "item" to "item2" from list "l". /// @@ -406,7 +409,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) tv_list_append(l, li); } -//{{{3 Operations on the whole list +//{{{2 Operations on the whole list /// Make a copy of list /// @@ -596,6 +599,8 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) /// @param[in] l2 Second list to compare. /// @param[in] ic True if case is to be ignored. /// @param[in] recursive True when used recursively. +/// +/// @return True if lists are equal, false otherwise. bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT @@ -623,7 +628,7 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } -//{{{3 Indexing/searching +//{{{2 Indexing/searching /// Locate item with a given index in a list and return it /// @@ -761,6 +766,552 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) } return idx; } + +//{{{1 Dictionaries +//{{{2 Dictionary watchers + +/// Perform all necessary cleanup for a `DictWatcher` instance +/// +/// @param watcher Watcher to free. +void tv_dict_watcher_free(DictWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + callback_free(&watcher->callback); + xfree(watcher->key_pattern); + xfree(watcher); +} + +/// Test if `key` matches with with `watcher->key_pattern` +/// +/// @param[in] watcher Watcher to check key pattern from. +/// @param[in] key Key to check. +/// +/// @return true if key matches, false otherwise. +static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE +{ + // For now only allow very simple globbing in key patterns: a '*' at the end + // of the string means it should match everything up to the '*' instead of the + // whole string. + const size_t len = strlen(watcher->key_pattern); + if (watcher->key_pattern[len - 1] == '*') { + return strncmp(key, watcher->key_pattern, len - 1) == 0; + } else { + return strcmp(key, watcher->key_pattern) == 0; + } +} + +/// Send a change notification to all dictionary watchers that match given key +/// +/// @param[in] dict Dictionary which was modified. +/// @param[in] key Key which was modified. +/// @param[in] newtv New key value. +/// @param[in] oldtv Old key value. +void tv_dict_watcher_notify(dict_T *const dict, const char *const key, + typval_T *const newtv, typval_T *const oldtv) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + typval_T argv[3]; + + argv[0].v_type = VAR_DICT; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_dict = dict; + argv[1].v_type = VAR_STRING; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_string = (char_u *)xstrdup(key); + argv[2].v_type = VAR_DICT; + argv[2].v_lock = VAR_UNLOCKED; + argv[2].vval.v_dict = tv_dict_alloc(); + argv[2].vval.v_dict->dv_refcount++; + + if (newtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); + copy_tv(newtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + if (oldtv) { + dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); + copy_tv(oldtv, &v->di_tv); + tv_dict_add(argv[2].vval.v_dict, v); + } + + typval_T rettv; + + QUEUE *w; + QUEUE_FOREACH(w, &dict->watchers) { + DictWatcher *watcher = tv_dict_watcher_node_data(w); + if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) { + rettv = TV_INITIAL_VALUE; + watcher->busy = true; + callback_call(&watcher->callback, 3, argv, &rettv); + watcher->busy = false; + tv_clear(&rettv); + } + } + + for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { + tv_clear(argv + i); + } +} + +//{{{2 Dictionary item + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// @param[in] key_len Key length. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); + memcpy(di->di_key, key, key_len); + di->di_key[key_len] = NUL; + di->di_flags = DI_FLAGS_ALLOC; + return di; +} + +/// Allocate a dictionary item +/// +/// @note that the value of the item (->di_tv) still needs to be initialized. +/// +/// @param[in] key Key, is copied to the new item. +/// +/// @return [allocated] new dictionary item. +dictitem_T *tv_dict_item_alloc(const char *const key) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + return tv_dict_item_alloc_len(key, strlen(key)); +} + +/// Free a dictionary item, also clearing the value +/// +/// @param item Item to free. +void tv_dict_item_free(dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + tv_clear(&item->di_tv); + if (item->di_flags & DI_FLAGS_ALLOC) { + xfree(item); + } +} + +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +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); +} + +/// Make a copy of a dictionary item +/// +/// @param[in] di Item to copy. +/// +/// @return [allocated] new dictionary item. +static dictitem_T *tv_dict_item_copy(dictitem_T *const di) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_MALLOC +{ + dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); + copy_tv(&di->di_tv, &new_di->di_tv); + return new_di; +} + +/// Remove item from dictionary and free it +/// +/// @param dict Dictionary to remove item from. +/// @param item Item to remove. +void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item) + FUNC_ATTR_NONNULL_ALL +{ + hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key); + if (HASHITEM_EMPTY(hi)) { + emsgf(_(e_intern2), "tv_dict_item_remove()"); + } else { + hash_remove(&dict->dv_hashtab, hi); + } + tv_dict_item_free(item); +} + +//{{{2 Alloc/free + +/// Allocate an empty dictionary +/// +/// @return [allocated] new dictionary. +dict_T *tv_dict_alloc(void) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + dict_T *const d = xmalloc(sizeof(dict_T)); + + // Add the dict to the list of dicts for garbage collection. + if (gc_first_dict != NULL) { + gc_first_dict->dv_used_prev = d; + } + d->dv_used_next = gc_first_dict; + d->dv_used_prev = NULL; + gc_first_dict = d; + + hash_init(&d->dv_hashtab); + d->dv_lock = VAR_UNLOCKED; + d->dv_scope = VAR_NO_SCOPE; + d->dv_refcount = 0; + d->dv_copyID = 0; + QUEUE_INIT(&d->watchers); + + return d; +} + +/// Free items contained in a dictionary +/// +/// @param[in,out] d Dictionary to clear. +void tv_dict_free_contents(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Lock the hashtab, we don't want it to resize while freeing items. + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + HASHTAB_ITER(&d->dv_hashtab, hi, { + // Remove the item before deleting it, just in case there is + // something recursive causing trouble. + dictitem_T *const di = TV_DICT_HI2DI(hi); + hash_remove(&d->dv_hashtab, hi); + tv_dict_item_free(di); + }); + + while (!QUEUE_EMPTY(&d->watchers)) { + QUEUE *w = QUEUE_HEAD(&d->watchers); + QUEUE_REMOVE(w); + DictWatcher *watcher = tv_dict_watcher_node_data(w); + tv_dict_watcher_free(watcher); + } + + hash_clear(&d->dv_hashtab); + d->dv_hashtab.ht_locked--; + hash_init(&d->dv_hashtab); +} + +/// Free a dictionary itself, ignoring items it contains +/// +/// Ignores the reference count. +/// +/// @param[in,out] d Dictionary to free. +void tv_dict_free_dict(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + // Remove the dict from the list of dicts for garbage collection. + if (d->dv_used_prev == NULL) { + gc_first_dict = d->dv_used_next; + } else { + d->dv_used_prev->dv_used_next = d->dv_used_next; + } + if (d->dv_used_next != NULL) { + d->dv_used_next->dv_used_prev = d->dv_used_prev; + } + + xfree(d); +} + +/// Free a dictionary, including all items it contains +/// +/// Ignores the reference count. +/// +/// @param d Dictionary to free. +void tv_dict_free(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + if (!tv_in_free_unref_items) { + tv_dict_free_contents(d); + tv_dict_free_dict(d); + } +} + + +/// Unreference a dictionary +/// +/// Decrements the reference count and frees dictionary when it becomes zero. +/// +/// @param[in] d Dictionary to operate on. +void tv_dict_unref(dict_T *const d) +{ + if (d != NULL && --d->dv_refcount <= 0) { + tv_dict_free(d); + } +} + +//{{{2 Indexing/searching + +/// Find item in dictionary +/// +/// @param[in] d Dictionary to check. +/// @param[in] key Dictionary key. +/// @param[in] len Key length. If negative, then strlen(key) is used. +/// +/// @return found item or NULL if nothing was found. +dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, + const ptrdiff_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + hashitem_T *const hi = (len < 0 + ? hash_find(&d->dv_hashtab, (const char_u *)key) + : hash_find_len(&d->dv_hashtab, key, (size_t)len)); + if (HASHITEM_EMPTY(hi)) { + return NULL; + } + return TV_DICT_HI2DI(hi); +} + +/// Get a number item from a dictionary +/// +/// Returns 0 if the entry does not exist. +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Key to find in dictionary. +/// +/// @return Dictionary item. +varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return 0; + } + return get_tv_number(&di->di_tv); +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] save If true, returned string will be placed in the allocated +/// memory. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. If returned value is not NULL, it may +/// be allocated depending on `save` argument. +char *tv_dict_get_string(dict_T *const d, const char *const key, + const bool save) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char numbuf[NUMBUFLEN]; + const char *const s = tv_dict_get_string_buf(d, key, numbuf); + if (save && s != NULL) { + return xstrdup(s); + } + return (char *)s; +} + +/// Get a string item from a dictionary +/// +/// @param[in] d Dictionary to get item from. +/// @param[in] key Dictionary key. +/// @param[in] numbuf Numbuf for. +/// +/// @return NULL if key does not exist, empty string in case of type error, +/// string item value otherwise. +const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, + char *const numbuf) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + dictitem_T *const di = tv_dict_find(d, key, -1); + if (di == NULL) { + return NULL; + } + return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); +} + +//{{{2 Operations on the whole dict + +/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. +/// +/// @param d The Dictionary to clear +void tv_dict_clear(dict_T *const d) + FUNC_ATTR_NONNULL_ALL +{ + hash_lock(&d->dv_hashtab); + assert(d->dv_hashtab.ht_locked > 0); + + HASHTAB_ITER(&d->dv_hashtab, hi, { + tv_dict_item_free(TV_DICT_HI2DI(hi)); + hash_remove(&d->dv_hashtab, hi); + }); + + hash_unlock(&d->dv_hashtab); +} + +/// Extend dictionary with items from another dictionary +/// +/// @param d1 Dictionary to extend. +/// @param[in] d2 Dictionary to extend with. +/// @param[in] action "error", "force", "keep": +/// +/// e*, including "error": duplicate key gives an error. +/// f*, including "force": duplicate d2 keys override d1. +/// 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 +{ + const bool watched = tv_dict_is_watched(d1); + const char *const arg_errmsg = _("extend() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + + TV_DICT_ITER(d2, di2, { + 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 + && di2->di_tv.v_type == VAR_FUNC + && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { + break; + } + if (!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); + } + } else if (*action == 'e') { + emsgf(_("E737: Key already exists: %s"), di2->di_key); + break; + } else if (*action == 'f' && di2 != di1) { + typval_T oldtv; + + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { + break; + } + + if (watched) { + copy_tv(&di1->di_tv, &oldtv); + } + + tv_clear(&di1->di_tv); + copy_tv(&di2->di_tv, &di1->di_tv); + + if (watched) { + tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, + &oldtv); + tv_clear(&oldtv); + } + } + }); +} + +/// Compare two dictionaries +/// +/// @param[in] d1 First dictionary. +/// @param[in] d2 Second dictionary. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +bool tv_dict_equal(dict_T *const d1, dict_T *const d2, + const bool ic, const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (d1 == d2) { + return true; + } + if (d1 == NULL || d2 == NULL) { + return false; + } + if (tv_dict_len(d1) != tv_dict_len(d2)) { + return false; + } + + TV_DICT_ITER(d1, di1, { + dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1); + if (di2 == NULL) { + return false; + } + if (!tv_equal(&di1->di_tv, &di2->di_tv, ic, recursive)) { + return false; + } + }); + return true; +} + +/// Make a copy of dictionary +/// +/// @param[in] conv If non-NULL, then all internal strings will be converted. +/// @param[in] orig Original dictionary to copy. +/// @param[in] deep If false, then shallow copy will be done. +/// @param[in] copyID See var_item_copy(). +/// +/// @return Copied dictionary. May be NULL in case original dictionary is NULL +/// or some failure happens. The refcount of the new dictionary is set +/// to 1. +dict_T *tv_dict_copy(const vimconv_T *const conv, + dict_T *const orig, + const bool deep, + const int copyID) +{ + if (orig == NULL) { + return NULL; + } + + dict_T *copy = tv_dict_alloc(); + if (copyID != 0) { + orig->dv_copyID = copyID; + orig->dv_copydict = copy; + } + TV_DICT_ITER(orig, di, { + if (got_int) { + break; + } + dictitem_T *new_di; + 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); + if (key == NULL) { + new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); + } else { + new_di = tv_dict_item_alloc_len(key, len); + xfree(key); + } + } + if (deep) { + if (var_item_copy(conv, &di->di_tv, &new_di->di_tv, deep, + copyID) == FAIL) { + xfree(new_di); + break; + } + } else { + copy_tv(&di->di_tv, &new_di->di_tv); + } + if (tv_dict_add(copy, new_di) == FAIL) { + tv_dict_item_free(new_di); + break; + } + }); + + copy->dv_refcount++; + if (got_int) { + tv_dict_unref(copy); + copy = NULL; + } + + return copy; +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc @@ -783,6 +1334,21 @@ list_T *tv_list_alloc_ret(typval_T *const ret_tv) return l; } +/// Allocate an empty dictionary for a return value +/// +/// Also sets reference count. +/// +/// @param[out] ret_tv Structure where dictionary is saved. +void tv_dict_alloc_ret(typval_T *const ret_tv) + FUNC_ATTR_NONNULL_ALL +{ + dict_T *const d = tv_dict_alloc(); + ret_tv->vval.v_dict = d; + ret_tv->v_type = VAR_DICT; + ret_tv->v_lock = VAR_UNLOCKED; + d->dv_refcount++; +} + //{{{3 Clear #define TYPVAL_ENCODE_ALLOW_SPECIALS false @@ -884,7 +1450,7 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ do { \ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - dict_unref((dict_T *)dict); \ + tv_dict_unref((dict_T *)dict); \ *((dict_T **)&dict) = NULL; \ if (tv != NULL) { \ ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ @@ -966,7 +1532,7 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, FUNC_ATTR_ALWAYS_INLINE { if ((const void *)dictp != nodictvar) { - dict_unref(*dictp); + tv_dict_unref(*dictp); *dictp = NULL; } } @@ -1077,13 +1643,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, d->dv_lock); if (deep < 0 || deep > 1) { // recursive: lock/unlock the items the List contains - int todo = (int)d->dv_hashtab.ht_used; - for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - tv_item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); - } - } + TV_DICT_ITER(d, di, { + tv_item_lock(&di->di_tv, deep - 1, lock); + }); } } break; @@ -1121,6 +1683,140 @@ bool tv_islocked(const typval_T *const tv) && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); } +/// Return true if typval is locked +/// +/// Also gives an error message when typval is locked. +/// +/// @param[in] lock Lock status. +/// @param[in] name Variable name, used in the error message. +/// @param[in] use_gettext True if variable name also is to be translated. +/// +/// @return true if variable is locked, false otherwise. +bool tv_check_lock(const VarLockStatus lock, const char *const name, + const size_t name_len) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *error_message = NULL; + switch (lock) { + case VAR_UNLOCKED: { + return false; + } + case VAR_LOCKED: { + error_message = N_("E741: Value is locked: %.*s"); + break; + } + case VAR_FIXED: { + error_message = N_("E742: Cannot change value of %.*s"); + break; + } + } + assert(error_message != NULL); + + const char *const unknown_name = _("Unknown"); + + emsgf(_(error_message), (name != NULL ? name_len : strlen(unknown_name)), + (name != NULL ? name : unknown_name)); + + return true; +} + +//{{{2 Comparison + +static int tv_equal_recurse_limit; + +/// Compare two VimL values +/// +/// Like "==", but strings and numbers are different, as well as floats and +/// numbers. +/// +/// @warning Too nested structures may be considered equal even if they are not. +/// +/// @param[in] tv1 First value to compare. +/// @param[in] tv2 Second value to compare. +/// @param[in] ic True if case is to be ignored. +/// @param[in] recursive True when used recursively. +/// +/// @return true if values are equal. +bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, + const bool recursive) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + // TODO(ZyX-I): Make this not recursive + static int recursive_cnt = 0; // Catch recursive loops. + + if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) + && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) + && tv1->v_type != tv2->v_type) { + return false; + } + + // Catch lists and dicts that have an endless loop by limiting + // recursiveness to a limit. We guess they are equal then. + // A fixed limit has the problem of still taking an awful long time. + // Reduce the limit every time running into it. That should work fine for + // deeply linked structures that are not recursively linked and catch + // recursiveness quickly. + if (!recursive) { + tv_equal_recurse_limit = 1000; + } + if (recursive_cnt >= tv_equal_recurse_limit) { + tv_equal_recurse_limit--; + return true; + } + + switch (tv1->v_type) { + case VAR_LIST: { + recursive_cnt++; + const bool r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, + true); + recursive_cnt--; + return r; + } + case VAR_DICT: { + recursive_cnt++; + const bool r = tv_dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, + true); + recursive_cnt--; + return r; + } + case VAR_PARTIAL: + case VAR_FUNC: { + if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL) + || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL)) { + return false; + } + recursive_cnt++; + const bool r = func_equal(tv1, tv2, ic); + recursive_cnt--; + return r; + } + case VAR_NUMBER: { + return tv1->vval.v_number == tv2->vval.v_number; + } + case VAR_FLOAT: { + return tv1->vval.v_float == tv2->vval.v_float; + } + case VAR_STRING: { + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); + const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + return mb_strcmp_ic((bool)ic, s1, s2) == 0; + } + case VAR_SPECIAL: { + return tv1->vval.v_special == tv2->vval.v_special; + } + case VAR_UNKNOWN: { + // VAR_UNKNOWN can be the result of an invalid expression, let’s say it + // does not equal anything, not even self. + return false; + } + } + + assert(false); + return false; +} + //{{{2 Type checks /// Check that given value is a number or string @@ -1135,37 +1831,37 @@ bool tv_islocked(const typval_T *const tv) bool tv_check_str_or_nr(const typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - switch (tv->v_type) { - case VAR_NUMBER: - case VAR_STRING: { - return true; - } - case VAR_FLOAT: { - EMSG(_("E805: Expected a Number or a String, Float found")); - return false; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Expected a Number or a String, Funcref found")); - return false; - } - case VAR_LIST: { - EMSG(_("E745: Expected a Number or a String, List found")); - return false; - } - case VAR_DICT: { - EMSG(_("E728: Expected a Number or a String, Dictionary found")); - return false; - } - case VAR_SPECIAL: { - EMSG(_("E5300: Expected a Number or a String")); - return false; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); - return false; - } + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_STRING: { + return true; } - assert(false); - return false; + case VAR_FLOAT: { + emsgf(_("E805: Expected a Number or a String, Float found")); + return false; + } + case VAR_PARTIAL: + case VAR_FUNC: { + emsgf(_("E703: Expected a Number or a String, Funcref found")); + return false; + } + case VAR_LIST: { + emsgf(_("E745: Expected a Number or a String, List found")); + return false; + } + case VAR_DICT: { + emsgf(_("E728: Expected a Number or a String, Dictionary found")); + return false; + } + case VAR_SPECIAL: { + emsgf(_("E5300: Expected a Number or a String")); + return false; + } + case VAR_UNKNOWN: { + emsgf(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)"); + return false; + } + } + assert(false); + return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index cf83904ffc..6183397d12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -8,6 +8,7 @@ #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" +#include "nvim/func_attr.h" #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T @@ -31,6 +32,31 @@ typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; typedef struct partial_S partial_T; +typedef struct ufunc ufunc_T; + +typedef enum { + kCallbackNone, + kCallbackFuncref, + kCallbackPartial, +} CallbackType; + +typedef struct { + union { + char_u *funcref; + partial_T *partial; + } data; + CallbackType type; +} Callback; +#define CALLBACK_NONE ((Callback){ .type = kCallbackNone }) + +/// Structure holding dictionary watcher +typedef struct dict_watcher { + Callback callback; + char *key_pattern; + QUEUE node; + bool busy; // prevent recursion if the dict is changed in the callback +} DictWatcher; + /// Special variable values typedef enum { kSpecialVarFalse, ///< v:false @@ -134,7 +160,7 @@ struct dictitem_S { struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ - char_u di_key[KEY_LEN]; /* NUL. */ \ + char_u di_key[KEY_LEN]; /* Key value. */ \ } /// Structure to hold a scope dictionary @@ -181,9 +207,7 @@ typedef int scid_T; // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; -// Structure to hold info for a user function. -typedef struct ufunc ufunc_T; - +/// Structure to hold info for a user function. struct ufunc { int uf_varargs; ///< variable nr of arguments int uf_flags; @@ -207,12 +231,12 @@ struct ufunc { int uf_tml_idx; ///< index of line being timed; -1 if none int uf_tml_execed; ///< line being timed was executed scid_T uf_script_ID; ///< ID of script where function was defined, - // used for s: variables + ///< used for s: variables int uf_refcount; ///< reference count, see func_name_refcount() funccall_T *uf_scoped; ///< l: local variables for closure char_u uf_name[1]; ///< name of function (actually longer); can - // start with 123_ ( is K_SPECIAL - // KS_EXTRA KE_SNR) + ///< start with 123_ ( is K_SPECIAL + ///< KS_EXTRA KE_SNR) }; /// Maximum number of function arguments @@ -245,24 +269,17 @@ typedef struct list_stack_S { // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. -/// Convert a dictitem pointer to a hashitem key pointer -#define DI2HIKEY(di) ((di)->di_key) - -/// Convert a hashitem key pointer to a dictitem pointer -#define HIKEY2DI(p) ((dictitem_T *)(p - offsetof(dictitem_T, di_key))) - -/// Convert a hashitem value pointer to a dictitem pointer -#define HIVAL2DI(p) \ - ((dictitem_T *)(((char *)p) - offsetof(dictitem_T, di_tv))) - /// Convert a hashitem pointer to a dictitem pointer -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) +#define TV_DICT_HI2DI(hi) \ + ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) + +static inline long tv_list_len(list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. static inline long tv_list_len(list_T *const l) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { if (l == NULL) { return 0; @@ -270,6 +287,64 @@ static inline long tv_list_len(list_T *const l) return l->lv_len; } +static inline long tv_dict_len(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get the number of items in a Dictionary +/// +/// @param[in] d Dictionary to check. +static inline long tv_dict_len(const dict_T *const d) +{ + if (d == NULL) { + return 0L; + } + return (long)d->dv_hashtab.ht_used; +} + +static inline bool tv_dict_is_watched(const dict_T *const d) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check if dictionary is watched +/// +/// @param[in] d Dictionary to check. +/// +/// @return true if there is at least one watcher. +static inline bool tv_dict_is_watched(const dict_T *const d) +{ + return d && !QUEUE_EMPTY(&d->watchers); +} + +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + +/// Initialize VimL object +/// +/// Initializes to unlocked VAR_UNKNOWN object. +/// +/// @param[out] tv Object to initialize. +static inline void tv_init(typval_T *const tv) +{ + if (tv != NULL) { + memset(tv, 0, sizeof(*tv)); + } +} + +#define TV_INITIAL_VALUE \ + ((typval_T) { \ + .v_type = VAR_UNKNOWN, \ + .v_lock = VAR_UNLOCKED, \ + }) + /// Empty string /// /// Needed for hack which allows not allocating empty string and still not @@ -279,6 +354,21 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a dictionary +/// +/// @param[in] d Dictionary to iterate over. +/// @param di Name of the variable with current dictitem_T entry. +/// @param code Cycle body. +#define TV_DICT_ITER(d, di, code) \ + HASHTAB_ITER(&(d)->dv_hashtab, di##hi_, { \ + { \ + dictitem_T *const di = TV_DICT_HI2DI(di##hi_); \ + { \ + code \ + } \ + } \ + }) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index eb89a601ff..ad54eef4a0 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -406,11 +406,11 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const dictitem_T *val_di; if (TYPVAL_ENCODE_ALLOW_SPECIALS && tv->vval.v_dict->dv_hashtab.ht_used == 2 - && (type_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_TYPE", -1)) != NULL + && (type_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_TYPE"))) != NULL && type_di->di_tv.v_type == VAR_LIST - && (val_di = dict_find((dict_T *)tv->vval.v_dict, - (char_u *)"_VAL", -1)) != NULL) { + && (val_di = tv_dict_find((dict_T *)tv->vval.v_dict, + S_LEN("_VAL"))) != NULL) { size_t i; for (i = 0; i < ARRAY_SIZE(eval_msgpack_type_lists); i++) { if (type_di->di_tv.vval.v_list == eval_msgpack_type_lists[i]) { @@ -662,7 +662,7 @@ typval_encode_stop_converting_one_item: while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { cur_mpsv->data.d.hi++; } - dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); + dictitem_T *const di = TV_DICT_HI2DI(cur_mpsv->data.d.hi); cur_mpsv->data.d.todo--; cur_mpsv->data.d.hi++; TYPVAL_ENCODE_CONV_STR_STRING(NULL, &di->di_key[0], diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 5cbf7f9ce7..26d70a5e6d 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -21,7 +21,7 @@ struct process { int pid, status, refcount; // set to the hrtime of when process_stop was called for the process. uint64_t stopped_time; - char *cwd; + const char *cwd; char **argv; Stream *in, *out, *err; process_exit_cb cb; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 151a4d375f..9681527fee 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -4764,8 +4764,8 @@ void fix_help_buffer(void) char_u *p; char_u *rt; - /* set filetype to "help". */ - set_option_value((char_u *)"ft", 0L, (char_u *)"help", OPT_LOCAL); + // Set filetype to "help". + set_option_value("ft", 0L, "help", OPT_LOCAL); if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e59a87b335..b84834d351 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2073,9 +2073,9 @@ void ex_listdo(exarg_T *eap) // Clear 'shm' to avoid that the file message overwrites // any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); do_argfile(eap, i); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); } if (curwin->w_arg_idx != i) { @@ -2138,9 +2138,9 @@ void ex_listdo(exarg_T *eap) // Go to the next buffer. Clear 'shm' to avoid that the file // message overwrites any output from the command. p_shm_save = vim_strsave(p_shm); - set_option_value((char_u *)"shm", 0L, (char_u *)"", 0); + set_option_value("shm", 0L, "", 0); goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum); - set_option_value((char_u *)"shm", 0L, p_shm_save, 0); + set_option_value("shm", 0L, (char *)p_shm_save, 0); xfree(p_shm_save); // If autocommands took us elsewhere, quit here. @@ -2496,16 +2496,16 @@ static int APP_BOTH; static void add_pack_plugin(char_u *fname, void *cookie) { char_u *p4, *p3, *p2, *p1, *p; - char_u *new_rtp; - char_u *ffname = (char_u *)fix_fname((char *)fname); + + char *const ffname = fix_fname((char *)fname); if (ffname == NULL) { return; } - if (cookie != &APP_LOAD && strstr((char *)p_rtp, (char *)ffname) == NULL) { + if (cookie != &APP_LOAD && strstr((char *)p_rtp, ffname) == NULL) { // directory is not yet in 'runtimepath', add it - p4 = p3 = p2 = p1 = get_past_head(ffname); + p4 = p3 = p2 = p1 = get_past_head((char_u *)ffname); for (p = p1; *p; mb_ptr_adv(p)) { if (vim_ispathsep_nocolon(*p)) { p4 = p3; p3 = p2; p2 = p1; p1 = p; @@ -2521,13 +2521,13 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = NUL; // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences - size_t fname_len = STRLEN(ffname); - char_u *insp = p_rtp; + size_t fname_len = strlen(ffname); + const char *insp = (const char *)p_rtp; for (;;) { - if (vim_fnamencmp(insp, ffname, fname_len) == 0) { + if (path_fnamencmp(insp, ffname, fname_len) == 0) { break; } - insp = vim_strchr(insp, ','); + insp = strchr(insp, ','); if (insp == NULL) { break; } @@ -2536,10 +2536,10 @@ static void add_pack_plugin(char_u *fname, void *cookie) if (insp == NULL) { // not found, append at the end - insp = p_rtp + STRLEN(p_rtp); + insp = (const char *)p_rtp + STRLEN(p_rtp); } else { // append after the matching directory. - insp += STRLEN(ffname); + insp += strlen(ffname); while (*insp != NUL && *insp != ',') { insp++; } @@ -2547,31 +2547,40 @@ static void add_pack_plugin(char_u *fname, void *cookie) *p4 = c; // check if rtp/pack/name/start/name/after exists - char *afterdir = concat_fnames((char *)ffname, "after", true); + char *afterdir = concat_fnames(ffname, "after", true); size_t afterlen = 0; if (os_isdir((char_u *)afterdir)) { - afterlen = STRLEN(afterdir) + 1; // add one for comma + afterlen = strlen(afterdir) + 1; // add one for comma } - size_t oldlen = STRLEN(p_rtp); - size_t addlen = STRLEN(ffname) + 1; // add one for comma - new_rtp = try_malloc(oldlen + addlen + afterlen + 1); // add one for NUL + const size_t oldlen = STRLEN(p_rtp); + const size_t addlen = strlen(ffname) + 1; // add one for comma + const size_t new_rtp_len = oldlen + addlen + afterlen + 1; + // add one for NUL -------------------------------------^ + char *const new_rtp = try_malloc(new_rtp_len); if (new_rtp == NULL) { goto theend; } - uintptr_t keep = (uintptr_t)(insp - p_rtp); + const size_t keep = (size_t)(insp - (const char *)p_rtp); + size_t new_rtp_fill = 0; memmove(new_rtp, p_rtp, keep); - new_rtp[keep] = ','; - memmove(new_rtp + keep + 1, ffname, addlen); + new_rtp_fill += keep; + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, ffname, addlen); + new_rtp_fill += addlen - 1; + assert(new_rtp[new_rtp_fill] == NUL || new_rtp[new_rtp_fill] == ','); if (p_rtp[keep] != NUL) { - memmove(new_rtp + keep + addlen, p_rtp + keep, - oldlen - keep + 1); + memmove(new_rtp + new_rtp_fill, p_rtp + keep, oldlen - keep + 1); + new_rtp_fill += oldlen - keep; } if (afterlen > 0) { - STRCAT(new_rtp, ","); - STRCAT(new_rtp, afterdir); + assert(new_rtp[new_rtp_fill] == NUL); + new_rtp[new_rtp_fill++] = ','; + memmove(new_rtp + new_rtp_fill, afterdir, afterlen - 1); + new_rtp_fill += afterlen - 1; } - set_option_value((char_u *)"rtp", 0L, new_rtp, 0); + new_rtp[new_rtp_fill] = NUL; + set_option_value("rtp", 0L, new_rtp, 0); xfree(new_rtp); xfree(afterdir); } @@ -2580,7 +2589,7 @@ static void add_pack_plugin(char_u *fname, void *cookie) static const char *plugpat = "%s/plugin/**/*.vim"; // NOLINT static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT - size_t len = STRLEN(ffname) + STRLEN(ftpat); + size_t len = strlen(ffname) + STRLEN(ftpat); char_u *pat = try_malloc(len + 1); if (pat == NULL) { goto theend; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index dc99c0771d..87fe52c119 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9483,18 +9483,18 @@ void dialog_msg(char_u *buff, char *format, char_u *fname) static void ex_behave(exarg_T *eap) { if (STRCMP(eap->arg, "mswin") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"exclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"mouse,key", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"popup", 0); - set_option_value((char_u *)"keymodel", 0L, - (char_u *)"startsel,stopsel", 0); + set_option_value("selection", 0L, "exclusive", 0); + set_option_value("selectmode", 0L, "mouse,key", 0); + set_option_value("mousemodel", 0L, "popup", 0); + set_option_value("keymodel", 0L, "startsel,stopsel", 0); } else if (STRCMP(eap->arg, "xterm") == 0) { - set_option_value((char_u *)"selection", 0L, (char_u *)"inclusive", 0); - set_option_value((char_u *)"selectmode", 0L, (char_u *)"", 0); - set_option_value((char_u *)"mousemodel", 0L, (char_u *)"extend", 0); - set_option_value((char_u *)"keymodel", 0L, (char_u *)"", 0); - } else + set_option_value("selection", 0L, "inclusive", 0); + set_option_value("selectmode", 0L, "", 0); + set_option_value("mousemodel", 0L, "extend", 0); + set_option_value("keymodel", 0L, "", 0); + } else { EMSG2(_(e_invarg2), eap->arg); + } } /* @@ -9608,8 +9608,9 @@ void filetype_maybe_enable(void) */ static void ex_setfiletype(exarg_T *eap) { - if (!did_filetype) - set_option_value((char_u *)"filetype", 0L, eap->arg, OPT_LOCAL); + if (!did_filetype) { + set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL); + } } static void ex_digraphs(exarg_T *eap) @@ -9695,7 +9696,8 @@ static void ex_match(exarg_T *eap) c = *end; *end = NUL; - match_add(curwin, g, p + 1, 10, id, NULL, NULL); + match_add(curwin, (const char *)g, (const char *)p + 1, 10, id, + NULL, NULL); xfree(g); *end = c; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9851ed5396..872b7fe365 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -5173,7 +5173,7 @@ static int ex_window(void) // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); curwin->w_p_rl = cmdmsg_rl; cmdmsg_rl = false; curbuf->b_p_ma = true; @@ -5191,7 +5191,7 @@ static int ex_window(void) add_map((char_u *)" ", INSERT); add_map((char_u *)" a", NORMAL); } - set_option_value((char_u *)"ft", 0L, (char_u *)"vim", OPT_LOCAL); + set_option_value("ft", 0L, "vim", OPT_LOCAL); } /* Reset 'textwidth' after setting 'filetype' (the Vim filetype plugin diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b73d9944ce..8ab6955042 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1556,7 +1556,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0c131d7b33..076ee13a80 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1881,9 +1881,8 @@ static int vgetorpeek(int advance) (size_t)(mlen - typebuf.tb_maplen)); } - del_typebuf(mlen, 0); /* remove the chars */ - set_option_value((char_u *)"paste", - (long)!p_paste, NULL, 0); + del_typebuf(mlen, 0); // Remove the chars. + set_option_value("paste", !p_paste, NULL, 0); if (!(State & INSERT)) { msg_col = 0; msg_row = (int)Rows - 1; diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index b14bb01c0e..354c9718ba 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -82,7 +82,7 @@ void hash_clear_all(hashtab_T *ht, unsigned int off) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find(hashtab_T *ht, const char_u *key) +hashitem_T *hash_find(const hashtab_T *const ht, const char_u *const key) { return hash_lookup(ht, (const char *)key, STRLEN(key), hash_hash(key)); } @@ -99,7 +99,8 @@ hashitem_T *hash_find(hashtab_T *ht, const char_u *key) /// /// @warning Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) +hashitem_T *hash_find_len(const hashtab_T *const ht, const char *const key, + const size_t len) { return hash_lookup(ht, key, len, hash_hash_len(key, len)); } @@ -115,7 +116,7 @@ hashitem_T *hash_find_len(hashtab_T *ht, const char *key, const size_t len) /// used for that key. /// WARNING: Returned pointer becomes invalid as soon as the hash table /// is changed in any way. -hashitem_T *hash_lookup(hashtab_T *const ht, +hashitem_T *hash_lookup(const hashtab_T *const ht, const char *const key, const size_t key_len, const hash_T hash) { diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 0da2b13f2e..973b97d476 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -70,6 +70,25 @@ typedef struct hashtable_S { hashitem_T ht_smallarray[HT_INIT_SIZE]; /// initial array } hashtab_T; +/// Iterate over a hashtab +/// +/// @param[in] ht Hashtab to iterate over. +/// @param hi Name of the variable with current hashtab entry. +/// @param code Cycle body. +#define HASHTAB_ITER(ht, hi, code) \ + do { \ + hashtab_T *const hi##ht_ = (ht); \ + size_t hi##todo_ = hi##ht_->ht_used; \ + for (hashitem_T *hi = hi##ht_->ht_array; hi##todo_; hi++) { \ + if (!HASHITEM_EMPTY(hi)) { \ + { \ + code \ + } \ + hi##todo_--; \ + } \ + } \ + } while (0) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "hashtab.h.generated.h" #endif diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 650bf76156..5042663041 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -19,6 +19,15 @@ # define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif +/// String with length +/// +/// For use in functions which accept (char *s, size_t len) pair in arguments. +/// +/// @param[in] s Static string. +/// +/// @return `s, sizeof(s) - 1` +#define S_LEN(s) (s), (sizeof(s) - 1) + /* * Position comparisons */ diff --git a/src/nvim/main.c b/src/nvim/main.c index 0c978dc47d..33e1551351 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -803,17 +803,18 @@ static void command_line_scan(mparm_T *parmp) argv_idx = -1; /* skip to next argument */ break; - case 'A': /* "-A" start in Arabic mode */ - set_option_value((char_u *)"arabic", 1L, NULL, 0); + case 'A': { // "-A" start in Arabic mode. + set_option_value("arabic", 1L, NULL, 0); break; - - case 'b': /* "-b" binary mode */ - /* Needs to be effective before expanding file names, because - * for Win32 this makes us edit a shortcut file itself, - * instead of the file it links to. */ + } + case 'b': { // "-b" binary mode. + // Needs to be effective before expanding file names, because + // for Win32 this makes us edit a shortcut file itself, + // instead of the file it links to. set_options_bin(curbuf->b_p_bin, 1, 0); - curbuf->b_p_bin = 1; /* binary file I/O */ + curbuf->b_p_bin = 1; // Binary file I/O. break; + } case 'e': /* "-e" Ex mode */ exmode_active = EXMODE_NORMAL; @@ -830,24 +831,27 @@ static void command_line_scan(mparm_T *parmp) main_start_gui(); break; - case 'F': /* "-F" start in Farsi mode: rl + fkmap set */ - p_fkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'F': { // "-F" start in Farsi mode: rl + fkmap set. + p_fkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } case 'h': /* "-h" give help message */ usage(); mch_exit(0); - case 'H': /* "-H" start in Hebrew mode: rl + hkmap set */ - p_hkmap = TRUE; - set_option_value((char_u *)"rl", 1L, NULL, 0); + case 'H': { // "-H" start in Hebrew mode: rl + hkmap set. + p_hkmap = true; + set_option_value("rl", 1L, NULL, 0); break; + } - case 'l': /* "-l" lisp mode, 'lisp' and 'showmatch' on */ - set_option_value((char_u *)"lisp", 1L, NULL, 0); - p_sm = TRUE; + case 'l': { // "-l" lisp mode, 'lisp' and 'showmatch' on. + set_option_value("lisp", 1L, NULL, 0); + p_sm = true; break; + } case 'M': /* "-M" no changes or writing of files */ reset_modifiable(); @@ -946,8 +950,7 @@ static void command_line_scan(mparm_T *parmp) /* default is 10: a little bit verbose */ p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value((char_u *)"verbosefile", 0L, - (char_u *)argv[0] + argv_idx, 0); + set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0); argv_idx = (int)STRLEN(argv[0]); } break; @@ -956,7 +959,7 @@ static void command_line_scan(mparm_T *parmp) /* "-w {scriptout}" write to script */ if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); break; } want_argument = TRUE; @@ -1088,7 +1091,7 @@ scripterror: if (ascii_isdigit(*((char_u *)argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value((char_u *)"window", n, NULL, 0); + set_option_value("window", n, NULL, 0); argv_idx = -1; break; } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 1d1e13e7e0..ae94ec7ecd 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -62,7 +62,7 @@ int setmark(int c) /// Free fmark_T item void free_fmark(fmark_T fm) { - dict_unref(fm.additional_data); + tv_dict_unref(fm.additional_data); } /// Free xfmark_T item diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 57518a9535..621fa6fefc 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -583,7 +583,7 @@ int utf_ptr2char(const char_u *p) * If byte sequence is illegal or incomplete, returns -1 and does not advance * "s". */ -static int utf_safe_read_char_adv(char_u **s, size_t *n) +static int utf_safe_read_char_adv(const char_u **s, size_t *n) { int c; @@ -1233,7 +1233,8 @@ bool utf_isupper(int a) return utf_tolower(a) != a; } -static int utf_strnicmp(char_u *s1, char_u *s2, size_t n1, size_t n2) +static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, + size_t n2) { int c1, c2, cdiff; char_u buffer[6]; @@ -1392,19 +1393,26 @@ int utf16_to_utf8(const WCHAR *strw, char **str) * Returns zero if s1 and s2 are equal (ignoring case), the difference between * two characters otherwise. */ -int mb_strnicmp(char_u *s1, char_u *s2, size_t nn) +int mb_strnicmp(const char_u *s1, const char_u *s2, const size_t nn) { return utf_strnicmp(s1, s2, nn, nn); } -/* We need to call mb_stricmp() even when we aren't dealing with a multi-byte - * encoding because mb_stricmp() takes care of all ascii and non-ascii - * encodings, including characters with umlauts in latin1, etc., while - * STRICMP() only handles the system locale version, which often does not - * handle non-ascii properly. */ -int mb_stricmp(char_u *s1, char_u *s2) +/// Compare strings case-insensitively +/// +/// @note We need to call mb_stricmp() even when we aren't dealing with +/// a multi-byte encoding because mb_stricmp() takes care of all ASCII and +/// non-ascii encodings, including characters with umlauts in latin1, +/// etc., while STRICMP() only handles the system locale version, which +/// often does not handle non-ascii properly. +/// +/// @param[in] s1 First string to compare, not more then #MAXCOL characters. +/// @param[in] s2 Second string to compare, not more then #MAXCOL characters. +/// +/// @return 0 if strings are equal, <0 if s1 < s2, >0 if s1 > s2. +int mb_stricmp(const char *s1, const char *s2) { - return mb_strnicmp(s1, s2, MAXCOL); + return mb_strnicmp((const char_u *)s1, (const char_u *)s2, MAXCOL); } /* @@ -2020,8 +2028,8 @@ void * my_iconv_open(char_u *to, char_u *from) * Returns the converted string in allocated memory. NULL for an error. * If resultlenp is not NULL, sets it to the result length in bytes. */ -static char_u * iconv_string(vimconv_T *vcp, char_u *str, size_t slen, - size_t *unconvlenp, size_t *resultlenp) +static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, + size_t slen, size_t *unconvlenp, size_t *resultlenp) { const char *from; size_t fromlen; @@ -2306,7 +2314,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, * Illegal chars are often changed to "?", unless vcp->vc_fail is set. * When something goes wrong, NULL is returned and "*lenp" is unchanged. */ -char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) +char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp) { return string_convert_ext(vcp, ptr, lenp, NULL); } @@ -2316,7 +2324,7 @@ char_u * string_convert(vimconv_T *vcp, char_u *ptr, size_t *lenp) * an incomplete sequence at the end it is not converted and "*unconvlenp" is * set to the number of remaining bytes. */ -char_u * string_convert_ext(vimconv_T *vcp, char_u *ptr, +char_u * string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp, size_t *unconvlenp) { char_u *retval = NULL; diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index 5f5bab9fcd..c20e6d47ff 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -2,8 +2,10 @@ #define NVIM_MBYTE_H #include +#include #include "nvim/iconv.h" +#include "nvim/func_attr.h" /* * Return byte length of character that starts with byte "b". @@ -66,4 +68,17 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif + +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) + REAL_FATTR_NONNULL_ALL REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Compare strings +/// +/// @param[in] ic True if case is to be ignored. +/// +/// @return 0 if s1 == s2, <0 if s1 < s2, >0 if s1 > s2. +static inline int mb_strcmp_ic(bool ic, const char *s1, const char *s2) +{ + return (ic ? mb_stricmp(s1, s2) : strcmp(s1, s2)); +} #endif // NVIM_MBYTE_H diff --git a/src/nvim/memline.c b/src/nvim/memline.c index f9d3751390..5ea2397db3 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -992,7 +992,7 @@ void ml_recover(void) if (b0_ff != 0) set_fileformat(b0_ff - 1, OPT_LOCAL); if (b0_fenc != NULL) { - set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, TRUE); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 85cef59aec..d634a8c393 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -16,6 +16,7 @@ #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" @@ -888,7 +889,7 @@ static void set_yreg_additional_data(yankreg_T *reg, dict_T *additional_data) if (reg->additional_data == additional_data) { return; } - dict_unref(reg->additional_data); + tv_dict_unref(reg->additional_data); reg->additional_data = additional_data; } @@ -2581,7 +2582,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; - dict_clear(dict); + tv_dict_clear(dict); recursive = false; } diff --git a/src/nvim/option.c b/src/nvim/option.c index b037b0ae35..4c8606a999 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -25,6 +25,7 @@ #include #include "nvim/vim.h" +#include "nvim/macros.h" #include "nvim/ascii.h" #include "nvim/edit.h" #include "nvim/option.h" @@ -34,6 +35,7 @@ #include "nvim/diff.h" #include "nvim/digraph.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" @@ -604,7 +606,7 @@ void set_init_1(void) /* * 'maxmemtot' and 'maxmem' may have to be adjusted for available memory */ - opt_idx = findoption((char_u *)"maxmemtot"); + opt_idx = findoption("maxmemtot"); if (opt_idx >= 0) { { /* Use half of amount of memory available to Vim. */ @@ -614,7 +616,7 @@ void set_init_1(void) ? UINTPTR_MAX : (uintptr_t)(available_kib /2); options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; - opt_idx = findoption((char_u *)"maxmem"); + opt_idx = findoption("maxmem"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)n; } @@ -645,7 +647,7 @@ void set_init_1(void) } } buf[j] = NUL; - opt_idx = findoption((char_u *)"cdpath"); + opt_idx = findoption("cdpath"); if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = buf; options[opt_idx].flags |= P_DEF_ALLOCED; @@ -764,8 +766,9 @@ void set_init_1(void) * NOTE: mlterm's author is being asked to 'set' a variable * instead of an environment variable due to inheritance. */ - if (os_env_exists("MLTERM")) - set_option_value((char_u *)"tbidi", 1L, NULL, 0); + if (os_env_exists("MLTERM")) { + set_option_value("tbidi", 1L, NULL, 0); + } didset_options2(); @@ -775,7 +778,7 @@ void set_init_1(void) char_u *p = enc_locale(); if (p == NULL) { // use utf-8 as 'default' if locale encoding can't be detected. - p = vim_strsave((char_u *)"utf-8"); + p = (char_u *)xmemdupz(S_LEN("utf-8")); } fenc_default = p; @@ -882,7 +885,7 @@ set_options_default ( static void set_string_default(const char *name, char *val, bool allocated) FUNC_ATTR_NONNULL_ALL { - int opt_idx = findoption((char_u *)name); + int opt_idx = findoption(name); if (opt_idx >= 0) { if (options[opt_idx].flags & P_DEF_ALLOCED) { xfree(options[opt_idx].def_val[VI_DEFAULT]); @@ -904,9 +907,10 @@ void set_number_default(char *name, long val) { int opt_idx; - opt_idx = findoption((char_u *)name); - if (opt_idx >= 0) + opt_idx = findoption(name); + if (opt_idx >= 0) { options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val; + } } #if defined(EXITFREE) @@ -947,17 +951,19 @@ void set_init_2(void) * wrong when the window height changes. */ set_number_default("scroll", Rows / 2); - idx = findoption((char_u *)"scroll"); - if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) + idx = findoption("scroll"); + if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL, p_cp); + } comp_col(); /* * 'window' is only for backwards compatibility with Vi. * Default is Rows - 1. */ - if (!option_was_set((char_u *)"window")) + if (!option_was_set("window")) { p_window = Rows - 1; + } set_number_default("window", Rows - 1); parse_shape_opt(SHAPE_CURSOR); /* set cursor shapes from 'guicursor' */ (void)parse_printoptions(); /* parse 'printoptions' default value */ @@ -976,16 +982,18 @@ void set_init_3(void) int idx_sp; int do_sp; - idx_srr = findoption((char_u *)"srr"); - if (idx_srr < 0) - do_srr = FALSE; - else + idx_srr = findoption("srr"); + if (idx_srr < 0) { + do_srr = false; + } else { do_srr = !(options[idx_srr].flags & P_WAS_SET); - idx_sp = findoption((char_u *)"sp"); - if (idx_sp < 0) - do_sp = FALSE; - else + } + idx_sp = findoption("sp"); + if (idx_sp < 0) { + do_sp = false; + } else { do_sp = !(options[idx_sp].flags & P_WAS_SET); + } size_t len = 0; char_u *p = (char_u *)invocation_path_tail(p_sh, &len); @@ -1029,7 +1037,7 @@ void set_init_3(void) } if (bufempty()) { - int idx_ffs = findoption((char_u *)"ffs"); + int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. if (idx_ffs >= 0 && (options[idx_ffs].flags & P_WAS_SET)) { @@ -1048,14 +1056,16 @@ void set_helplang_default(const char *lang) { int idx; - if (lang == NULL || STRLEN(lang) < 2) /* safety check */ + const size_t lang_len = strlen(lang); + if (lang == NULL || lang_len < 2) { // safety check return; - idx = findoption((char_u *)"hlg"); + } + idx = findoption("hlg"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { if (options[idx].flags & P_ALLOCED) free_string_option(p_hlg); - p_hlg = (char_u *)xstrdup(lang); - /* zh_CN becomes "cn", zh_TW becomes "tw". */ + p_hlg = (char_u *)xmemdupz(lang, lang_len); + // zh_CN becomes "cn", zh_TW becomes "tw". if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); @@ -1082,12 +1092,12 @@ void set_title_defaults(void) * icon name. Saves a bit of time, because the X11 display server does * not need to be contacted. */ - idx1 = findoption((char_u *)"title"); + idx1 = findoption("title"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_title = 0; } - idx1 = findoption((char_u *)"icon"); + idx1 = findoption("icon"); if (idx1 >= 0 && !(options[idx1].flags & P_WAS_SET)) { options[idx1].def_val[VI_DEFAULT] = (char_u *)(intptr_t)0; p_icon = 0; @@ -1193,7 +1203,7 @@ do_set ( goto skip; } if (arg[1] == 't' && arg[2] == '_') { // could be term code - opt_idx = findoption_len(arg + 1, (size_t) (len - 1)); + opt_idx = findoption_len((const char *)arg + 1, (size_t)(len - 1)); } len++; if (opt_idx == -1) { @@ -1209,7 +1219,7 @@ do_set ( len++; } } - opt_idx = findoption_len(arg, (size_t) len); + opt_idx = findoption_len((const char *)arg, (size_t)len); if (opt_idx == -1) { key = find_key_option(arg); } @@ -1391,11 +1401,10 @@ do_set ( value = prefix; } - errmsg = set_bool_option(opt_idx, varp, (int)value, - opt_flags); - } else { /* numeric or string */ - if (vim_strchr((char_u *)"=:&<", nextchar) == NULL - || prefix != 1) { + errmsg = (char_u *)set_bool_option(opt_idx, varp, (int)value, + opt_flags); + } else { // Numeric or string. + if (strchr("=:&<", nextchar) == NULL || prefix != 1) { errmsg = e_invarg; goto skip; } @@ -1448,15 +1457,19 @@ do_set ( goto skip; } - if (adding) + if (adding) { value = *(long *)varp + value; - if (prepending) + } + if (prepending) { value = *(long *)varp * value; - if (removing) + } + if (removing) { value = *(long *)varp - value; - errmsg = set_num_option(opt_idx, varp, value, - errbuf, sizeof(errbuf), opt_flags); - } else if (opt_idx >= 0) { /* string */ + } + errmsg = (char_u *)set_num_option(opt_idx, varp, value, + errbuf, sizeof(errbuf), + opt_flags); + } else if (opt_idx >= 0) { // String. char_u *save_arg = NULL; char_u *s = NULL; char_u *oldval = NULL; // previous value if *varp @@ -2221,7 +2234,7 @@ static void check_string_option(char_u **pp) */ int was_set_insecurely(char_u *opt, int opt_flags) { - int idx = findoption(opt); + int idx = findoption((const char *)opt); if (idx >= 0) { uint32_t *flagp = insecure_flag(idx, opt_flags); @@ -2283,9 +2296,9 @@ set_string_option_direct ( int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0; int idx = opt_idx; - if (idx == -1) { /* use name */ - idx = findoption(name); - if (idx < 0) { /* not found (should not happen) */ + if (idx == -1) { // Use name. + idx = findoption((const char *)name); + if (idx < 0) { // Not found (should not happen). EMSG2(_(e_intern2), "set_string_option_direct()"); EMSG2(_("For option %s"), name); return; @@ -2765,7 +2778,7 @@ did_set_string_option ( // option. opt_idx = ((options[opt_idx].fullname[0] == 'v') ? (shada_idx == -1 - ? ((shada_idx = findoption((char_u *) "shada"))) + ? ((shada_idx = findoption("shada"))) : shada_idx) : opt_idx); // Update free_oldval now that we have the opt_idx for 'shada', otherwise @@ -3575,24 +3588,24 @@ static void set_option_scriptID_idx(int opt_idx, int opt_flags, int id) } } -/* - * Set the value of a boolean option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_bool_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - int value, /* new value */ - int opt_flags /* OPT_LOCAL and/or OPT_GLOBAL */ -) +/// Set the value of a boolean option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param[in] opt_flags OPT_LOCAL and/or OPT_GLOBAL. +/// +/// @return NULL on success, error message on error. +static char *set_bool_option(const int opt_idx, char_u *const varp, + const int value, + const int opt_flags) { int old_value = *(int *)varp; /* Disallow changing some options from secure mode */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *(int *)varp = value; /* set the new value */ @@ -3605,20 +3618,18 @@ set_bool_option ( *(int *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = value; // Ensure that options set to p_force_on cannot be disabled. - if ((int *)varp == &p_force_on && p_force_on == FALSE) { - p_force_on = TRUE; - return e_unsupportedoption; - } + if ((int *)varp == &p_force_on && p_force_on == false) { + p_force_on = true; + return (char *)e_unsupportedoption; // Ensure that options set to p_force_off cannot be enabled. - else if ((int *)varp == &p_force_off && p_force_off == TRUE) { - p_force_off = FALSE; - return e_unsupportedoption; - } - /* 'undofile' */ - else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { - /* Only take action when the option was set. When reset we do not - * delete the undo file, the option may be set again without making - * any changes in between. */ + } else if ((int *)varp == &p_force_off && p_force_off == true) { + p_force_off = false; + return (char *)e_unsupportedoption; + // 'undofile' + } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { + // Only take action when the option was set. When reset we do not + // delete the undo file, the option may be set again without making + // any changes in between. if (curbuf->b_p_udf || p_udf) { char_u hash[UNDO_HASH_SIZE]; buf_T *save_curbuf = curbuf; @@ -3740,8 +3751,8 @@ set_bool_option ( if (curwin->w_p_pvw) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { if (win->w_p_pvw && win != curwin) { - curwin->w_p_pvw = FALSE; - return (char_u *)N_("E590: A preview window already exists"); + curwin->w_p_pvw = false; + return N_("E590: A preview window already exists"); } } } @@ -3889,9 +3900,8 @@ set_bool_option ( /* set 'delcombine' */ p_deco = TRUE; - /* Force-set the necessary keymap for arabic */ - set_option_value((char_u *)"keymap", 0L, (char_u *)"arabic", - OPT_LOCAL); + // Force-set the necessary keymap for arabic. + set_option_value("keymap", 0L, "arabic", OPT_LOCAL); p_altkeymap = 0; p_hkmap = 0; p_fkmap = 0; @@ -3957,20 +3967,18 @@ set_bool_option ( return NULL; } -/* - * Set the value of a number option, and take care of side effects. - * Returns NULL for success, or an error message for an error. - */ -static char_u * -set_num_option ( - int opt_idx, /* index in options[] table */ - char_u *varp, /* pointer to the option variable */ - long value, /* new value */ - char_u *errbuf, /* buffer for error messages */ - size_t errbuflen, /* length of "errbuf" */ - int opt_flags /* OPT_LOCAL, OPT_GLOBAL and - OPT_MODELINE */ -) +/// Set the value of a number option, taking care of side effects +/// +/// @param[in] opt_idx Option index in options[] table. +/// @param[out] varp Pointer to the option variable. +/// @param[in] value New value. +/// @param errbuf Buffer for error messages. +/// @param[in] errbuflen Length of `errbuf`. +/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. +/// +/// @return NULL on success, error message on error. +static char *set_num_option(int opt_idx, char_u *varp, long value, + char_u *errbuf, size_t errbuflen, int opt_flags) { char_u *errmsg = NULL; long old_value = *(long *)varp; @@ -3981,7 +3989,7 @@ set_num_option ( /* Disallow changing some options from secure mode. */ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { - return e_secure; + return (char *)e_secure; } *pp = value; @@ -4251,8 +4259,9 @@ set_num_option ( cmdline_row = (int)(Rows - p_ch); } } - if (p_window >= Rows || !option_was_set((char_u *)"window")) + if (p_window >= Rows || !option_was_set("window")) { p_window = Rows - 1; + } } if (curbuf->b_p_ts <= 0) { @@ -4357,7 +4366,7 @@ set_num_option ( curwin->w_set_curswant = TRUE; check_redraw(options[opt_idx].flags); - return errmsg; + return (char *)errmsg; } /* @@ -4395,39 +4404,36 @@ static void check_redraw(uint32_t flags) /// @param[in] len Length of the option. /// /// @return Index of the option or -1 if option was not found. -int findoption_len(const char_u *const arg, const size_t len) +int findoption_len(const char *const arg, const size_t len) { - char *s, *p; + const char *s; + const char *p; static int quick_tab[27] = { 0, 0 }; // quick access table - int is_term_opt; - /* - * For first call: Initialize the quick-access table. - * It contains the index for the first option that starts with a certain - * letter. There are 26 letters, plus the first "t_" option. - */ + // For first call: Initialize the quick-access table. + // It contains the index for the first option that starts with a certain + // letter. There are 26 letters, plus the first "t_" option. if (quick_tab[1] == 0) { p = options[0].fullname; for (short int i = 1; (s = options[i].fullname) != NULL; i++) { if (s[0] != p[0]) { - if (s[0] == 't' && s[1] == '_') + if (s[0] == 't' && s[1] == '_') { quick_tab[26] = i; - else + } else { quick_tab[CharOrdLow(s[0])] = i; + } } p = s; } } - /* - * Check for name starting with an illegal character. - */ + // Check for name starting with an illegal character. if (len == 0 || arg[0] < 'a' || arg[0] > 'z') { return -1; } int opt_idx; - is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); + const bool is_term_opt = (len > 2 && arg[0] == 't' && arg[1] == '_'); if (is_term_opt) { opt_idx = quick_tab[26]; } else { @@ -4435,7 +4441,7 @@ int findoption_len(const char_u *const arg, const size_t len) } // Match full name for (; (s = options[opt_idx].fullname) != NULL; opt_idx++) { - if (STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } } @@ -4444,20 +4450,22 @@ int findoption_len(const char_u *const arg, const size_t len) // Match short name for (; options[opt_idx].fullname != NULL; opt_idx++) { s = options[opt_idx].shortname; - if (s != NULL && STRNCMP(arg, s, len) == 0 && s[len] == NUL) { + if (s != NULL && strncmp(arg, s, len) == 0 && s[len] == NUL) { break; } s = NULL; } } - if (s == NULL) + if (s == NULL) { opt_idx = -1; + } return opt_idx; } -bool is_tty_option(char *name) +bool is_tty_option(const char *name) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || !strcmp((char *)name, "term"); + return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; } #define TCO_BUFFER_SIZE 8 @@ -4493,7 +4501,7 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(char *name, char *value) +bool set_tty_option(const char *name, const char *value) { if (!strcmp(name, "t_Co")) { int colors = atoi(value); @@ -4504,23 +4512,24 @@ bool set_tty_option(char *name, char *value) if (colors != t_colors) { t_colors = colors; // We now have a different color setup, initialize it again. - init_highlight(TRUE, FALSE); + init_highlight(true, false); } return true; } - return is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype"); + return (is_tty_option(name) || !strcmp(name, "term") + || !strcmp(name, "ttytype")); } -/* - * Find index for option 'arg'. - * Return -1 if not found. - */ -static int findoption(char_u *arg) +/// Find index for an option +/// +/// @param[in] arg Option name. +/// +/// @return Option index or -1 if option was not found. +static int findoption(const char *const arg) { - return findoption_len(arg, STRLEN(arg)); + return findoption_len(arg, strlen(arg)); } /* @@ -4548,9 +4557,10 @@ get_option_value ( int opt_idx; char_u *varp; - opt_idx = findoption(name); - if (opt_idx < 0) /* unknown option */ + opt_idx = findoption((const char *)name); + if (opt_idx < 0) { // Unknown option. return -3; + } varp = get_varp_scope(&(options[opt_idx]), opt_flags); @@ -4608,7 +4618,7 @@ int get_option_value_strict(char *name, char_u *varp = NULL; vimoption_T *p; int rv = 0; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { return 0; } @@ -4702,21 +4712,19 @@ int get_option_value_strict(char *name, return rv; } -/* - * Set the value of option "name". - * Use "string" for string options, use "number" for other options. - * - * Returns NULL on success or error message on error. - */ -char_u * -set_option_value ( - char_u *name, - long number, - char_u *string, - int opt_flags /* OPT_LOCAL or 0 (both) */ -) +/// Set the value of an option +/// +/// @param[in] name Option name. +/// @param[in] number New value for the number or boolean option. +/// @param[in] string New value for string option. +/// @param[in] opt_flags Flags: OPT_LOCAL or 0 (both). +/// +/// @return NULL on success, error message on error. +char *set_option_value(const char *const name, const long number, + const char *const string, const int opt_flags) + FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option((char *)name, (char *)string)) { + if (set_tty_option(name, string)) { return NULL; } @@ -4724,9 +4732,9 @@ set_option_value ( char_u *varp; opt_idx = findoption(name); - if (opt_idx < 0) + if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); - else { + } else { uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { @@ -4734,11 +4742,11 @@ set_option_value ( return NULL; } if (flags & P_STRING) { - const char *s = (const char *)string; + const char *s = string; if (s == NULL) { s = ""; } - return (char_u *)set_string_option(opt_idx, s, opt_flags); + return set_string_option(opt_idx, s, opt_flags); } else { varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp != NULL) { /* hidden option is not changed */ @@ -4757,12 +4765,11 @@ set_option_value ( return NULL; // do nothing as we hit an error } } - if (flags & P_NUM) - return set_num_option(opt_idx, varp, number, - NULL, 0, opt_flags); - else - return set_bool_option(opt_idx, varp, (int)number, - opt_flags); + if (flags & P_NUM) { + return set_num_option(opt_idx, varp, number, NULL, 0, opt_flags); + } else { + return set_bool_option(opt_idx, varp, (int)number, opt_flags); + } } } } @@ -4773,9 +4780,10 @@ char_u *get_highlight_default(void) { int i; - i = findoption((char_u *)"hl"); - if (i >= 0) + i = findoption("hl"); + if (i >= 0) { return options[i].def_val[VI_DEFAULT]; + } return (char_u *)NULL; } @@ -5212,7 +5220,7 @@ void unset_global_local_option(char *name, void *from) vimoption_T *p; buf_T *buf = (buf_T *)from; - int opt_idx = findoption((uint8_t *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { EMSG2(_("E355: Unknown option: %s"), name); return; @@ -5775,11 +5783,12 @@ void reset_modifiable(void) { int opt_idx; - curbuf->b_p_ma = FALSE; - p_ma = FALSE; - opt_idx = findoption((char_u *)"ma"); - if (opt_idx >= 0) - options[opt_idx].def_val[VI_DEFAULT] = FALSE; + curbuf->b_p_ma = false; + p_ma = false; + opt_idx = findoption("ma"); + if (opt_idx >= 0) { + options[opt_idx].def_val[VI_DEFAULT] = false; + } } /* @@ -5877,15 +5886,15 @@ set_context_in_set_cmd ( expand_option_name[2] = p[-2]; expand_option_name[3] = p[-1]; } else { - /* Allow * wildcard */ - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') + // Allow * wildcard. + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '*') { p++; - if (*p == NUL) + } + if (*p == NUL) { return; + } nextchar = *p; - *p = NUL; - opt_idx = findoption(arg); - *p = nextchar; + opt_idx = findoption_len((const char *)arg, (size_t)(p - arg)); if (opt_idx == -1 || options[opt_idx].var == NULL) { xp->xp_context = EXPAND_NOTHING; return; @@ -6047,7 +6056,7 @@ void ExpandOldSetting(int *num_file, char_u ***file) * For a terminal key code expand_option_idx is < 0. */ if (expand_option_idx < 0) { - expand_option_idx = findoption(expand_option_name); + expand_option_idx = findoption((const char *)expand_option_name); } if (expand_option_idx >= 0) { @@ -6447,20 +6456,22 @@ void vimrc_found(char_u *fname, char_u *envname) } } -/* - * Return TRUE when option "name" has been set. - * Only works correctly for global options. - */ -int option_was_set(char_u *name) +/// Check whether global option has been set +/// +/// @param[in] name Option name. +/// +/// @return True if it was set. +static bool option_was_set(const char *name) { int idx; idx = findoption(name); - if (idx < 0) /* unknown option */ - return FALSE; - if (options[idx].flags & P_WAS_SET) - return TRUE; - return FALSE; + if (idx < 0) { // Unknown option. + return false; + } else if (options[idx].flags & P_WAS_SET) { + return true; + } + return false; } /* @@ -6945,10 +6956,11 @@ bool signcolumn_on(win_T *wp) return wp->w_buffer->b_signlist != NULL; } -/// Get window or buffer local options. -dict_T * get_winbuf_options(int bufopt) +/// Get window or buffer local options +dict_T *get_winbuf_options(const int bufopt) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - dict_T *d = dict_alloc(); + dict_T *const d = tv_dict_alloc(); for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { struct vimoption *opt = &options[opt_idx]; diff --git a/src/nvim/path.c b/src/nvim/path.c index dfcafc85de..2bd87b608e 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -282,48 +282,63 @@ bool dir_of_file_exists(char_u *fname) return retval; } -/* - * Versions of fnamecmp() and fnamencmp() that handle '/' and '\' equally - * and deal with 'fileignorecase'. - */ -int vim_fnamecmp(char_u *x, char_u *y) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamecmp(const char *fname1, const char *fname2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - return vim_fnamencmp(x, y, MAXPATHL); + const size_t len1 = strlen(fname1); + const size_t len2 = strlen(fname2); + return path_fnamencmp(fname1, fname2, MAX(len1, len2)); #else - if (p_fic) - return mb_stricmp(x, y); - return STRCMP(x, y); + return mb_strcmp_ic((bool)p_fic, fname1, fname2); #endif } -int vim_fnamencmp(char_u *x, char_u *y, size_t len) +/// Compare two file names +/// +/// Handles '/' and '\\' correctly and deals with &fileignorecase option. +/// +/// @param[in] fname1 First file name. +/// @param[in] fname2 Second file name. +/// @param[in] len Compare at most len bytes. +/// +/// @return 0 if they are equal, non-zero otherwise. +int path_fnamencmp(const char *const fname1, const char *const fname2, + const size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME - char_u *px = x; - char_u *py = y; - int cx = NUL; - int cy = NUL; + int c1 = NUL; + int c2 = NUL; + const char *p1 = fname1; + const char *p2 = fname2; while (len > 0) { - cx = PTR2CHAR(px); - cy = PTR2CHAR(py); - if (cx == NUL || cy == NUL - || ((p_fic ? vim_tolower(cx) != vim_tolower(cy) : cx != cy) - && !(cx == '/' && cy == '\\') - && !(cx == '\\' && cy == '/'))) + c1 = PTR2CHAR(p1); + c2 = PTR2CHAR(p2); + if (c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) + || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; - len -= MB_PTR2LEN(px); - px += MB_PTR2LEN(px); - py += MB_PTR2LEN(py); + } + len -= MB_PTR2LEN(p1); + p1 += MB_PTR2LEN(p1); + p2 += MB_PTR2LEN(p2); } - if (len == 0) - return 0; - return cx - cy; + return c1 - c2; #else - if (p_fic) - return mb_strnicmp(x, y, len); - return STRNCMP(x, y, len); + if (p_fic) { + return mb_strnicmp((const char_u *)fname1, (const char_u *)fname2, len); + } + return strncmp(fname1, fname2, len); #endif } diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ea00afbd86..6346951c05 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -610,13 +610,10 @@ static int pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, - (char_u *)"nofile", OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, - (char_u *)"wipe", OPT_LOCAL); - set_option_value((char_u *)"diff", 0L, - NULL, OPT_LOCAL); + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "nofile", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); + set_option_value("diff", 0L, NULL, OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 323503c4f5..d23059ff22 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1236,7 +1236,7 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_nr = nr; if (type != 1 && !vim_isprintc(type)) /* only printable chars allowed */ type = 0; - qfp->qf_type = type; + qfp->qf_type = (char_u)type; qfp->qf_valid = valid; lastp = &qi->qf_lists[qi->qf_curlist].qf_last; @@ -2581,15 +2581,13 @@ void ex_copen(exarg_T *eap) else { /* Create a new quickfix buffer */ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, oldwin); - /* switch off 'swapfile' */ - set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL); - set_option_value((char_u *)"bt", 0L, (char_u *)"quickfix", - OPT_LOCAL); - set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL); + // Switch off 'swapfile'. + set_option_value("swf", 0L, NULL, OPT_LOCAL); + set_option_value("bt", 0L, "quickfix", OPT_LOCAL); + set_option_value("bh", 0L, "wipe", OPT_LOCAL); RESET_BINDING(curwin); - curwin->w_p_diff = FALSE; - set_option_value((char_u *)"fdm", 0L, (char_u *)"manual", - OPT_LOCAL); + curwin->w_p_diff = false; + set_option_value("fdm", 0L, "manual", OPT_LOCAL); } /* Only set the height when still in the same tab page and there is no @@ -2901,14 +2899,14 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) } } - /* correct cursor position */ - check_lnums(TRUE); + // Correct cursor position. + check_lnums(true); if (old_last == NULL) { // Set the 'filetype' to "qf" each time after filling the buffer. This // resembles reading a file into a buffer, it's more logical when using // autocommands. - set_option_value((char_u *)"ft", 0L, (char_u *)"qf", OPT_LOCAL); + set_option_value("ft", 0L, "qf", OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -4002,7 +4000,7 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; @@ -4057,7 +4055,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) int flags = QF_GETLIST_NONE; int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4070,15 +4068,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } - if (dict_find(what, (char_u *)"all", -1) != NULL) { + if (tv_dict_find(what, S_LEN("all")) != NULL) { flags |= QF_GETLIST_ALL; } - if (dict_find(what, (char_u *)"title", -1) != NULL) { + if (tv_dict_find(what, S_LEN("title")) != NULL) { flags |= QF_GETLIST_TITLE; } - if (dict_find(what, (char_u *)"winid", -1) != NULL) { + if (tv_dict_find(what, S_LEN("winid")) != NULL) { flags |= QF_GETLIST_WINID; } @@ -4132,17 +4130,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, if (d == NULL) continue; - char_u *filename = get_dict_string(d, "filename", true); - int bufnum = (int)get_dict_number(d, "bufnr"); - long lnum = get_dict_number(d, "lnum"); - int col = (int)get_dict_number(d, "col"); - char_u vcol = (char_u)get_dict_number(d, "vcol"); - int nr = (int)get_dict_number(d, "nr"); - char_u *type = get_dict_string(d, "type", true); - char_u *pattern = get_dict_string(d, "pattern", true); - char_u *text = get_dict_string(d, "text", true); + char *const filename = tv_dict_get_string(d, "filename", true); + int bufnum = (int)tv_dict_get_number(d, "bufnr"); + long lnum = tv_dict_get_number(d, "lnum"); + int col = (int)tv_dict_get_number(d, "col"); + char_u vcol = (char_u)tv_dict_get_number(d, "vcol"); + int nr = (int)tv_dict_get_number(d, "nr"); + const char *type_str = tv_dict_get_string(d, "type", false); + const char_u type = (char_u)(uint8_t)(type_str == NULL ? NUL : *type_str); + char *const pattern = tv_dict_get_string(d, "pattern", true); + char *text = tv_dict_get_string(d, "text", true); if (text == NULL) { - text = vim_strsave((char_u *)""); + text = xcalloc(1, 1); } bool valid = true; if ((filename == NULL && bufnum == 0) || (lnum == 0 && pattern == NULL)) { @@ -4162,21 +4161,20 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, int status = qf_add_entry(qi, NULL, // dir - filename, + (char_u *)filename, bufnum, - text, + (char_u *)text, lnum, col, vcol, // vis_col - pattern, // search pattern + (char_u *)pattern, // search pattern nr, - (char_u)(type == NULL ? NUL : *type), + type, valid); xfree(filename); xfree(pattern); xfree(text); - xfree(type); if (status == FAIL) { retval = FAIL; @@ -4213,7 +4211,7 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) newlist = true; } int qf_idx = qi->qf_curlist; // default is the current list - if ((di = dict_find(what, (char_u *)"nr", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { qf_idx = di->di_tv.vval.v_number - 1; @@ -4231,10 +4229,11 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) qf_idx = qi->qf_curlist; } - if ((di = dict_find(what, (char_u *)"title", -1)) != NULL) { + if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { if (di->di_tv.v_type == VAR_STRING) { xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = get_dict_string(what, "title", true); + qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( + what, "title", true); if (qf_idx == qi->qf_curlist) { qf_update_win_titlevar(qi); } diff --git a/src/nvim/search.c b/src/nvim/search.c index 8c56eda7cf..fb9e820928 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -282,7 +282,7 @@ void restore_search_patterns(void) static inline void free_spat(struct spat *const spat) { xfree(spat->pat); - dict_unref(spat->additional_data); + tv_dict_unref(spat->additional_data); } #if defined(EXITFREE) @@ -1290,10 +1290,11 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat) * ignored because we are interested in the next line -- Acevedo */ if ((compl_cont_status & CONT_ADDING) && !(compl_cont_status & CONT_SOL)) { - if ((p_ic ? mb_stricmp(p, pat) : STRCMP(p, pat)) == 0) + if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) { return OK; - } else if (*p != NUL) { /* ignore empty lines */ - /* expanding lines or words */ + } + } else if (*p != NUL) { // Ignore empty lines. + // Expanding lines or words. assert(compl_length >= 0); if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length) : STRNCMP(p, pat, compl_length)) == 0) diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c550cb0888..2fe042cda8 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1623,10 +1623,10 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, for (const hashitem_T *hi= d->dv_hashtab.ht_array; todo; hi++) { \ if (!HASHITEM_EMPTY(hi)) { \ todo--; \ - dictitem_T *const di = HI2DI(hi); \ - const size_t key_len = strlen((const char *) hi->hi_key); \ + dictitem_T *const di = TV_DICT_HI2DI(hi); \ + 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); \ + msgpack_pack_str_body(spacker, (const char *)hi->hi_key, key_len); \ if (encode_vim_to_msgpack(spacker, &di->di_tv, \ _("additional data of ShaDa " what)) \ == FAIL) { \ @@ -3156,17 +3156,17 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemJump: case kSDItemGlobalMark: case kSDItemLocalMark: { - dict_unref(entry->data.filemark.additional_data); + tv_dict_unref(entry->data.filemark.additional_data); xfree(entry->data.filemark.fname); break; } case kSDItemSearchPattern: { - dict_unref(entry->data.search_pattern.additional_data); + tv_dict_unref(entry->data.search_pattern.additional_data); xfree(entry->data.search_pattern.pat); break; } case kSDItemRegister: { - dict_unref(entry->data.reg.additional_data); + tv_dict_unref(entry->data.reg.additional_data); for (size_t i = 0; i < entry->data.reg.contents_size; i++) { xfree(entry->data.reg.contents[i]); } @@ -3192,7 +3192,7 @@ static void shada_free_shada_entry(ShadaEntry *const entry) case kSDItemBufferList: { for (size_t i = 0; i < entry->data.buffer_list.size; i++) { xfree(entry->data.buffer_list.buffers[i].fname); - dict_unref(entry->data.buffer_list.buffers[i].additional_data); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } xfree(entry->data.buffer_list.buffers); break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 5ca5ab3339..56f0350aef 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7134,16 +7134,17 @@ void ex_spelldump(exarg_T *eap) char_u *spl; long dummy; - if (no_spell_checking(curwin)) + if (no_spell_checking(curwin)) { return; - get_option_value((char_u*)"spl", &dummy, &spl, OPT_LOCAL); + } + get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value((char_u*)"spell", true, (char_u*)"", OPT_LOCAL); - set_option_value((char_u*)"spl", dummy, spl, OPT_LOCAL); + set_option_value("spell", true, "", OPT_LOCAL); + set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); if (!bufempty()) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index c108ae4a2c..6ba2801203 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5439,7 +5439,7 @@ static void init_spellfile(void) fname != NULL && strstr((char *)path_tail(fname), ".ascii.") != NULL ? (char_u *)"ascii" : spell_enc()); - set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); + set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } aspath = false; diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 0a27d9dd92..632a5bf2a5 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -1406,14 +1406,14 @@ static int syn_stack_equal(synstate_T *sp) /* If the pointer is different it can still be the * same text. Compare the strings, ignore case when * the start item has the sp_ic flag set. */ - if (bsx->matches[j] == NULL - || six->matches[j] == NULL) + if (bsx->matches[j] == NULL || six->matches[j] == NULL) { break; - if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic - ? mb_stricmp(bsx->matches[j], - six->matches[j]) != 0 - : STRCMP(bsx->matches[j], six->matches[j]) != 0) + } + if (mb_strcmp_ic((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic, + (const char *)bsx->matches[j], + (const char *)six->matches[j]) != 0) { break; + } } } if (j != NSUBEXP) @@ -6510,16 +6510,16 @@ do_highlight ( if (!ui_rgb_attached()) { must_redraw = CLEAR; if (color >= 0) { - if (t_colors < 16) + if (t_colors < 16) { i = (color == 0 || color == 4); - else + } else { i = (color < 7 || color == 8); - /* Set the 'background' option if the value is - * wrong. */ - if (i != (*p_bg == 'd')) - set_option_value((char_u *)"bg", 0L, - i ? (char_u *)"dark" - : (char_u *)"light", 0); + } + // Set the 'background' option if the value is + // wrong. + if (i != (*p_bg == 'd')) { + set_option_value("bg", 0L, (i ? "dark" : "light"), 0); + } } } } @@ -7113,7 +7113,7 @@ set_hl_attr ( * Lookup a highlight group name and return it's ID. * If it is not found, 0 is returned. */ -int syn_name2id(char_u *name) +int syn_name2id(const char_u *name) { int i; char_u name_u[200]; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index b0ffc12b5f..af19a1bfc4 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -773,7 +773,7 @@ do_tag ( cmd[len] = NUL; } - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); dict_add_nr_str(dict, "text", 0L, tag_name); @@ -2768,8 +2768,8 @@ add_tag_field ( int len = 0; int retval; - /* check that the field name doesn't exist yet */ - if (dict_find(dict, (char_u *)field_name, -1) != NULL) { + // Check that the field name doesn't exist yet. + if (tv_dict_find(dict, field_name, -1) != NULL) { if (p_verbose > 0) { verbose_enter(); smsg(_("Duplicate field name: %s"), field_name); @@ -2824,7 +2824,7 @@ int get_tags(list_T *list, char_u *pat) if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) continue; - dict = dict_alloc(); + dict = tv_dict_alloc(); tv_list_append_dict(list, dict); full_fname = tag_full_fname(&tp); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c81e883fb9..85c4950b42 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -226,17 +226,17 @@ Terminal *terminal_open(TerminalOptions opts) rv->invalid_start = 0; rv->invalid_end = opts.height; refresh_screen(rv, curbuf); - set_option_value((uint8_t *)"buftype", 0, (uint8_t *)"terminal", OPT_LOCAL); + set_option_value("buftype", 0, "terminal", OPT_LOCAL); // Default settings for terminal buffers curbuf->b_p_ma = false; // 'nomodifiable' curbuf->b_p_ul = -1; // 'undolevels' curbuf->b_p_scbk = p_scbk; // 'scrollback' curbuf->b_p_tw = 0; // 'textwidth' - set_option_value((uint8_t *)"wrap", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"number", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"relativenumber", false, NULL, OPT_LOCAL); - set_option_value((uint8_t *)"list", false, NULL, OPT_LOCAL); + set_option_value("wrap", false, NULL, OPT_LOCAL); + set_option_value("number", false, NULL, OPT_LOCAL); + set_option_value("relativenumber", false, NULL, OPT_LOCAL); + set_option_value("list", false, NULL, OPT_LOCAL); buf_set_term_title(curbuf, (char *)curbuf->b_ffname); RESET_BINDING(curwin); // Reset cursor in current window. diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 729cf03e15..6e21dccebb 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -317,7 +317,7 @@ static long get_undolevel(void) static inline void zero_fmark_additional_data(fmark_T *fmarks) { for (size_t i = 0; i < NMARKS; i++) { - dict_unref(fmarks[i].additional_data); + tv_dict_unref(fmarks[i].additional_data); fmarks[i].additional_data = NULL; } } @@ -2941,7 +2941,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) dict_T *dict; while (uhp != NULL) { - dict = dict_alloc(); + dict = tv_dict_alloc(); dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); if (uhp == curbuf->b_u_newhead) diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 87e9889465..ae654251f9 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -109,11 +109,14 @@ Error: configure did not run properly.Check auto/config.log. // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS) -/* directions */ -#define FORWARD 1 -#define BACKWARD (-1) -#define FORWARD_FILE 3 -#define BACKWARD_FILE (-3) +/// Directions. +typedef enum { + kDirectionNotSet = 0, + FORWARD = 1, + BACKWARD = (-1), + FORWARD_FILE = 3, + BACKWARD_FILE = (-3), +} Direction; /* return values for functions */ #if !(defined(OK) && (OK == 1)) @@ -282,15 +285,22 @@ enum { #define SHOWCMD_COLS 10 /* columns needed by shown command */ #define STL_MAX_ITEM 80 /* max nr of % in statusline */ -/* - * fnamecmp() is used to compare file names. - * On some systems case in a file name does not matter, on others it does. - * (this does not account for maximum name lengths and things like "../dir", - * thus it is not 100% accurate!) - */ -#define fnamecmp(x, y) vim_fnamecmp((char_u *)(x), (char_u *)(y)) -#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \ - (size_t)(n)) +/// Compare file names +/// +/// On some systems case in a file name does not matter, on others it does. +/// +/// @note Does not account for maximum name lengths and things like "../dir", +/// thus it is not 100% accurate. OS may also use different algorythm for +/// case-insensitive comparison. +/// +/// @param[in] x First file name to compare. +/// @param[in] y Second file name to compare. +/// +/// @return 0 for equal file names, non-zero otherwise. +#define fnamecmp(x, y) path_fnamecmp((const char *)(x), (const char *)(y)) +#define fnamencmp(x, y, n) path_fnamencmp((const char *)(x), \ + (const char *)(y), \ + (size_t)(n)) /* * Enums need a typecast to be used as array index (for Ultrix). diff --git a/src/nvim/window.c b/src/nvim/window.c index 47a97da0cf..a3b0e6fc2d 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2983,8 +2983,8 @@ static tabpage_T *alloc_tabpage(void) tp->handle = ++last_tp_handle; handle_register_tabpage(tp); - /* init t: variables */ - tp->tp_vars = dict_alloc(); + // Init t: variables. + tp->tp_vars = tv_dict_alloc(); init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); tp->tp_diff_invalid = TRUE; tp->tp_ch_used = p_ch; @@ -3811,8 +3811,8 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->handle = ++last_win_id; handle_register_window(new_wp); - /* init w: variables */ - new_wp->w_vars = dict_alloc(); + // Init w: variables. + new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); /* Don't execute autocommands while the window is not properly @@ -5504,9 +5504,9 @@ void restore_buffer(bufref_T *save_curbuf) // Optionally, a desired ID 'id' can be specified (greater than or equal to 1). // If no particular ID is desired, -1 must be specified for 'id'. // Return ID of added match, -1 on failure. -int match_add(win_T *wp, char_u *grp, char_u *pat, +int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, list_T *pos_list, - char_u *conceal_char) + const char *const conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5534,11 +5534,11 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, cur = cur->next; } } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) { + if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { EMSG2(_(e_nogroup), grp); return -1; } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5557,14 +5557,14 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = pat == NULL ? NULL: vim_strsave(pat); + m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); m->hlg_id = hlg_id; m->match.regprog = regprog; m->match.rmm_ic = FALSE; m->match.rmm_maxcol = 0; m->conceal_char = 0; if (conceal_char != NULL) { - m->conceal_char = (*mb_ptr2char)(conceal_char); + m->conceal_char = (*mb_ptr2char)((const char_u *)conceal_char); } // Set up position matches diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua new file mode 100644 index 0000000000..df91e41475 --- /dev/null +++ b/test/functional/eval/match_functions_spec.lua @@ -0,0 +1,39 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command + +before_each(clear) + +describe('setmatches()', function() + it('correctly handles case when both group and pattern entries are numbers', + function() + command('hi def link 1 PreProc') + eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}})) + eq({{ + group='1', + pattern='2', + id=3, + priority=4, + }}, funcs.getmatches()) + eq(0, funcs.setmatches({{group=1, pattern=2, id=3, priority=4, conceal=5}})) + eq({{ + group='1', + pattern='2', + id=3, + priority=4, + conceal='5', + }}, funcs.getmatches()) + eq(0, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) + eq({{ + group='1', + pos1={2}, + pos2={6}, + id=3, + priority=4, + conceal='5', + }}, funcs.getmatches()) + end) +end) diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua index f6279e85e8..adc1af9b8e 100644 --- a/test/functional/eval/string_spec.lua +++ b/test/functional/eval/string_spec.lua @@ -7,7 +7,6 @@ local eval = helpers.eval local exc_exec = helpers.exc_exec local redir_exec = helpers.redir_exec local funcs = helpers.funcs -local write_file = helpers.write_file local NIL = helpers.NIL local source = helpers.source local dedent = helpers.dedent @@ -105,10 +104,8 @@ describe('string() function', function() end) describe('used to represent funcrefs', function() - local fname = 'Xtest-functional-eval-string_spec-fref-script.vim' - before_each(function() - write_file(fname, [[ + source([[ function Test1() endfunction @@ -120,11 +117,6 @@ describe('string() function', function() let g:Test2_f = function('s:Test2') ]]) - command('source ' .. fname) - end) - - after_each(function() - os.remove(fname) end) it('dumps references to built-in functions', function() diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 30753c34ac..057245db1c 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -48,7 +48,7 @@ describe('dictionary change notifications', function() eq({'notification', 'values', {key, vals}}, next_msg()) end - describe('watcher', function() + describe(dict_expr .. ' watcher', function() if dict_init then setup(function() source(dict_init) @@ -58,7 +58,7 @@ describe('dictionary change notifications', function() before_each(function() source([[ function! g:Changed(dict, key, value) - if a:dict != ]]..dict_expr..[[ | + if a:dict isnot ]]..dict_expr..[[ | throw 'invalid dict' endif call rpcnotify(g:channel, 'values', a:key, a:value) diff --git a/test/functional/ex_cmds/quickfix_commands_spec.lua b/test/functional/ex_cmds/quickfix_commands_spec.lua new file mode 100644 index 0000000000..5ab34db3fb --- /dev/null +++ b/test/functional/ex_cmds/quickfix_commands_spec.lua @@ -0,0 +1,83 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local clear = helpers.clear +local funcs = helpers.funcs +local command = helpers.command +local exc_exec = helpers.exc_exec +local write_file = helpers.write_file +local curbufmeths = helpers.curbufmeths + +local file_base = 'Xtest-functional-ex_cmds-quickfix_commands' + +before_each(clear) + +for _, c in ipairs({'l', 'c'}) do + local file = ('%s.%s'):format(file_base, c) + local filecmd = c .. 'file' + local getfcmd = c .. 'getfile' + local addfcmd = c .. 'addfile' + local getlist = (c == 'c') and funcs.getqflist or ( + function() return funcs.getloclist(0) end) + + describe((':%s*file commands'):format(c), function() + before_each(function() + write_file(file, ([[ + %s-1.res:700:10:Line 700 + %s-2.res:800:15:Line 800 + ]]):format(file, file)) + end) + after_each(function() + os.remove(file) + end) + + it('work', function() + command(('%s %s'):format(filecmd, file)) + -- Second line of each entry (i.e. `nr=-1, …`) was obtained from actual + -- results. First line (i.e. `{lnum=…`) was obtained from legacy test. + local list = { + {lnum=700, col=10, text='Line 700', + nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, + {lnum=800, col=15, text='Line 800', + nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, + } + eq(list, getlist()) + eq(('%s-1.res'):format(file), funcs.bufname(list[1].bufnr)) + eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) + + -- Run cfile/lfile from a modified buffer + command('enew!') + curbufmeths.set_lines(1, 1, true, {'Quickfix'}) + eq(('Vim(%s):E37: No write since last change (add ! to override)'):format( + filecmd), + exc_exec(('%s %s'):format(filecmd, file))) + + write_file(file, ([[ + %s-3.res:900:30:Line 900 + ]]):format(file)) + command(('%s %s'):format(addfcmd, file)) + list[#list + 1] = { + lnum=900, col=30, text='Line 900', + nr=-1, bufnr=5, valid=1, pattern='', vcol=0, ['type']='', + } + eq(list, getlist()) + eq(('%s-3.res'):format(file), funcs.bufname(list[3].bufnr)) + + write_file(file, ([[ + %s-1.res:222:77:Line 222 + %s-2.res:333:88:Line 333 + ]]):format(file, file)) + command('enew!') + command(('%s %s'):format(getfcmd, file)) + list = { + {lnum=222, col=77, text='Line 222', + nr=-1, bufnr=2, valid=1, pattern='', vcol=0, ['type']=''}, + {lnum=333, col=88, text='Line 333', + nr=-1, bufnr=3, valid=1, pattern='', vcol=0, ['type']=''}, + } + eq(list, getlist()) + eq(('%s-1.res'):format(file), funcs.bufname(list[1].bufnr)) + eq(('%s-2.res'):format(file), funcs.bufname(list[2].bufnr)) + end) + end) +end diff --git a/test/functional/viml/completion_spec.lua b/test/functional/viml/completion_spec.lua index cd5f4260e0..3c09d71eb7 100644 --- a/test/functional/viml/completion_spec.lua +++ b/test/functional/viml/completion_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear, feed = helpers.clear, helpers.feed local eval, eq, neq = helpers.eval, helpers.eq, helpers.neq local execute, source, expect = helpers.execute, helpers.source, helpers.expect +local meths = helpers.meths if helpers.pending_win32(pending) then return end @@ -814,6 +815,41 @@ describe('completion', function() end) end) + describe('with numeric items', function() + before_each(function() + source([[ + function! TestComplete() abort + call complete(1, g:_complist) + return '' + endfunction + ]]) + meths.set_option('completeopt', 'menuone,noselect') + meths.set_var('_complist', {{ + word=0, + abbr=1, + menu=2, + kind=3, + info=4, + icase=5, + dup=6, + empty=7, + }}) + end) + + it('shows correct variant as word', function() + feed('i=TestComplete()') + screen:expect([[ + ^ | + {1:1 3 2 }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end) + end) end) describe('External completion popupmenu', function() diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 49f929937f..be77ef4c83 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -272,17 +272,17 @@ local lua2typvalt_type_tab = { processed[l].dv_refcount = processed[l].dv_refcount + 1 return typvalt(eval.VAR_DICT, {v_dict=processed[l]}) end - local dct = eval.dict_alloc() + local dct = eval.tv_dict_alloc() dct.dv_refcount = 1 processed[l] = dct local ret = typvalt(eval.VAR_DICT, {v_dict=dct}) for k, v in pairs(l) do if type(k) == 'string' then - local di = eval.dictitem_alloc(to_cstr(k)) + local di = eval.tv_dict_item_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) eval.copy_tv(val_tv, di.di_tv) eval.tv_clear(val_tv) - eval.dict_add(dct, di) + eval.tv_dict_add(dct, di) end end return ret -- cgit From 54bd2e8b731376b29c150f909c6b50103bfbb91a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 22:25:57 +0300 Subject: eval: Make setmatches() return -1 in case of some failures --- src/nvim/eval.c | 17 ++++++++++++----- test/functional/eval/match_functions_spec.lua | 7 +++++++ test/functional/legacy/063_match_and_matchadd_spec.lua | 5 ++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5015deead7..7ec1fda0a8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14680,6 +14680,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) clear_matches(curwin); li = l->lv_first; + bool match_add_failed = false; while (li != NULL) { int i = 0; @@ -14728,17 +14729,23 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) &conceal_di->di_tv) : NULL); if (i == 0) { - match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal); + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; + } } else { - match_add(curwin, group, NULL, priority, id, s, conceal); + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } tv_list_unref(s); s = NULL; } li = li->li_next; } - rettv->vval.v_number = 0; + if (!match_add_failed) { + rettv->vval.v_number = 0; + } } } diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index df91e41475..f6bad59c66 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -36,4 +36,11 @@ describe('setmatches()', function() conceal='5', }}, funcs.getmatches()) end) + + it('fails with -1 if highlight group is not defined', function() + eq(-1, funcs.setmatches({{group=1, pattern=2, id=3, priority=4}})) + eq({}, funcs.getmatches()) + eq(-1, funcs.setmatches({{group=1, pos1={2}, pos2={6}, id=3, priority=4, conceal=5}})) + eq({}, funcs.getmatches()) + end) end) diff --git a/test/functional/legacy/063_match_and_matchadd_spec.lua b/test/functional/legacy/063_match_and_matchadd_spec.lua index 298e0a31ea..5818bb6b3a 100644 --- a/test/functional/legacy/063_match_and_matchadd_spec.lua +++ b/test/functional/legacy/063_match_and_matchadd_spec.lua @@ -97,11 +97,10 @@ describe('063: Test for ":match", "matchadd()" and related functions', function( -- Check that "setmatches()" will not add two matches with the same ID. The -- expected behaviour (for now) is to add the first match but not the - -- second and to return 0 (even though it is a matter of debate whether - -- this can be considered successful behaviour). + -- second and to return -1. execute("let r1 = setmatches([{'group': 'MyGroup1', 'pattern': 'TODO', 'priority': 10, 'id': 1}, {'group': 'MyGroup2', 'pattern': 'FIXME', 'priority': 10, 'id': 1}])") feed("") - eq(0, eval("r1")) + eq(-1, eval("r1")) eq({{group = 'MyGroup1', pattern = 'TODO', priority = 10, id = 1}}, eval('getmatches()')) -- Check that "setmatches()" returns 0 if successful and otherwise -1. -- cgit From ecff8387f465bbf5d57de09498059ff1de671131 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:12:15 +0300 Subject: eval: Move get_dict_callback to typval.c --- src/nvim/eval.c | 265 ++++++++++++++++++++++++------------------------- src/nvim/eval/typval.c | 36 +++++++ 2 files changed, 164 insertions(+), 137 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7ec1fda0a8..8d6b6d2dc1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -729,8 +729,8 @@ void set_internal_string_var(char_u *name, char_u *value) } static lval_T *redir_lval = NULL; -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; +static garray_T redir_ga; // Only valid when redir_lval is not NULL. +static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /* @@ -761,9 +761,9 @@ var_redir_start ( /* The output is stored in growarray "redir_ga" until redirection ends. */ ga_init(&redir_ga, (int)sizeof(char), 500); - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); + // Parse the variable name (can be a dict or list entry). + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, + 0, FNE_CHECK_START); if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) { clear_lval(redir_lval); @@ -839,12 +839,13 @@ void var_redir_stop(void) ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ tv.v_type = VAR_STRING; tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, (char_u *)"."); + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, + false, false, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) { + set_var_lval(redir_lval, redir_endp, &tv, false, (char_u *)"."); + } clear_lval(redir_lval); } @@ -1457,11 +1458,13 @@ void ex_let(exarg_T *eap) char_u *argend; int first = TRUE; - argend = skip_var_list(arg, &var_count, &semicolon); - if (argend == NULL) + argend = (char_u *)skip_var_list(arg, &var_count, &semicolon); + if (argend == NULL) { return; - if (argend > arg && argend[-1] == '.') /* for var.='str' */ - --argend; + } + if (argend > arg && argend[-1] == '.') { // For var.='str'. + argend--; + } expr = skipwhite(argend); if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL && expr[1] == '=')) { @@ -1527,7 +1530,7 @@ ex_let_vars ( char_u *nextchars ) { - char_u *arg = arg_start; + char_u *arg = arg_start; list_T *l; int i; listitem_T *item; @@ -1606,9 +1609,11 @@ ex_let_vars ( * for "[var, var; var]" set "semicolon". * Return NULL for an error. */ -static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) +static const char_u *skip_var_list(const char_u *arg, int *var_count, + int *semicolon) { - char_u *p, *s; + const char_u *p; + const char_u *s; if (*arg == '[') { /* "[var, var]": find the matching ']'. */ @@ -1645,7 +1650,7 @@ static char_u *skip_var_list(char_u *arg, int *var_count, int *semicolon) * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, * l[idx]. */ -static char_u *skip_var_one(char_u *arg) +static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) return arg + 2; @@ -1825,34 +1830,36 @@ static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Set one item of ":let var = expr" or ":let [v1, v2] = list" to its value. - * Returns a pointer to the char just after the var name. - * Returns NULL if there is an error. - */ -static char_u * -ex_let_one ( - char_u *arg, /* points to variable name */ - typval_T *tv, /* value to assign to variable */ - int copy, /* copy value from "tv" */ - char_u *endchars, /* valid chars after variable name or NULL */ - char_u *op /* "+", "-", "." or NULL*/ -) +/// Set one item of `:let var = expr` or `:let [v1, v2] = list` to its value +/// +/// @param[in] arg Start of the variable name. +/// @param[in] tv Value to assign to the variable. +/// @param[in] copy If true, copy value from `tv`. +/// @param[in] endchars Valid characters after variable name or NULL. +/// @param[in] op Operation performed: *op is `+`, `-`, `.` for `+=`, etc. +/// NULL for `=`. +/// +/// @return a pointer to the char just after the var name or NULL in case of +/// error. +static char_u *ex_let_one(char_u *arg, typval_T *const tv, + const bool copy, const char_u *const endchars, + const char_u *const op) + FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; - char_u *arg_end = NULL; + char_u *name; + char_u *arg_end = NULL; int len; int opt_flags; - char_u *tofree = NULL; + char_u *tofree = NULL; /* * ":let $VAR = expr": Set environment variable. */ if (*arg == '$') { - /* Find the end of the name. */ - ++arg; + // Find the end of the name. + arg++; name = arg; - len = get_env_len(&arg); + len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); } else { @@ -1975,9 +1982,9 @@ ex_let_one ( char_u *const p = get_lval(arg, tv, &lv, false, false, 0, FNE_CHECK_START); if (p != NULL && lv.ll_name != NULL) { - if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) + if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); - else { + } else { set_var_lval(&lv, p, tv, copy, op); arg_end = p; } @@ -2020,7 +2027,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, FUNC_ATTR_NONNULL_ARG(1, 3) { char_u *p; - char_u *expr_start, *expr_end; int cc; dictitem_T *v; typval_T var1; @@ -2036,13 +2042,17 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, memset(lp, 0, sizeof(lval_T)); if (skip) { - /* When skipping just find the end of the name. */ - lp->ll_name = name; - return find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + // When skipping just find the end of the name. + lp->ll_name = (char_u *)name; + return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); } - /* Find the end of the name. */ - p = find_name_end(name, &expr_start, &expr_end, fne_flags); + // Find the end of the name. + char_u *expr_start; + char_u *expr_end; + p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2063,8 +2073,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; - } else - lp->ll_name = name; + } else { + lp->ll_name = (char_u *)name; + } /* Without [idx] or .key we are done. */ if ((*p != '[' && *p != '.') || lp->ll_name == NULL) @@ -2351,7 +2362,8 @@ static void clear_lval(lval_T *lp) * "endp" points to just after the parsed name. * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". */ -static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, char_u *op) +static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, + int copy, const char_u *op) { int cc; listitem_T *ri; @@ -2493,10 +2505,10 @@ notify: * Set "*errp" to TRUE for an error, FALSE otherwise; * Return a pointer that holds the info. Null when there is an error. */ -void *eval_for_line(char_u *arg, bool *errp, char_u **nextcmdp, int skip) +void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); - char_u *expr; + const char_u *expr; typval_T tv; list_T *l; @@ -2824,16 +2836,17 @@ void ex_lockvar(exarg_T *eap) static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) { char_u *arg = argstart; - char_u *name_end; - int error = FALSE; + bool error = false; lval_T lv; do { - /* Parse the name and find the end. */ - name_end = get_lval(arg, NULL, &lv, TRUE, eap->skip || error, 0, - FNE_CHECK_START); - if (lv.ll_name == NULL) - error = TRUE; /* error but continue parsing */ + // Parse the name and find the end. + char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true, + eap->skip || error, + 0, FNE_CHECK_START); + if (lv.ll_name == NULL) { + error = true; // error, but continue parsing. + } if (name_end == NULL || (!ascii_iswhite(*name_end) && !ends_excmd(*name_end))) { if (name_end != NULL) { @@ -2868,7 +2881,7 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) // TODO(ZyX-I): move to eval/ex_cmds -static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit) +static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) { int ret = OK; int cc; @@ -3022,7 +3035,7 @@ int do_unlet(char_u *name, int forceit) * "deep" is the levels to go (-1 for unlimited); * "lock" is TRUE for ":lockvar", FALSE for ":unlockvar". */ -static int do_lock_var(lval_T *lp, char_u *name_end, const int deep, +static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, const bool lock) { int ret = OK; @@ -5605,34 +5618,6 @@ void dict_set_keys_readonly(dict_T *const dict) }); } -/// Get a function from a dictionary -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. -/// @return true/false on success/failure. -static bool get_dict_callback(dict_T *d, char *key, Callback *result) -{ - dictitem_T *const di = tv_dict_find(d, key, -1); - - if (di == NULL) { - result->type = kCallbackNone; - return true; - } - - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); - result->type = kCallbackNone; - return false; - } - - typval_T tv; - copy_tv(&di->di_tv, &tv); - set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); - tv_clear(&tv); - return res; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -5997,7 +5982,7 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) ++*arg; name = *arg; - len = get_env_len(arg); + len = get_env_len((const char_u **)arg); if (evaluate) { if (len == 0) { @@ -11291,16 +11276,16 @@ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) { lval_T lv; - char_u *end; dictitem_T *di; rettv->vval.v_number = -1; - end = get_lval(get_tv_string(&argvars[0]), NULL, &lv, false, false, - GLV_NO_AUTOLOAD|GLV_READ_ONLY, FNE_CHECK_START); + const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + &lv, false, false, + GLV_NO_AUTOLOAD, FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { - if (*end != NUL) + if (*end != NUL) { EMSG(_(e_trailing)); - else { + } else { if (lv.ll_tv == NULL) { di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); if (di != NULL) { @@ -16580,7 +16565,8 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } -static bool callback_from_typval(Callback *callback, typval_T *arg) +bool callback_from_typval(Callback *const callback, typval_T *const arg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL) { callback->data.partial = arg->vval.v_partial; @@ -17698,15 +17684,16 @@ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) * Advance "arg" to the first character after the name. * Return 0 for error. */ -static int get_env_len(char_u **arg) +static int get_env_len(const char_u **arg) { - char_u *p; int len; - for (p = *arg; vim_isIDc(*p); ++p) - ; - if (p == *arg) /* no name found */ + const char_u *p; + for (p = *arg; vim_isIDc(*p); p++) { + } + if (p == *arg) { // No name found. return 0; + } len = (int)(p - *arg); *arg = p; @@ -17758,8 +17745,6 @@ static int get_name_len(const char **const arg, int verbose) { int len; - char_u *expr_start; - char_u *expr_end; *alias = NULL; /* default to no alias */ @@ -17775,12 +17760,12 @@ static int get_name_len(const char **const arg, *arg += len; } - /* - * Find the end of the name; check for {} construction. - */ + // Find the end of the name; check for {} construction. + char_u *expr_start; + char_u *expr_end; const char *p = (const char *)find_name_end((char_u *)(*arg), - &expr_start, - &expr_end, + (const char_u **)&expr_start, + (const char_u **)&expr_end, len > 0 ? 0 : FNE_CHECK_START); if (expr_start != NULL) { if (!evaluate) { @@ -17816,12 +17801,11 @@ static int get_name_len(const char **const arg, // "flags" can have FNE_INCL_BR and FNE_CHECK_START. // Return a pointer to just after the name. Equal to "arg" if there is no // valid name. -static char_u *find_name_end(char_u *arg, char_u **expr_start, - char_u **expr_end, int flags) +static const char_u *find_name_end(const char_u *arg, const char_u **expr_start, + const char_u **expr_end, int flags) { int mb_nest = 0; int br_nest = 0; - char_u *p; int len; if (expr_start != NULL) { @@ -17834,6 +17818,7 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, return arg; } + const char_u *p; for (p = arg; *p != NUL && (eval_isnamec(*p) || *p == '{' @@ -17905,7 +17890,8 @@ static char_u *find_name_end(char_u *arg, char_u **expr_start, * Returns a new allocated string, which the caller must free. * Returns NULL for failure. */ -static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) +static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, + char_u *expr_end, char_u *in_end) { char_u c1; char_u *retval = NULL; @@ -17934,7 +17920,9 @@ static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u * *expr_end = '}'; if (retval != NULL) { - temp_result = find_name_end(retval, &expr_start, &expr_end, 0); + temp_result = (char_u *)find_name_end(retval, + (const char_u **)&expr_start, + (const char_u **)&expr_end, 0); if (expr_start != NULL) { /* Further expansion! */ temp_result = make_expanded_name(retval, expr_start, @@ -18351,7 +18339,7 @@ handle_subscript( return ret; } -static void set_selfdict(typval_T *rettv, dict_T *selfdict) +void set_selfdict(typval_T *rettv, dict_T *selfdict) { // Don't do this when "dict.Func" is already a partial that was bound // explicitly (pt_auto is false). @@ -20195,15 +20183,15 @@ ret_free: static char_u * trans_function_name( char_u **pp, - int skip, /* only find the end, don't evaluate */ + int skip, // only find the end, don't evaluate int flags, funcdict_T *fdp, // return: info about dictionary used partial_T **partial // return: partial of a FuncRef ) { char_u *name = NULL; - char_u *start; - char_u *end; + const char_u *start; + const char_u *end; int lead; char_u sid_buf[20]; int len; @@ -20229,9 +20217,9 @@ trans_function_name( start += lead; } - /* Note that TFN_ flags use the same values as GLV_ flags. */ - end = get_lval(start, NULL, &lv, FALSE, skip, flags, - lead > 2 ? 0 : FNE_CHECK_START); + // Note that TFN_ flags use the same values as GLV_ flags. + end = get_lval((char_u *)start, NULL, &lv, false, skip, flags, + lead > 2 ? 0 : FNE_CHECK_START); if (end == start) { if (!skip) EMSG(_("E129: Function name required")); @@ -20244,10 +20232,12 @@ trans_function_name( * interrupt, or an exception. */ if (!aborting()) { - if (end != NULL) - EMSG2(_(e_invarg2), start); - } else - *pp = find_name_end(start, NULL, NULL, FNE_INCL_BR); + if (end != NULL) { + emsgf(_(e_invarg2), start); + } + } else { + *pp = (char_u *)find_name_end(start, NULL, NULL, FNE_INCL_BR); + } goto theend; } @@ -20260,11 +20250,11 @@ trans_function_name( } if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) { name = vim_strsave(lv.ll_tv->vval.v_string); - *pp = end; + *pp = (char_u *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); - *pp = end; + *pp = (char_u *)end; if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; } @@ -20274,7 +20264,7 @@ trans_function_name( || fdp->fd_newkey == NULL)) { EMSG(_(e_funcref)); } else { - *pp = end; + *pp = (char_u *)end; } name = NULL; } @@ -20282,8 +20272,8 @@ trans_function_name( } if (lv.ll_name == NULL) { - /* Error found, but continue after the function name. */ - *pp = end; + // Error found, but continue after the function name. + *pp = (char_u *)end; goto theend; } @@ -20305,7 +20295,7 @@ trans_function_name( } if (name != NULL) { name = vim_strsave(name); - *pp = end; + *pp = (char_u *)end; if (strncmp((char *)name, "", 5) == 0) { // Change "" to the byte sequence. name[0] = K_SPECIAL; @@ -20379,7 +20369,7 @@ trans_function_name( } memmove(name + lead, lv.ll_name, (size_t)len); name[lead + len] = NUL; - *pp = end; + *pp = (char_u *)end; theend: clear_lval(&lv); @@ -20408,8 +20398,8 @@ static int eval_fname_script(const char *const p) /// Check whether function name starts with or s: /// -/// Only works for names previously checked by eval_fname_script(), if it -/// returned non-zero. +/// @warning Only works for names previously checked by eval_fname_script(), if +/// it returned non-zero. /// /// @param[in] name Name to check. /// @@ -20558,14 +20548,15 @@ bool translated_function_exists(const char *name) /// @return True if it exists, false otherwise. static bool function_exists(const char *const name, bool no_deref) { - char_u *nm = (char_u *)name; + const char_u *nm = (const char_u *)name; bool n = false; int flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD; if (no_deref) { flag |= TFN_NO_DEREF; } - char *const p = (char *)trans_function_name(&nm, false, flag, NULL, NULL); + char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL, + NULL); nm = skipwhite(nm); /* Only accept "funcname", "funcname ", "funcname (..." and @@ -22510,9 +22501,9 @@ static inline TerminalJobData *common_job_init(char **argv, static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, Callback *on_stderr, Callback *on_exit) { - if (get_dict_callback(vopts, "on_stdout", on_stdout) - && get_dict_callback(vopts, "on_stderr", on_stderr) - && get_dict_callback(vopts, "on_exit", on_exit)) { + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { vopts->dv_refcount++; return true; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index bb7baed7d2..f4f34e0d92 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1130,6 +1130,42 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); } +/// Get a function from a dictionary +/// +/// @param[in] d Dictionary to get callback from. +/// @param[in] key Dictionary key. +/// @param[in] key_len Key length, may be -1 to use strlen(). +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. +/// +/// @return true/false on success/failure. +bool tv_dict_get_callback(dict_T *const d, + const char *const key, const ptrdiff_t key_len, + Callback *const result) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + result->type = kCallbackNone; + + dictitem_T *const di = tv_dict_find(d, key, key_len); + + if (di == NULL) { + return true; + } + + if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING + && di->di_tv.v_type != VAR_PARTIAL) { + EMSG(_("Argument is not a function or function name")); + return false; + } + + typval_T tv; + copy_tv(&di->di_tv, &tv); + set_selfdict(&tv, d); + bool res = callback_from_typval(result, &tv); + tv_clear(&tv); + return res; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. -- cgit From 983a5532ca17060508ea308a9b7069dae4facb59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:14:50 +0300 Subject: eval: Move dict_set_keys_readonly to typval.c --- src/nvim/eval.c | 15 +-------------- src/nvim/eval/typval.c | 17 +++++++++++++++-- src/nvim/file_search.c | 2 +- src/nvim/ops.c | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8d6b6d2dc1..afbb93ef92 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5605,19 +5605,6 @@ int dict_add_dict(dict_T *d, char *key, dict_T *dict) return OK; } -/// Set all existing keys in "dict" as read-only. -/// -/// This does not protect against adding new keys to the Dictionary. -/// -/// @param dict The dict whose keys should be frozen -void dict_set_keys_readonly(dict_T *const dict) - FUNC_ATTR_NONNULL_ALL -{ - TV_DICT_ITER(dict, di, { - di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; - }); -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -18083,7 +18070,7 @@ void set_vim_var_dict(const VimVarIndex idx, dict_T *const val) if (val != NULL) { val->dv_refcount++; // Set readonly - dict_set_keys_readonly(val); + tv_dict_set_keys_readonly(val); } } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f4f34e0d92..0df818040b 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1135,8 +1135,8 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, /// @param[in] d Dictionary to get callback from. /// @param[in] key Dictionary key. /// @param[in] key_len Key length, may be -1 to use strlen(). -/// @param[out] result The address where a pointer to the wanted callback -/// will be left. +/// @param[out] result The address where a pointer to the wanted callback +/// will be left. /// /// @return true/false on success/failure. bool tv_dict_get_callback(dict_T *const d, @@ -1348,6 +1348,19 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, return copy; } +/// Set all existing keys in "dict" as read-only. +/// +/// This does not protect against adding new keys to the Dictionary. +/// +/// @param dict The dict whose keys should be frozen. +void tv_dict_set_keys_readonly(dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + TV_DICT_ITER(dict, di, { + di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; + }); +} + //{{{1 Generic typval operations //{{{2 Init/alloc/clear //{{{3 Alloc diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 8ab6955042..d7120a7309 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1551,7 +1551,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, NULL); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d634a8c393..4bca2f0889 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2578,7 +2578,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) buf[1] = NUL; dict_add_nr_str(dict, "operator", 0, (char_u *)buf); - dict_set_keys_readonly(dict); + tv_dict_set_keys_readonly(dict); textlock++; apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf); textlock--; -- cgit From 210342d795a13e88579d3e55ae931463d16f241d Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 11 Jan 2017 23:20:03 +0300 Subject: eval: Move dict_add_list and dict_add_dict to typval.c --- src/nvim/eval.c | 57 ++++++++++---------------------------------------- src/nvim/eval/typval.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/nvim/ops.c | 3 ++- src/nvim/undo.c | 3 ++- 4 files changed, 67 insertions(+), 48 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index afbb93ef92..e7c2d95cdd 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5569,42 +5569,6 @@ int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) return OK; } -/* - * Add a list entry to dictionary "d". - * Returns FAIL when key already exists. - */ -int dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/// Add a dict entry to dictionary "d". -/// Returns FAIL when out of memory and when key already exists. -int dict_add_dict(dict_T *d, char *key, dict_T *dict) -{ - dictitem_T *const item = tv_dict_item_alloc(key); - - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_DICT; - item->di_tv.vval.v_dict = dict; - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - dict->dv_refcount++; - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9128,7 +9092,7 @@ static dict_T *get_buffer_info(buf_T *buf) NULL); // Get a reference to buffer variables - dict_add_dict(dict, "variables", buf->b_vars); + tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer list_T *const windows = tv_list_alloc(); @@ -9137,13 +9101,13 @@ static dict_T *get_buffer_info(buf_T *buf) tv_list_append_number(windows, (varnumber_T)wp->handle); } } - dict_add_list(dict, "windows", windows); + tv_dict_add_list(dict, S_LEN("windows"), windows); if (buf->b_signlist != NULL) { // List of signs placed in this buffer list_T *const signs = tv_list_alloc(); get_buffer_signs(buf, signs); - dict_add_list(dict, "signs", signs); + tv_dict_add_list(dict, S_LEN("signs"), signs); } return dict; @@ -9900,7 +9864,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; ++i) { llpos_T *llpos; - char buf[6]; + char buf[6]; llpos = &cur->pos.pos[i]; if (llpos->lnum == 0) { @@ -9912,8 +9876,9 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(l, (varnumber_T)llpos->col); tv_list_append_number(l, (varnumber_T)llpos->len); } - sprintf(buf, "pos%d", i + 1); - dict_add_list(dict, buf, l); + int len = snprintf(buf, sizeof(buf), "pos%d", i + 1); + assert((size_t)len < sizeof(buf)); + tv_dict_add_list(dict, buf, (size_t)len, l); } } else { dict_add_nr_str(dict, "pattern", 0L, cur->pattern); @@ -10082,10 +10047,10 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } - dict_add_list(dict, "windows", l); + tv_dict_add_list(dict, S_LEN("windows"), l); // Make a reference to tabpage variables - dict_add_dict(dict, "variables", tp->tp_vars); + tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars); return dict; } @@ -10187,7 +10152,7 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) NULL); // Add a reference to window variables - dict_add_dict(dict, "variables", wp->w_vars); + tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); return dict; } @@ -17029,7 +16994,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); - dict_add_list(dict, "entries", list); + tv_dict_add_list(dict, S_LEN("entries"), list); } /* diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 0df818040b..f9885926c7 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1166,6 +1166,58 @@ bool tv_dict_get_callback(dict_T *const d, return res; } +//{{{2 dict_add* + +/// Add a list entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param list List to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_list(dict_T *const d, const char *const key, + const size_t key_len, list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + list->lv_refcount++; + return OK; +} + +/// Add a dictionary entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param dict Dictionary to add. Will have reference count incremented. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_dict(dict_T *const d, const char *const key, + const size_t key_len, dict_T *const dict) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc(key); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_DICT; + item->di_tv.vval.v_dict = dict; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + dict->dv_refcount++; + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 4bca2f0889..28ac5aad4e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -42,6 +42,7 @@ #include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/window.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -2561,7 +2562,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) tv_list_append_string(list, (const char *)reg->y_array[i], -1); } list->lv_lock = VAR_FIXED; - dict_add_list(dict, "regcontents", list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type char buf[NUMBUFLEN+2]; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 6e21dccebb..8c75d28f2a 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -82,6 +82,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/undo.h" +#include "nvim/macros.h" #include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/eval.h" @@ -2956,7 +2957,7 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) /* Recursive call to add alternate undo tree. */ u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - dict_add_list(dict, "alt", alt_list); + tv_dict_add_list(dict, S_LEN("alt"), alt_list); } tv_list_append_dict(list, dict); -- cgit From 2dcfc439b2ec2be4d830826061e89860977516be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 20 Aug 2016 23:56:49 +0300 Subject: eval: Split and move dict_add_nr_str to typval.c Function was split into tv_dict_add_nr() and tv_dict_add_str(). --- src/nvim/edit.c | 24 +++++---- src/nvim/eval.c | 143 ++++++++++++++++++++----------------------------- src/nvim/eval/typval.c | 50 ++++++++++++++++- src/nvim/eval/typval.h | 1 + src/nvim/file_search.c | 4 +- src/nvim/ops.c | 31 ++++++----- src/nvim/option.c | 5 +- src/nvim/quickfix.c | 44 +++++++++------ src/nvim/search.c | 5 +- src/nvim/tag.c | 29 +++++----- src/nvim/undo.c | 21 ++++---- 11 files changed, 202 insertions(+), 155 deletions(-) diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 53b196c61f..d1cceb435a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -4034,16 +4034,20 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc(); - dict_add_nr_str(dict, "word", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_str)); - dict_add_nr_str(dict, "abbr", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); - dict_add_nr_str(dict, "menu", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); - dict_add_nr_str(dict, "kind", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); - dict_add_nr_str(dict, "info", 0L, - EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + tv_dict_add_str(dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + tv_dict_add_str( + dict, S_LEN("abbr"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); + tv_dict_add_str( + dict, S_LEN("menu"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_MENU])); + tv_dict_add_str( + dict, S_LEN("kind"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_KIND])); + tv_dict_add_str( + dict, S_LEN("info"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) { compl_curr_match = compl_shown_match; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e7c2d95cdd..a64eb3c959 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5544,31 +5544,6 @@ static bool set_ref_in_funccal(funccall_T *fc, int copyID) return abort; } -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when key already exists. - */ -int dict_add_nr_str(dict_T *d, char *key, long nr, char_u *str) -{ - dictitem_T *item; - - item = tv_dict_item_alloc(key); - item->di_tv.v_lock = 0; - if (str == NULL) { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } else { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (tv_dict_add(d, item) == FAIL) { - tv_dict_item_free(item); - return FAIL; - } - return OK; -} - /* * Allocate a variable for a Dictionary and fill it from "*arg". * Return OK or FAIL. Returns NOTDONE for {expr}. @@ -9066,9 +9041,10 @@ static void get_buffer_signs(buf_T *buf, list_T *l) for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); - dict_add_nr_str(d, "id", sign->id, NULL); - dict_add_nr_str(d, "lnum", sign->lnum, NULL); - dict_add_nr_str(d, "name", 0L, sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), + (const char *)sign_typenr2name(sign->typenr)); tv_list_append_dict(l, d); } @@ -9079,17 +9055,16 @@ static dict_T *get_buffer_info(buf_T *buf) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "bufnr", buf->b_fnum, NULL); - dict_add_nr_str(dict, "name", 0L, - buf->b_ffname != NULL ? buf->b_ffname : (char_u *)""); - dict_add_nr_str(dict, "lnum", buflist_findlnum(buf), NULL); - dict_add_nr_str(dict, "loaded", buf->b_ml.ml_mfp != NULL, NULL); - dict_add_nr_str(dict, "listed", buf->b_p_bl, NULL); - dict_add_nr_str(dict, "changed", bufIsChanged(buf), NULL); - dict_add_nr_str(dict, "changedtick", buf->b_changedtick, NULL); - dict_add_nr_str(dict, "hidden", - buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0, - NULL); + tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); + tv_dict_add_str(dict, S_LEN("name"), + buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); + tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); + tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); + tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); + tv_dict_add_nr(dict, S_LEN("changedtick"), buf->b_changedtick); + tv_dict_add_nr(dict, S_LEN("hidden"), + buf->b_ml.ml_mfp != NULL && buf->b_nwindows == 0); // Get a reference to buffer variables tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); @@ -9411,9 +9386,9 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "char", 0L, last_csearch()); - dict_add_nr_str(dict, "forward", last_csearch_forward(), NULL); - dict_add_nr_str(dict, "until", last_csearch_until(), NULL); + tv_dict_add_str(dict, S_LEN("char"), last_csearch()); + tv_dict_add_nr(dict, S_LEN("forward"), last_csearch_forward()); + tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } /* @@ -9881,17 +9856,18 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_list(dict, buf, (size_t)len, l); } } else { - dict_add_nr_str(dict, "pattern", 0L, cur->pattern); + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cur->pattern); } - dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id)); - dict_add_nr_str(dict, "priority", (long)cur->priority, NULL); - dict_add_nr_str(dict, "id", (long)cur->id, NULL); + tv_dict_add_str(dict, S_LEN("group"), + (const char *)syn_id2name(cur->hlg_id)); + tv_dict_add_nr(dict, S_LEN("priority"), (varnumber_T)cur->priority); + tv_dict_add_nr(dict, S_LEN("id"), (varnumber_T)cur->id); if (cur->conceal_char) { - char_u buf[MB_MAXBYTES + 1]; + char buf[MB_MAXBYTES + 1]; - buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL; - dict_add_nr_str(dict, "conceal", 0L, (char_u *)&buf); + buf[(*mb_char2bytes)((int)cur->conceal_char, (char_u *)buf)] = NUL; + tv_dict_add_str(dict, S_LEN("conceal"), buf); } tv_list_append_dict(rettv->vval.v_list, dict); @@ -10041,7 +10017,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tp_idx, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); list_T *const l = tv_list_alloc(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { @@ -10139,17 +10115,16 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { dict_T *const dict = tv_dict_alloc(); - dict_add_nr_str(dict, "tabnr", tpnr, NULL); - dict_add_nr_str(dict, "winnr", winnr, NULL); - dict_add_nr_str(dict, "winid", wp->handle, NULL); - dict_add_nr_str(dict, "height", wp->w_height, NULL); - dict_add_nr_str(dict, "width", wp->w_width, NULL); - dict_add_nr_str(dict, "bufnr", wp->w_buffer->b_fnum, NULL); + tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr); + tv_dict_add_nr(dict, S_LEN("winnr"), winnr); + tv_dict_add_nr(dict, S_LEN("winid"), wp->handle); + tv_dict_add_nr(dict, S_LEN("height"), wp->w_height); + tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); + tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); - dict_add_nr_str(dict, "quickfix", bt_quickfix(wp->w_buffer), NULL); - dict_add_nr_str(dict, "loclist", - (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL), - NULL); + tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); + tv_dict_add_nr(dict, S_LEN("loclist"), + (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)); // Add a reference to window variables tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars); @@ -12074,15 +12049,15 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) char *const mapmode = map_mode_to_chars(mp->m_mode); dict_T *dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lhs", 0L, lhs); - dict_add_nr_str(dict, "rhs", 0L, mp->m_orig_str); - dict_add_nr_str(dict, "noremap", mp->m_noremap ? 1L : 0L, NULL); - dict_add_nr_str(dict, "expr", mp->m_expr ? 1L : 0L, NULL); - dict_add_nr_str(dict, "silent", mp->m_silent ? 1L : 0L, NULL); - dict_add_nr_str(dict, "sid", (long)mp->m_script_ID, NULL); - dict_add_nr_str(dict, "buffer", (long)buffer_local, NULL); - dict_add_nr_str(dict, "nowait", mp->m_nowait ? 1L : 0L, NULL); - dict_add_nr_str(dict, "mode", 0L, (char_u *)mapmode); + tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); + tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); + tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_local); + tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); + tv_dict_add_str(dict, S_LEN("mode"), mapmode); xfree(lhs); xfree(mapmode); @@ -16984,13 +16959,13 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *dict = rettv->vval.v_dict; list_T *list; - dict_add_nr_str(dict, "synced", (long)curbuf->b_u_synced, NULL); - dict_add_nr_str(dict, "seq_last", curbuf->b_u_seq_last, NULL); - dict_add_nr_str(dict, "save_last", - (long)curbuf->b_u_save_nr_last, NULL); - dict_add_nr_str(dict, "seq_cur", curbuf->b_u_seq_cur, NULL); - dict_add_nr_str(dict, "time_cur", (long)curbuf->b_u_time_cur, NULL); - dict_add_nr_str(dict, "save_cur", (long)curbuf->b_u_save_nr_cur, NULL); + tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); + tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); + tv_dict_add_nr(dict, S_LEN("save_last"), + (varnumber_T)curbuf->b_u_save_nr_last); + tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)curbuf->b_u_seq_cur); + tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); + tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); list = tv_list_alloc(); u_eval_tree(curbuf->b_u_oldhead, list); @@ -17223,16 +17198,16 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict = rettv->vval.v_dict; - dict_add_nr_str(dict, "lnum", (long)curwin->w_cursor.lnum, NULL); - dict_add_nr_str(dict, "col", (long)curwin->w_cursor.col, NULL); - dict_add_nr_str(dict, "coladd", (long)curwin->w_cursor.coladd, NULL); + tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum); + tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col); + tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd); update_curswant(); - dict_add_nr_str(dict, "curswant", (long)curwin->w_curswant, NULL); + tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant); - dict_add_nr_str(dict, "topline", (long)curwin->w_topline, NULL); - dict_add_nr_str(dict, "topfill", (long)curwin->w_topfill, NULL); - dict_add_nr_str(dict, "leftcol", (long)curwin->w_leftcol, NULL); - dict_add_nr_str(dict, "skipcol", (long)curwin->w_skipcol, NULL); + tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline); + tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill); + tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol); + tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } /// Writes list of strings to file diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index f9885926c7..62460bcc3a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1180,7 +1180,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, const size_t key_len, list_T *const list) FUNC_ATTR_NONNULL_ALL { - dictitem_T *item = tv_dict_item_alloc_len(key, key_len); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; @@ -1218,6 +1218,54 @@ int tv_dict_add_dict(dict_T *const d, const char *const key, return OK; } +/// Add a number entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] nr Number to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_nr(dict_T *const d, const char *const key, + const size_t key_len, const varnumber_T nr) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + +/// Add a string entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str(dict_T *const d, + const char *const key, const size_t key_len, + const char *const val) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = (char_u *)xstrdup(val); + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + //{{{2 Operations on the whole dict /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6183397d12..6929e37e43 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "nvim/hashtab.h" #include "nvim/garray.h" diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index d7120a7309..f7932bc296 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1549,8 +1549,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) assert(false); } - dict_add_nr_str(dict, "scope", 0L, (char_u *)buf); - dict_add_nr_str(dict, "cwd", 0L, (char_u *)new_dir); + tv_dict_add_str(dict, S_LEN("scope"), buf); + tv_dict_add_str(dict, S_LEN("cwd"), new_dir); tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 28ac5aad4e..fd1e2d20f2 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -14,6 +14,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/assert.h" #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" @@ -2567,17 +2568,17 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) // the register type char buf[NUMBUFLEN+2]; format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf)); - dict_add_nr_str(dict, "regtype", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); // name of requested register or the empty string for an unnamed operation. buf[0] = (char)oap->regname; buf[1] = NUL; - dict_add_nr_str(dict, "regname", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("regname"), buf); // kind of operation (yank/delete/change) buf[0] = (char)get_op_char(oap->op_type); buf[1] = NUL; - dict_add_nr_str(dict, "operator", 0, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("operator"), buf); tv_dict_set_keys_readonly(dict); textlock++; @@ -5484,17 +5485,19 @@ void cursor_pos_info(dict_T *dict) if (dict != NULL) { // Don't shorten this message, the user asked for it. - dict_add_nr_str(dict, "words", word_count, NULL); - dict_add_nr_str(dict, "chars", char_count, NULL); - dict_add_nr_str(dict, "bytes", byte_count + bom_count, NULL); - - dict_add_nr_str(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", - byte_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", - char_count_cursor, NULL); - dict_add_nr_str(dict, l_VIsual_active ? "visual_words" : "cursor_words", - word_count_cursor, NULL); - } + tv_dict_add_nr(dict, S_LEN("words"), (varnumber_T)word_count); + tv_dict_add_nr(dict, S_LEN("chars"), (varnumber_T)char_count); + tv_dict_add_nr(dict, S_LEN("bytes"), (varnumber_T)(byte_count + bom_count)); + + STATIC_ASSERT(sizeof("visual") == sizeof("cursor"), + "key_len argument in tv_dict_add_nr is wrong"); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_bytes" : "cursor_bytes", + sizeof("visual_bytes") - 1, (varnumber_T)byte_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_chars" : "cursor_chars", + sizeof("visual_chars") - 1, (varnumber_T)char_count_cursor); + tv_dict_add_nr(dict, l_VIsual_active ? "visual_words" : "cursor_words", + sizeof("visual_words") - 1, (varnumber_T)word_count_cursor); + } } /// Check if the default register (used in an unnamed paste) should be a diff --git a/src/nvim/option.c b/src/nvim/option.c index 4c8606a999..1881b329ec 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -6971,9 +6971,10 @@ dict_T *get_winbuf_options(const int bufopt) if (varp != NULL) { if (opt->flags & P_STRING) { - dict_add_nr_str(d, opt->fullname, 0L, *(char_u **)varp); + tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), + *(const char **)varp); } else { - dict_add_nr_str(d, opt->fullname, *varp, NULL); + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp); } } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d23059ff22..06ac2821b0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3972,7 +3972,6 @@ static void unload_dummy_buffer(buf_T *buf, char_u *dirname_start) int get_errorlist(win_T *wp, int qf_idx, list_T *list) { qf_info_T *qi = &ql_info; - dict_T *dict; char_u buf[2]; qfline_T *qfp; int i; @@ -4000,23 +3999,34 @@ int get_errorlist(win_T *wp, int qf_idx, list_T *list) if (bufnum != 0 && (buflist_findnr(bufnum) == NULL)) bufnum = 0; - dict = tv_dict_alloc(); + dict_T *const dict = tv_dict_alloc(); tv_list_append_dict(list, dict); buf[0] = qfp->qf_type; buf[1] = NUL; - if ( dict_add_nr_str(dict, "bufnr", (long)bufnum, NULL) == FAIL - || dict_add_nr_str(dict, "lnum", (long)qfp->qf_lnum, NULL) == FAIL - || dict_add_nr_str(dict, "col", (long)qfp->qf_col, NULL) == FAIL - || dict_add_nr_str(dict, "vcol", (long)qfp->qf_viscol, NULL) == FAIL - || dict_add_nr_str(dict, "nr", (long)qfp->qf_nr, NULL) == FAIL - || dict_add_nr_str(dict, "pattern", 0L, - qfp->qf_pattern == NULL ? (char_u *)"" : qfp->qf_pattern) == FAIL - || dict_add_nr_str(dict, "text", 0L, - qfp->qf_text == NULL ? (char_u *)"" : qfp->qf_text) == FAIL - || dict_add_nr_str(dict, "type", 0L, buf) == FAIL - || dict_add_nr_str(dict, "valid", (long)qfp->qf_valid, NULL) == FAIL) - return FAIL; + if (tv_dict_add_nr(dict, S_LEN("bufnr"), (varnumber_T)bufnum) == FAIL + || (tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)qfp->qf_lnum) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)qfp->qf_col) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("vcol"), (varnumber_T)qfp->qf_viscol) + == FAIL) + || (tv_dict_add_nr(dict, S_LEN("nr"), (varnumber_T)qfp->qf_nr) == FAIL) + || tv_dict_add_str(dict, S_LEN("pattern"), + (qfp->qf_pattern == NULL + ? "" + : (const char *)qfp->qf_pattern)) == FAIL + || tv_dict_add_str(dict, S_LEN("text"), + (qfp->qf_text == NULL + ? "" + : (const char *)qfp->qf_text)) == FAIL + || tv_dict_add_str(dict, S_LEN("type"), (const char *)buf) == FAIL + || (tv_dict_add_nr(dict, S_LEN("valid"), (varnumber_T)qfp->qf_valid) + == FAIL)) { + // tv_dict_add* fail only if key already exist, but this is a newly + // allocated dictionary which is thus guaranteed to have no existing keys. + assert(false); + } qfp = qfp->qf_next; if (qfp == NULL) { @@ -4085,15 +4095,15 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) if (t == NULL) { t = (char_u *)""; } - status = dict_add_nr_str(retdict, "title", 0L, t); + status = tv_dict_add_str(retdict, S_LEN("title"), (const char *)t); } if ((status == OK) && (flags & QF_GETLIST_NR)) { - status = dict_add_nr_str(retdict, "nr", qf_idx + 1, NULL); + status = tv_dict_add_nr(retdict, S_LEN("nr"), qf_idx + 1); } if ((status == OK) && (flags & QF_GETLIST_WINID)) { win_T *win = qf_find_win(qi); if (win != NULL) { - status = dict_add_nr_str(retdict, "winid", win->handle, NULL); + status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } diff --git a/src/nvim/search.c b/src/nvim/search.c index fb9e820928..c5c92b41c5 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -356,9 +356,10 @@ int pat_has_uppercase(char_u *pat) return FALSE; } -char_u *last_csearch(void) +const char *last_csearch(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return lastc_bytes; + return (const char *)lastc_bytes; } int last_csearch_forward(void) diff --git a/src/nvim/tag.c b/src/nvim/tag.c index af19a1bfc4..b812dd2ffd 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -776,11 +776,12 @@ do_tag ( dict = tv_dict_alloc(); tv_list_append_dict(list, dict); - dict_add_nr_str(dict, "text", 0L, tag_name); - dict_add_nr_str(dict, "filename", 0L, fname); - dict_add_nr_str(dict, "lnum", lnum, NULL); - if (lnum == 0) - dict_add_nr_str(dict, "pattern", 0L, cmd); + tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); + tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); + tv_dict_add_nr(dict, S_LEN("lnum"), lnum); + if (lnum == 0) { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); + } } vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); @@ -2203,7 +2204,7 @@ parse_tag_line ( * Return TRUE if it is a static tag and adjust *tagname to the real tag. * Return FALSE if it is not a static tag. */ -static int test_for_static(tagptrs_T *tagp) +static bool test_for_static(tagptrs_T *tagp) { char_u *p; @@ -2790,7 +2791,8 @@ add_tag_field ( STRLCPY(buf, start, len + 1); } buf[len] = NUL; - retval = dict_add_nr_str(dict, field_name, 0L, buf); + retval = tv_dict_add_str(dict, field_name, STRLEN(field_name), + (const char *)buf); xfree(buf); return retval; } @@ -2806,7 +2808,7 @@ int get_tags(list_T *list, char_u *pat) char_u *full_fname; dict_T *dict; tagptrs_T tp; - long is_static; + bool is_static; ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, (int)MAXCOL, NULL); @@ -2829,14 +2831,13 @@ int get_tags(list_T *list, char_u *pat) full_fname = tag_full_fname(&tp); if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL - || add_tag_field(dict, "filename", full_fname, - NULL) == FAIL - || add_tag_field(dict, "cmd", tp.command, - tp.command_end) == FAIL + || add_tag_field(dict, "filename", full_fname, NULL) == FAIL + || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL || add_tag_field(dict, "kind", tp.tagkind, - tp.tagkind ? tp.tagkind_end : NULL) == FAIL - || dict_add_nr_str(dict, "static", is_static, NULL) == FAIL) + tp.tagkind ? tp.tagkind_end : NULL) == FAIL + || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) { ret = FAIL; + } xfree(full_fname); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 8c75d28f2a..ba687bf6da 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2943,19 +2943,22 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) while (uhp != NULL) { dict = tv_dict_alloc(); - dict_add_nr_str(dict, "seq", uhp->uh_seq, NULL); - dict_add_nr_str(dict, "time", (long)uhp->uh_time, NULL); - if (uhp == curbuf->b_u_newhead) - dict_add_nr_str(dict, "newhead", 1, NULL); - if (uhp == curbuf->b_u_curhead) - dict_add_nr_str(dict, "curhead", 1, NULL); - if (uhp->uh_save_nr > 0) - dict_add_nr_str(dict, "save", uhp->uh_save_nr, NULL); + tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); + tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); + if (uhp == curbuf->b_u_newhead) { + tv_dict_add_nr(dict, S_LEN("newhead"), 1); + } + if (uhp == curbuf->b_u_curhead) { + tv_dict_add_nr(dict, S_LEN("curhead"), 1); + } + if (uhp->uh_save_nr > 0) { + tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); + } if (uhp->uh_alt_next.ptr != NULL) { list_T *alt_list = tv_list_alloc(); - /* Recursive call to add alternate undo tree. */ + // Recursive call to add alternate undo tree. u_eval_tree(uhp->uh_alt_next.ptr, alt_list); tv_dict_add_list(dict, S_LEN("alt"), alt_list); } -- cgit From 5cdf7177ec71e4b9b3295ead93bedf7ff226a2b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 00:20:01 +0300 Subject: eval: Move get_float_arg to typval.h Assuming `inline` is there for a reason, so it is kept and function was moved to typval.h and not to typval.c which does not have problems with #including message.h. --- src/nvim/eval.c | 60 +++++++++++++++++++------------------------------- src/nvim/eval/typval.h | 27 +++++++++++++++++++++++ src/nvim/gettext.h | 21 ++++++++++++++++++ src/nvim/vim.h | 17 +------------- 4 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/nvim/gettext.h diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a64eb3c959..1d060eff33 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6473,24 +6473,6 @@ static int non_zero_arg(typval_T *argvars) */ -/* - * Get the float value of "argvars[0]" into "f". - * Returns FAIL when the argument is not a Number or Float. - */ -static inline int get_float_arg(typval_T *argvars, float_T *f) -{ - if (argvars[0].v_type == VAR_FLOAT) { - *f = argvars[0].vval.v_float; - return OK; - } - if (argvars[0].v_type == VAR_NUMBER) { - *f = (float_T)argvars[0].vval.v_number; - return OK; - } - EMSG(_("E808: Number or Float required")); - return FAIL; -} - // Apply a floating point C function on a typval with one float_T. // // Some versions of glibc on i386 have an optimization that makes it harder to @@ -6502,7 +6484,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) { + if (tv_get_float(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6957,14 +6939,15 @@ static void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -8557,13 +8540,14 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (get_float_arg(argvars, &f) == OK) { - if (f < -0x7fffffff) - rettv->vval.v_number = -0x7fffffff; - else if (f > 0x7fffffff) - rettv->vval.v_number = 0x7fffffff; - else + if (tv_get_float(argvars, &f)) { + if (f < VARNUMBER_MIN) { + rettv->vval.v_number = VARNUMBER_MIN; + } else if (f > VARNUMBER_MAX) { + rettv->vval.v_number = VARNUMBER_MAX; + } else { rettv->vval.v_number = (varnumber_T)f; + } } } @@ -8572,14 +8556,15 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* @@ -12790,14 +12775,15 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - float_T fx, fy; + float_T fx; + float_T fy; rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &fx) == OK - && get_float_arg(&argvars[1], &fy) == OK) + if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); - else + } else { rettv->vval.v_float = 0.0; + } } /* diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 6929e37e43..0eef2ddaac 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -13,6 +13,7 @@ #include "nvim/lib/queue.h" #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T +#include "nvim/gettext.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -370,6 +371,32 @@ extern bool tv_in_free_unref_items; } \ }) +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) + REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; + +// FIXME circular dependency, cannot import message.h. +bool emsgf(const char *const fmt, ...); + +/// Get the float value +/// +/// @param[in] tv VimL object to get value from. +/// @param[out] ret_f Location where resulting float is stored. +/// +/// @return true in case of success, false if tv is not a number or float. +static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +{ + if (tv->v_type == VAR_FLOAT) { + *ret_f = tv->vval.v_float; + return true; + } + if (tv->v_type == VAR_NUMBER) { + *ret_f = (float_T)tv->vval.v_number; + return true; + } + emsgf(_("E808: Number or Float required")); + return false; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h new file mode 100644 index 0000000000..aa0e97233e --- /dev/null +++ b/src/nvim/gettext.h @@ -0,0 +1,21 @@ +#ifndef NVIM_GETTEXT_H +#define NVIM_GETTEXT_H + +#ifdef HAVE_WORKING_LIBINTL +# include +# define _(x) gettext((char *)(x)) +// XXX do we actually need this? +# ifdef gettext_noop +# define N_(x) gettext_noop(x) +# else +# define N_(x) x +# endif +#else +# define _(x) ((char *)(x)) +# define N_(x) x +# define bindtextdomain(x, y) // empty +# define bind_textdomain_codeset(x, y) // empty +# define textdomain(x) // empty +#endif + +#endif // NVIM_GETTEXT_H diff --git a/src/nvim/vim.h b/src/nvim/vim.h index ae654251f9..e16ee00309 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -51,22 +51,7 @@ Error: configure did not run properly.Check auto/config.log. /* ================ end of the header file puzzle =============== */ -#ifdef HAVE_WORKING_LIBINTL -# include -# define _(x) gettext((char *)(x)) -// XXX do we actually need this? -# ifdef gettext_noop -# define N_(x) gettext_noop(x) -# else -# define N_(x) x -# endif -#else -# define _(x) ((char *)(x)) -# define N_(x) x -# define bindtextdomain(x, y) /* empty */ -# define bind_textdomain_codeset(x, y) /* empty */ -# define textdomain(x) /* empty */ -#endif +#include "nvim/gettext.h" /* special attribute addition: Put message in history */ #define MSG_HIST 0x1000 -- cgit From 28dafe3ff0b0dc082fb62b2251fd64a167ce7188 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:16:47 +0300 Subject: eval,*: Move get_tv_string to typval.c Function was renamed and changed to return `const char *`. --- src/nvim/api/private/helpers.c | 2 +- src/nvim/buffer.c | 6 +- src/nvim/edit.c | 14 +- src/nvim/eval.c | 1432 ++++++++++++++++++++-------------------- src/nvim/eval.h | 2 +- src/nvim/eval/executor.c | 6 +- src/nvim/eval/typval.c | 28 +- src/nvim/eval/typval.h | 2 + src/nvim/ex_docmd.c | 6 +- src/nvim/ex_eval.c | 20 +- src/nvim/ex_getln.c | 69 +- src/nvim/fileio.c | 36 +- src/nvim/getchar.c | 113 ++-- src/nvim/hardcopy.c | 8 +- src/nvim/message.c | 2 +- src/nvim/msgpack_rpc/channel.c | 20 +- src/nvim/msgpack_rpc/helpers.c | 4 +- src/nvim/normal.c | 21 +- src/nvim/os/fs.c | 25 +- src/nvim/os_unix.c | 4 +- src/nvim/path.c | 36 +- src/nvim/sha256.c | 14 +- src/nvim/shada.c | 2 - src/nvim/spell.c | 48 +- src/nvim/strings.c | 38 +- src/nvim/syntax.c | 92 +-- src/nvim/undo.c | 2 +- src/nvim/version.c | 12 +- 28 files changed, 1072 insertions(+), 992 deletions(-) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 6b17ba1a11..2c8a56cc1a 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -625,7 +625,7 @@ String cstr_as_string(char *str) FUNC_ATTR_PURE if (str == NULL) { return (String) STRING_INIT; } - return (String) {.data = str, .size = strlen(str)}; + return (String) { .data = str, .size = strlen(str) }; } /// Converts from type Object to a VimL value. diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0ce9ce073e..0fb9a21354 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4207,11 +4207,11 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) #ifdef WIN32 if (!buf->b_p_bin) { // If the file name is a shortcut file, use the file it links to. - char_u *rfname = (char_u *)os_resolve_shortcut(*ffname); + char *rfname = os_resolve_shortcut((const char *)(*ffname)); if (rfname != NULL) { xfree(*ffname); - *ffname = rfname; - *sfname = rfname; + *ffname = (char_u *)rfname; + *sfname = (char_u *)rfname; } } #endif diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d1cceb435a..b396251051 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3464,7 +3464,6 @@ expand_by_function ( { list_T *matchlist = NULL; dict_T *matchdict = NULL; - char_u *args[2]; char_u *funcname; pos_T pos; win_T *curwin_save; @@ -3475,9 +3474,8 @@ expand_by_function ( if (*funcname == NUL) return; - /* Call 'completefunc' to obtain the list of matches. */ - args[0] = (char_u *)"0"; - args[1] = base; + // Call 'completefunc' to obtain the list of matches. + const char_u *const args[2] = { (char_u *)"0", base }; pos = curwin->w_cursor; curwin_save = curwin; @@ -4589,7 +4587,6 @@ static int ins_complete(int c, bool enable_pum) * Call user defined function 'completefunc' with "a:findstart" * set to 1 to obtain the length of text to use for completion. */ - char_u *args[2]; int col; char_u *funcname; pos_T pos; @@ -4608,8 +4605,7 @@ static int ins_complete(int c, bool enable_pum) return FAIL; } - args[0] = (char_u *)"1"; - args[1] = NULL; + const char_u *const args[2] = { (char_u *)"1", NULL }; pos = curwin->w_cursor; curwin_save = curwin; curbuf_save = curbuf; @@ -7111,8 +7107,8 @@ static void ins_ctrl_g(void) */ static void ins_ctrl_hat(void) { - if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE)) { - /* ":lmap" mappings exists, Toggle use of ":lmap" mappings. */ + if (map_to_exists_mode("", LANGMAP, false)) { + // ":lmap" mappings exists, Toggle use of ":lmap" mappings. if (State & LANGMAP) { curbuf->b_p_iminsert = B_IMODE_NONE; State &= ~LANGMAP; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1d060eff33..7e40f5e828 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -965,31 +965,34 @@ eval_to_bool ( return retval; } -/* - * Top level evaluation function, returning a string. If "skip" is TRUE, - * only parsing to "nextcmd" is done, without reporting errors. Return - * pointer to allocated memory, or NULL for failure or when "skip" is TRUE. - */ -char_u * -eval_to_string_skip ( - char_u *arg, - char_u **nextcmd, - int skip /* only parse, don't execute */ -) +/// Top level evaluation function, returning a string +/// +/// @param[in] arg String to evaluate. +/// @param nextcmd Pointer to the start of the next Ex command. +/// @param[in] skip If true, only do parsing to nextcmd without reporting +/// errors or actually evaluating anything. +/// +/// @return [allocated] string result of evaluation or NULL in case of error or +/// when skipping. +char *eval_to_string_skip(const char *arg, const char **nextcmd, + const bool skip) + FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { typval_T tv; - char_u *retval; + char *retval; - if (skip) - ++emsg_skip; - if (eval0(arg, &tv, nextcmd, !skip) == FAIL || skip) + if (skip) { + emsg_skip++; + } + if (eval0((char_u *)arg, &tv, (char_u **)nextcmd, !skip) == FAIL || skip) { retval = NULL; - else { - retval = vim_strsave(get_tv_string(&tv)); + } else { + retval = xstrdup(tv_get_string(&tv)); tv_clear(&tv); } - if (skip) - --emsg_skip; + if (skip) { + emsg_skip--; + } return retval; } @@ -1015,13 +1018,12 @@ int skip_expr(char_u **pp) char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) { typval_T tv; - char_u *retval; + char *retval; garray_T ga; - char_u numbuf[NUMBUFLEN]; - if (eval0(arg, &tv, nextcmd, TRUE) == FAIL) + if (eval0(arg, &tv, nextcmd, true) == FAIL) { retval = NULL; - else { + } else { if (convert && tv.v_type == VAR_LIST) { ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { @@ -1031,16 +1033,18 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) } } ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; + retval = (char *)ga.ga_data; } else if (convert && tv.v_type == VAR_FLOAT) { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } else - retval = vim_strsave(get_tv_string(&tv)); + char numbuf[NUMBUFLEN]; + vim_snprintf(numbuf, NUMBUFLEN, "%g", tv.vval.v_float); + retval = xstrdup(numbuf); + } else { + retval = xstrdup(tv_get_string(&tv)); + } tv_clear(&tv); } - return retval; + return (char_u *)retval; } /* @@ -1159,14 +1163,15 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) * Return -1 if anything isn't right. * Used to get the good word and score from the eval_spell_expr() result. */ -int get_spellword(list_T *list, char_u **pp) +int get_spellword(list_T *list, const char **pp) { listitem_T *li; li = list->lv_first; - if (li == NULL) + if (li == NULL) { return -1; - *pp = get_tv_string(&li->li_tv); + } + *pp = tv_get_string(&li->li_tv); li = li->li_next; if (li == NULL) @@ -1198,9 +1203,9 @@ typval_T *eval_expr(char_u *arg, char_u **nextcmd) // // Return OK or FAIL. int call_vim_function( - char_u *func, + const char_u *func, int argc, - char_u **argv, + const char_u *const *const argv, int safe, // use the sandbox int str_arg_only, // all arguments are strings typval_T *rettv @@ -1233,7 +1238,7 @@ int call_vim_function( argvars[i].vval.v_number = n; } else { argvars[i].v_type = VAR_STRING; - argvars[i].vval.v_string = argv[i]; + argvars[i].vval.v_string = (char_u *)argv[i]; } } @@ -1268,8 +1273,8 @@ long call_func_retnr ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -1284,27 +1289,28 @@ call_func_retnr ( return retval; } -/* - * Call vimL function "func" and return the result as a string. - * Returns NULL when calling the function fails. - * Uses argv[argc] for the function arguments. - */ -void * -call_func_retstr ( - char_u *func, - int argc, - char_u **argv, - int safe /* use the sandbox */ -) +/// Call VimL function and return the result as a string +/// +/// @param[in] func Function name. +/// @param[in] argc Number of arguments. +/// @param[in] argv Array with string arguments. +/// @param[in] safe Use the sandbox. +/// +/// @return [allocated] NULL when calling function failes, allocated string +/// otherwise. +char *call_func_retstr(const char *const func, const int argc, + const char_u *const *const argv, + const bool safe) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; - char_u *retval; - - /* All arguments are passed as strings, no conversion to number. */ - if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) + // All arguments are passed as strings, no conversion to number. + if (call_vim_function((const char_u *)func, argc, argv, safe, true, &rettv) + == FAIL) { return NULL; + } - retval = vim_strsave(get_tv_string(&rettv)); + char *const retval = xstrdup(tv_get_string(&rettv)); tv_clear(&rettv); return retval; } @@ -1318,8 +1324,8 @@ void * call_func_retlist ( char_u *func, int argc, - char_u **argv, - int safe /* use the sandbox */ + const char_u *const *const argv, + int safe // use the sandbox ) { typval_T rettv; @@ -2202,7 +2208,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (lp->ll_tv->v_type == VAR_DICT) { if (len == -1) { // "[key]": get key from "var1" - key = get_tv_string_chk(&var1); // is number or string + key = (char_u *)tv_get_string(&var1); // is number or string if (key == NULL) { tv_clear(&var1); return NULL; @@ -4343,9 +4349,8 @@ eval_index ( bool empty1 = false; bool empty2 = false; long n1, n2 = 0; - long len = -1; + ptrdiff_t len = -1; int range = false; - char_u *s; char_u *key = NULL; switch (rettv->v_type) { @@ -4455,103 +4460,109 @@ eval_index ( tv_clear(&var1); } if (range) { - if (empty2) + if (empty2) { n2 = -1; - else { + } else { n2 = get_tv_number(&var2); tv_clear(&var2); } } switch (rettv->v_type) { - case VAR_NUMBER: - case VAR_STRING: - s = get_tv_string(rettv); - len = (long)STRLEN(s); - if (range) { - /* The resulting variable is a substring. If the indexes - * are out of range the result is empty. */ + case VAR_NUMBER: + case VAR_STRING: { + const char *const s = tv_get_string(rettv); + char *v; + len = (ptrdiff_t)strlen(s); + if (range) { + // The resulting variable is a substring. If the indexes + // are out of range the result is empty. + if (n1 < 0) { + n1 = len + n1; + if (n1 < 0) { + n1 = 0; + } + } + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len; + } + if (n1 >= len || n2 < 0 || n1 > n2) { + v = NULL; + } else { + v = xmemdupz(s + n1, (size_t)(n2 - n1 + 1)); + } + } else { + // The resulting variable is a string of a single + // character. If the index is too big or negative the + // result is empty. + if (n1 >= len || n1 < 0) { + v = NULL; + } else { + v = xmemdupz(s + n1, 1); + } + } + tv_clear(rettv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = (char_u *)v; + break; + } + case VAR_LIST: { + len = tv_list_len(rettv->vval.v_list); if (n1 < 0) { n1 = len + n1; - if (n1 < 0) - n1 = 0; } - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len; - if (n1 >= len || n2 < 0 || n1 > n2) - s = NULL; - else - s = vim_strnsave(s + n1, (int)(n2 - n1 + 1)); - } else { - /* The resulting variable is a string of a single - * character. If the index is too big or negative the - * result is empty. */ - if (n1 >= len || n1 < 0) - s = NULL; - else - s = vim_strnsave(s + n1, 1); - } - tv_clear(rettv); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = s; - break; - - case VAR_LIST: - len = tv_list_len(rettv->vval.v_list); - if (n1 < 0) { - n1 = len + n1; - } - if (!empty1 && (n1 < 0 || n1 >= len)) { - /* For a range we allow invalid values and return an empty - * list. A list index out of range is an error. */ - if (!range) { - if (verbose) - EMSGN(_(e_listidx), n1); - return FAIL; + if (!empty1 && (n1 < 0 || n1 >= len)) { + // For a range we allow invalid values and return an empty + // list. A list index out of range is an error. + if (!range) { + if (verbose) { + EMSGN(_(e_listidx), n1); + } + return FAIL; + } + n1 = len; } - n1 = len; - } - if (range) { - list_T *l; - listitem_T *item; - - if (n2 < 0) - n2 = len + n2; - else if (n2 >= len) - n2 = len - 1; - if (!empty2 && (n2 < 0 || n2 + 1 < n1)) - n2 = -1; - l = tv_list_alloc(); - item = tv_list_find(rettv->vval.v_list, n1); - while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + if (range) { + list_T *l; + listitem_T *item; + + if (n2 < 0) { + n2 = len + n2; + } else if (n2 >= len) { + n2 = len - 1; + } + if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { + n2 = -1; + } + l = tv_list_alloc(); + item = tv_list_find(rettv->vval.v_list, n1); + while (n1++ <= n2) { + tv_list_append_tv(l, &item->li_tv); + item = item->li_next; + } + tv_clear(rettv); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + l->lv_refcount++; + } else { + copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_clear(rettv); + *rettv = var1; } - tv_clear(rettv); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = l; - ++l->lv_refcount; - } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); - tv_clear(rettv); - *rettv = var1; + break; } - break; - - case VAR_DICT: - if (range) { - if (verbose) { - emsgf(_(e_dictrange)); - } - if (len == -1) { - tv_clear(&var1); + case VAR_DICT: { + if (range) { + if (verbose) { + emsgf(_(e_dictrange)); + } + if (len == -1) { + tv_clear(&var1); + } + return FAIL; } - return FAIL; - } - { - dictitem_T *item; if (len == -1) { key = get_tv_string_chk(&var1); @@ -4561,7 +4572,8 @@ eval_index ( } } - item = tv_dict_find(rettv->vval.v_dict, (const char *)key, len); + dictitem_T *const item = tv_dict_find(rettv->vval.v_dict, + (const char *)key, len); if (item == NULL && verbose) { emsgf(_(e_dictkey), key); @@ -4576,14 +4588,15 @@ eval_index ( copy_tv(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; + break; + } + case VAR_SPECIAL: + case VAR_FUNC: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_UNKNOWN: { + break; // Not evaluating, skipping over subscript } - break; - case VAR_SPECIAL: - case VAR_FUNC: - case VAR_PARTIAL: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; // Not evaluating, skipping over subscript } } @@ -6251,7 +6264,7 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) /// Also returns OK when an error was encountered while executing the function. int call_func( - char_u *funcname, // name of the function + const char_u *funcname, // name of the function int len, // length of "name" typval_T *rettv, // return value goes here int argcount_in, // number of "argvars" @@ -7249,24 +7262,24 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /// "call(func, arglist [, dict])" function static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *func; - partial_T *partial = NULL; - dict_T *selfdict = NULL; - if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[1].vval.v_list == NULL) + if (argvars[1].vval.v_list == NULL) { return; + } + char_u *func; + partial_T *partial = NULL; + dict_T *selfdict = NULL; if (argvars[0].v_type == VAR_FUNC) { func = argvars[0].vval.v_string; } else if (argvars[0].v_type == VAR_PARTIAL) { partial = argvars[0].vval.v_partial; func = partial_name(partial); } else { - func = get_tv_string(&argvars[0]); + func = (char_u *)tv_get_string(&argvars[0]); } if (*func == NUL) { return; // type error or empty name @@ -7299,15 +7312,15 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { utf8 = get_tv_number_chk(&argvars[1], NULL); + } - if (utf8) - rettv->vval.v_number = (*utf_ptr2char)(get_tv_string(&argvars[0])); - else - rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0])); - } else - rettv->vval.v_number = get_tv_string(&argvars[0])[0]; + rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( + (const char_u *)tv_get_string(&argvars[0])); + } else { + rettv->vval.v_number = (uint8_t)(tv_get_string(&argvars[0])[0]); + } } /* @@ -7552,16 +7565,17 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; + char_u *dbpath = NULL; + char_u *prepend = NULL; char_u buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = get_tv_string(&argvars[1]); - if (argvars[2].v_type != VAR_UNKNOWN) + dbpath = (char_u *)tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) { prepend = get_tv_string_buf(&argvars[2], buf); + } } rettv->vval.v_number = cs_connection(num, dbpath, prepend); @@ -7648,7 +7662,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u nbuf[NUMBUFLEN]; - char_u *name; char_u *flags; rettv->vval.v_number = -1; @@ -7656,7 +7669,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - name = get_tv_string(&argvars[0]); + const char *const name = tv_get_string(&argvars[0]); if (name == NULL || *name == NUL) { EMSG(_(e_invarg)); return; @@ -7670,10 +7683,10 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (*flags == NUL) { // delete a file - rettv->vval.v_number = os_remove((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "d") == 0) { // delete an empty directory - rettv->vval.v_number = os_rmdir((char *)name) == 0 ? 0 : -1; + rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; } else if (STRCMP(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); @@ -7894,7 +7907,8 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - rettv->vval.v_string = vim_strsave_escaped(get_tv_string(&argvars[0]), + rettv->vval.v_string = vim_strsave_escaped( + (const char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -7936,11 +7950,13 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name = get_tv_string(&argvars[0]); + const char *name = tv_get_string(&argvars[0]); // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(name, NULL, true) - || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); + rettv->vval.v_number = ( + os_can_exe((const char_u *)name, NULL, true) + || (gettail_dir(name) != name + && os_can_exe((const char_u *)name, NULL, false))); } static char_u * get_list_line(int c, void *cookie, int indent) @@ -7993,11 +8009,11 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) capture_ga = &capture_local; if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd((char *)get_tv_string(&argvars[0])); + do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { - list_T *list = argvars[0].vval.v_list; + list_T *const list = argvars[0].vval.v_list; list->lv_refcount++; - listitem_T *item = list->lv_first; + listitem_T *const item = list->lv_first; do_cmdline(NULL, get_list_line, (void *)&item, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); list->lv_refcount--; @@ -8017,10 +8033,10 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "exepath()" function static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *arg = get_tv_string(&argvars[0]); + const char *arg = tv_get_string(&argvars[0]); char_u *path = NULL; - (void)os_can_exe(arg, &path, true); + (void)os_can_exe((const char_u *)arg, &path, true); rettv->v_type = VAR_STRING; rettv->vval.v_string = path; @@ -8034,21 +8050,21 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) int n = false; int len = 0; - char *p = (char *)get_tv_string(&argvars[0]); + const char *p = tv_get_string(&argvars[0]); if (*p == '$') { // Environment variable. // First try "normal" environment variables (fast). if (os_getenv(p + 1) != NULL) { n = true; } else { // Try expanding things like $VIM and ${HOME}. - p = (char *)expand_env_save((char_u *)p); - if (p != NULL && *p != '$') { + char_u *const exp = expand_env_save((char_u *)p); + if (exp != NULL && *exp != '$') { n = true; } - xfree(p); + xfree(exp); } } else if (*p == '&' || *p == '+') { // Option. - n = (get_option_tv((const char **)&p, NULL, true) == OK); + n = (get_option_tv(&p, NULL, true) == OK); if (*skipwhite((const char_u *)p) != NUL) { n = false; // Trailing garbage. } @@ -8076,7 +8092,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = (get_var_tv(name, len, &tv, NULL, false, true) == OK); if (n) { // Handle d.key, l[idx], f(expr). - n = (handle_subscript((const char **)&p, &tv, true, false) == OK); + n = (handle_subscript(&p, &tv, true, false) == OK); if (n) { tv_clear(&tv); } @@ -8096,7 +8112,6 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; size_t len; char_u *errormsg; int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND; @@ -8113,11 +8128,11 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_list = NULL; } - s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); if (*s == '%' || *s == '#' || *s == '<') { - ++emsg_off; - result = eval_vars(s, s, &len, NULL, &errormsg, NULL); - --emsg_off; + emsg_off++; + result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); + emsg_off--; if (rettv->v_type == VAR_LIST) { tv_list_alloc_ret(rettv); if (result != NULL) { @@ -8134,21 +8149,24 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; - if (p_wic) + if (p_wic) { options += WILD_ICASE; + } if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, s, NULL, options, WILD_ALL); + rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, + WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, s, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); } ExpandCleanup(&xpc); } - } else + } else { rettv->vval.v_string = NULL; + } } } @@ -8235,7 +8253,6 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *keys, *flags = NULL; char_u nbuf[NUMBUFLEN]; /* This is not allowed in the sandbox. If the commands would still be @@ -8244,10 +8261,11 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_secure()) return; - keys = get_tv_string(&argvars[0]); + const char *const keys = tv_get_string(&argvars[0]); + const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -8257,9 +8275,9 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "filereadable()" function static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); rettv->vval.v_number = - (*p && !os_isdir(p) && os_file_is_readable((char*)p)); + (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p)); } /* @@ -8268,14 +8286,13 @@ static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *filename = (char *)get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); rettv->vval.v_number = os_file_is_writable(filename); } static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fname; char_u *fresult = NULL; char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; char_u *p; @@ -8287,7 +8304,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) rettv->vval.v_string = NULL; rettv->v_type = VAR_STRING; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { p = get_tv_string_buf_chk(&argvars[1], pathbuf); @@ -8311,8 +8328,8 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) do { if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) xfree(fresult); - fresult = find_file_in_path_option(first ? fname : NULL, - first ? STRLEN(fname) : 0, + fresult = find_file_in_path_option(first ? (char_u *)fname : NULL, + first ? strlen(fname) : 0, 0, first, path, find_what, curbuf->b_ffname, (find_what == FINDFILE_DIR @@ -8572,8 +8589,8 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_string = vim_strsave_fnameescape( - get_tv_string(&argvars[0]), FALSE); + rettv->vval.v_string = (char_u *)vim_strsave_fnameescape( + tv_get_string(&argvars[0]), false); rettv->v_type = VAR_STRING; } @@ -8765,7 +8782,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, s = partial_name(arg_pt); } else { // function('MyFunc', [arg], dict) - s = get_tv_string(&argvars[0]); + s = (char_u *)tv_get_string(&argvars[0]); use_string = true; } @@ -8780,7 +8797,9 @@ static void common_function(typval_T *argvars, typval_T *rettv, } if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) || (is_funcref && trans_name == NULL)) { - EMSG2(_(e_invarg2), use_string ? get_tv_string(&argvars[0]) : s); + emsgf(_(e_invarg2), (use_string + ? tv_get_string(&argvars[0]) + : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL && (is_funcref ? find_func(trans_name) == NULL @@ -8958,7 +8977,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) != NULL) { - di = tv_dict_find(d, (const char *)get_tv_string(&argvars[1]), -1); + di = tv_dict_find(d, tv_get_string(&argvars[1]), -1); if (di != NULL) { tv = &di->di_tv; } @@ -8977,27 +8996,26 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (pt != NULL) { - char_u *what = get_tv_string(&argvars[1]); - char_u *n; + const char *const what = tv_get_string(&argvars[1]); - if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) { + if (strcmp(what, "func") == 0 || strcmp(what, "name") == 0) { rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING); - n = partial_name(pt); + const char *const n = (const char *)partial_name(pt); if (n == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = vim_strsave(n); + rettv->vval.v_string = (char_u *)xstrdup(n); if (rettv->v_type == VAR_FUNC) { func_ref(rettv->vval.v_string); } } - } else if (STRCMP(what, "dict") == 0) { + } else if (strcmp(what, "dict") == 0) { rettv->v_type = VAR_DICT; rettv->vval.v_dict = pt->pt_dict; if (pt->pt_dict != NULL) { (pt->pt_dict->dv_refcount)++; } - } else if (STRCMP(what, "args") == 0) { + } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; if (tv_list_alloc_ret(rettv) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { @@ -9448,9 +9466,10 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } ExpandInit(&xpc); - xpc.xp_pattern = get_tv_string(&argvars[0]); + xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type(get_tv_string(&argvars[1])); + xpc.xp_context = cmdcomplete_str_to_type( + (char_u *)tv_get_string(&argvars[1])); if (xpc.xp_context == EXPAND_NOTHING) { EMSG2(_(e_invarg2), argvars[1].vval.v_string); return; @@ -9623,20 +9642,21 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *perm = NULL; + char *perm = NULL; char_u flags[] = "rwx"; - char_u *filename = get_tv_string(&argvars[0]); + const char *filename = tv_get_string(&argvars[0]); int32_t file_perm = os_getperm(filename); if (file_perm >= 0) { - perm = vim_strsave((char_u *)"---------"); + perm = xstrdup("---------"); for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) + if (file_perm & (1 << (8 - i))) { perm[i] = flags[i % 3]; + } } } rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; + rettv->vval.v_string = (char_u *)perm; } /* @@ -9644,16 +9664,16 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_NUMBER; FileInfo file_info; if (os_fileinfo(fname, &file_info)) { uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir((char_u *)fname)) + if (os_isdir((const char_u *)fname)) { rettv->vval.v_number = 0; - else { + } else { rettv->vval.v_number = (varnumber_T)filesize; /* non-perfect check for overflow */ @@ -9671,7 +9691,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char *fname = (char *)get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); FileInfo file_info; if (os_fileinfo(fname, &file_info)) { @@ -9686,15 +9706,14 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; char_u *type = NULL; char *t; - fname = get_tv_string(&argvars[0]); + const char *fname = tv_get_string(&argvars[0]); rettv->v_type = VAR_STRING; FileInfo file_info; - if (os_fileinfo_link((char *)fname, &file_info)) { + if (os_fileinfo_link(fname, &file_info)) { uint64_t mode = file_info.stat.st_mode; #ifdef S_ISREG if (S_ISREG(mode)) @@ -9746,10 +9765,11 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) default: t = "other"; } # else - if (os_isdir(fname)) + if (os_isdir((const char_u *)fname)) { t = "dir"; - else + } else { t = "file"; + } # endif #endif type = vim_strsave((char_u *)t); @@ -10345,11 +10365,12 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (p_wic) options += WILD_ICASE; if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, - options, WILD_ALL); + rettv->vval.v_string = ExpandOne( + &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { tv_list_alloc_ret(rettv); - ExpandOne(&xpc, get_tv_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, + WILD_ALL_KEEP); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10393,7 +10414,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath(get_tv_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10425,7 +10446,7 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "has()" function static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - static char *(has_list[]) = { + static const char *const has_list[] = { #ifdef UNIX "unix", #endif @@ -10536,13 +10557,11 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "winaltkeys", "writebackup", "nvim", - NULL }; bool n = false; - char *name = (char *)get_tv_string(&argvars[0]); - - for (int i = 0; has_list[i] != NULL; i++) { + const char *const name = tv_get_string(&argvars[0]); + for (size_t i = 0; i < ARRAY_SIZE(has_list); i++) { if (STRICMP(name, has_list[i]) == 0) { n = true; break; @@ -10608,7 +10627,7 @@ static void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; rettv->vval.v_number = tv_dict_find(argvars[0].vval.v_dict, - (const char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), -1) != NULL; } @@ -10718,24 +10737,24 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; - char_u *mode; + const char *mode; + const char *const name = tv_get_string(&argvars[0]); + bool abbr = false; char_u buf[NUMBUFLEN]; - int abbr = FALSE; - - name = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) - mode = (char_u *)"nvo"; - else { - mode = get_tv_string_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[1].v_type == VAR_UNKNOWN) { + mode = "nvo"; + } else { + mode = (const char *)get_tv_string_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); + } } - if (map_to_exists(name, mode, abbr)) - rettv->vval.v_number = TRUE; - else - rettv->vval.v_number = FALSE; + if (map_to_exists(name, mode, abbr)) { + rettv->vval.v_number = true; + } else { + rettv->vval.v_number = false; + } } /* @@ -10840,7 +10859,8 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = syn_name2id(get_tv_string(&argvars[0])); + rettv->vval.v_number = syn_name2id( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10848,7 +10868,8 @@ static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = highlight_exists(get_tv_string(&argvars[0])); + rettv->vval.v_number = highlight_exists( + (const char_u *)tv_get_string(&argvars[0])); } /* @@ -10868,25 +10889,27 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *from, *to, *str; vimconv_T vimconv; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - str = get_tv_string(&argvars[0]); - from = enc_canonize(enc_skip(get_tv_string_buf(&argvars[1], buf1))); - to = enc_canonize(enc_skip(get_tv_string_buf(&argvars[2], buf2))); + const char *const str = tv_get_string(&argvars[0]); + char_u buf1[NUMBUFLEN]; + char_u *const from = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[1], buf1))); + char_u buf2[NUMBUFLEN]; + char_u *const to = enc_canonize(enc_skip( + get_tv_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); - /* If the encodings are equal, no conversion needed. */ - if (vimconv.vc_type == CONV_NONE) - rettv->vval.v_string = vim_strsave(str); - else - rettv->vval.v_string = string_convert(&vimconv, str, NULL); + // If the encodings are equal, no conversion needed. + if (vimconv.vc_type == CONV_NONE) { + rettv->vval.v_string = (char_u *)xstrdup(str); + } else { + rettv->vval.v_string = string_convert(&vimconv, (char_u *)str, NULL); + } convert_setup(&vimconv, NULL, NULL); xfree(from); @@ -11075,7 +11098,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_clr_eos(); for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts((const char *)get_tv_string(&li->li_tv)); + msg_puts(tv_get_string(&li->li_tv)); msg_putchar('\n'); } @@ -11179,7 +11202,7 @@ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = os_isdir(get_tv_string(&argvars[0])); + rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0])); } /* @@ -11191,9 +11214,11 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; rettv->vval.v_number = -1; - const char_u *const end = get_lval(get_tv_string(&argvars[0]), NULL, + const char_u *const end = get_lval((char_u *)tv_get_string(&argvars[0]), + NULL, &lv, false, false, - GLV_NO_AUTOLOAD, FNE_CHECK_START); + GLV_NO_AUTOLOAD|GLV_READ_ONLY, + FNE_CHECK_START); if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) { EMSG(_(e_trailing)); @@ -11416,8 +11441,8 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (!input) { // Either the error has been handled by save_tv_as_string(), or there is no // input to send. @@ -11462,10 +11487,10 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } -static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) +static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) { if (cmd_tv->v_type == VAR_STRING) { - char *cmd_str = (char *)get_tv_string(cmd_tv); + const char *cmd_str = tv_get_string(cmd_tv); if (cmd) { *cmd = cmd_str; } @@ -11504,7 +11529,7 @@ static char **tv_to_argv(typval_T *cmd_tv, char **cmd, bool *executable) for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { char *a = (char *)get_tv_string_chk(&arg->li_tv); if (!a) { - // Did emsg in get_tv_string; just deallocate argv. + // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } @@ -11841,24 +11866,28 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr) { switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_NUMBER: - rettv->vval.v_number = (varnumber_T)STRLEN( - get_tv_string(&argvars[0])); - break; - case VAR_LIST: - rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); - break; - case VAR_DICT: - rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); - break; - case VAR_UNKNOWN: - case VAR_SPECIAL: - case VAR_FLOAT: - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E701: Invalid type for len()")); - break; + case VAR_STRING: + case VAR_NUMBER: { + rettv->vval.v_number = (varnumber_T)strlen( + tv_get_string(&argvars[0])); + break; + } + case VAR_LIST: { + rettv->vval.v_number = tv_list_len(argvars[0].vval.v_list); + break; + } + case VAR_DICT: { + rettv->vval.v_number = tv_dict_len(argvars[0].vval.v_dict); + break; + } + case VAR_UNKNOWN: + case VAR_SPECIAL: + case VAR_FLOAT: + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E701: Invalid type for len()")); + break; + } } } @@ -11983,7 +12012,6 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *keys; char_u *which; char_u buf[NUMBUFLEN]; char_u *keys_buf = NULL; @@ -11994,13 +12022,14 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mapblock_T *mp; int buffer_local; - /* return empty string for failure */ + // Return empty string for failure. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - keys = get_tv_string(&argvars[0]); - if (*keys == NUL) + char_u *keys = (char_u *)tv_get_string(&argvars[0]); + if (*keys == NUL) { return; + } if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); @@ -12118,7 +12147,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; li = l->lv_first; } else { - expr = str = get_tv_string(&argvars[0]); + expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); } @@ -12278,7 +12307,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) int prio = 10; /* default priority */ int id = -1; bool error = false; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; rettv->vval.v_number = -1; @@ -12296,7 +12325,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12311,7 +12340,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = match_add(curwin, (const char *)grp, (const char *)pat, prio, id, - NULL, (const char *)conceal_char); + NULL, conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -12338,7 +12367,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; int prio = 10; int id = -1; - char_u *conceal_char = NULL; + const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { prio = get_tv_number_chk(&argvars[2], &error); @@ -12352,7 +12381,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) != NULL) { - conceal_char = get_tv_string(&di->di_tv); + conceal_char = tv_get_string(&di->di_tv); } } } @@ -12368,7 +12397,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, (const char *)conceal_char); + l, conceal_char); } /* @@ -12523,9 +12552,10 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) *path_tail_with_sep(dir) = NUL; if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) + if (argvars[2].v_type != VAR_UNKNOWN) { prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) { + } + if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); if (ret != 0) { @@ -12894,8 +12924,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int binary = FALSE; - char_u *fname; + bool binary = false; FILE *fd; char_u buf[(IOSIZE/256)*256]; /* rounded to avoid odd + 1 */ int io_size = sizeof(buf); @@ -12909,19 +12938,21 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { - if (STRCMP(get_tv_string(&argvars[1]), "b") == 0) - binary = TRUE; - if (argvars[2].v_type != VAR_UNKNOWN) + if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { + binary = true; + } + if (argvars[2].v_type != VAR_UNKNOWN) { maxline = get_tv_number(&argvars[2]); + } } tv_list_alloc_ret(rettv); - /* Always open the file in binary mode, library functions have a mind of - * their own about CR-LF conversion. */ - fname = get_tv_string(&argvars[0]); - if (*fname == NUL || (fd = mch_fopen((char *)fname, READBIN)) == NULL) { - EMSG2(_(e_notopen), *fname == NUL ? (char_u *)_("") : fname); + // Always open the file in binary mode, library functions have a mind of + // their own about CR-LF conversion. + const char *const fname = tv_get_string(&argvars[0]); + if (*fname == NUL || (fd = mch_fopen(fname, READBIN)) == NULL) { + EMSG2(_(e_notopen), *fname == NUL ? _("") : fname); return; } @@ -13241,11 +13272,13 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) + if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; - else - rettv->vval.v_number = vim_rename(get_tv_string(&argvars[0]), + } else { + rettv->vval.v_number = vim_rename( + (char_u *)tv_get_string(&argvars[0]), get_tv_string_buf(&argvars[1], buf)); + } } /* @@ -13253,32 +13286,37 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - - n = get_tv_number(&argvars[1]); + varnumber_T n = get_tv_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); - if (argvars[0].vval.v_list != NULL) { - while (n-- > 0) { - tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); - } + while (n-- > 0) { + tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } } else { - p = get_tv_string(&argvars[0]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + if (n <= 0) { + return; + } + + const char *const p = tv_get_string(&argvars[0]); - int slen = (int)STRLEN(p); - int len = slen * n; - if (len <= 0) + const size_t slen = strlen(p); + if (slen == 0) { return; + } + const size_t len = slen * n; + // Detect overflow. + if (len / n != slen) { + return; + } - char_u *r = xmallocz(len); - for (int i = 0; i < n; i++) - memmove(r + i * slen, p, (size_t)slen); + char *const r = xmallocz(len); + for (varnumber_T i = 0; i < n; i++) { + memmove(r + i * slen, p, slen); + } - rettv->vval.v_string = r; + rettv->vval.v_string = (char_u *)r; } } @@ -13287,59 +13325,49 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; -#ifdef HAVE_READLINK - char_u *buf = NULL; -#endif - - p = get_tv_string(&argvars[0]); + rettv->v_type = VAR_STRING; + const char *fname = tv_get_string(&argvars[0]); #ifdef WIN32 - { - char *v = os_resolve_shortcut(p); - if (v != NULL) { - rettv->vval.v_string = (char_u *)v; - } else { - rettv->vval.v_string = vim_strsave(p); - } - } + char *const v = os_resolve_shortcut(fname); + rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v); #else # ifdef HAVE_READLINK { - char_u *cpy; - int len; - char_u *remain = NULL; - char_u *q; - int is_relative_to_current = FALSE; - int has_trailing_pathsep = FALSE; + bool is_relative_to_current = false; + bool has_trailing_pathsep = false; int limit = 100; - p = vim_strsave(p); + char *p = xstrdup(fname); if (p[0] == '.' && (vim_ispathsep(p[1]) - || (p[1] == '.' && (vim_ispathsep(p[2]))))) - is_relative_to_current = TRUE; + || (p[1] == '.' && (vim_ispathsep(p[2]))))) { + is_relative_to_current = true; + } - len = STRLEN(p); - if (len > 0 && after_pathsep((char *)p, (char *)p + len)) { - has_trailing_pathsep = TRUE; - p[len - 1] = NUL; /* the trailing slash breaks readlink() */ + ptrdiff_t len = (ptrdiff_t)strlen(p); + if (len > 0 && after_pathsep(p, p + len)) { + has_trailing_pathsep = true; + p[len - 1] = NUL; // The trailing slash breaks readlink(). } - q = path_next_component(p); + char *q = (char *)path_next_component(p); + char *remain = NULL; if (*q != NUL) { - /* Separate the first path component in "p", and keep the - * remainder (beginning with the path separator). */ - remain = vim_strsave(q - 1); + // Separate the first path component in "p", and keep the + // remainder (beginning with the path separator). + remain = xstrdup(q - 1); q[-1] = NUL; } - buf = xmallocz(MAXPATHL); + char *const buf = xmallocz(MAXPATHL); + char *cpy; for (;; ) { for (;; ) { - len = readlink((char *)p, (char *)buf, MAXPATHL); - if (len <= 0) + len = readlink(p, buf, MAXPATHL); + if (len <= 0) { break; + } buf[len] = NUL; if (limit-- == 0) { @@ -13347,66 +13375,74 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) xfree(remain); EMSG(_("E655: Too many symbolic links (cycle?)")); rettv->vval.v_string = NULL; - goto fail; + xfree(buf); + return; } - /* Ensure that the result will have a trailing path separator - * if the argument has one. */ - if (remain == NULL && has_trailing_pathsep) - add_pathsep((char *)buf); + // Ensure that the result will have a trailing path separator + // if the argument has one. */ + if (remain == NULL && has_trailing_pathsep) { + add_pathsep(buf); + } - /* Separate the first path component in the link value and - * concatenate the remainders. */ - q = path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); + // Separate the first path component in the link value and + // concatenate the remainders. */ + q = (char *)path_next_component(vim_ispathsep(*buf) ? buf + 1 : buf); if (*q != NUL) { cpy = remain; - remain = remain ? - concat_str(q - 1, remain) : (char_u *) xstrdup((char *)q - 1); + remain = (remain + ? (char *)concat_str((char_u *)q - 1, (char_u *)remain) + : xstrdup(q - 1)); xfree(cpy); q[-1] = NUL; } - q = path_tail(p); + q = (char *)path_tail((char_u *)p); if (q > p && *q == NUL) { - /* Ignore trailing path separator. */ + // Ignore trailing path separator. q[-1] = NUL; - q = path_tail(p); + q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path(buf)) { - /* symlink is relative to directory of argument */ - cpy = xmalloc(STRLEN(p) + STRLEN(buf) + 1); - STRCPY(cpy, p); - STRCPY(path_tail(cpy), buf); + if (q > p && !path_is_absolute_path((const char_u *)buf)) { + // Symlink is relative to directory of argument. + const size_t p_len = strlen(p); + const size_t buf_len = strlen(buf); + cpy = xmalloc(p_len + buf_len + 1); + memcpy(cpy, p, p_len); + memcpy(path_tail((char_u *)cpy), buf, buf_len + 1); xfree(p); p = cpy; } else { xfree(p); - p = vim_strsave(buf); + p = xstrdup(buf); } } - if (remain == NULL) + if (remain == NULL) { break; + } - /* Append the first path component of "remain" to "p". */ - q = path_next_component(remain + 1); + // Append the first path component of "remain" to "p". + q = (char *)path_next_component(remain + 1); len = q - remain - (*q != NUL); - cpy = vim_strnsave(p, STRLEN(p) + len); - STRNCAT(cpy, remain, len); + const size_t p_len = strlen(p); + cpy = xmallocz(p_len + len); + memcpy(cpy, p, p_len + 1); + strncat(cpy + p_len, remain, len); xfree(p); p = cpy; - /* Shorten "remain". */ - if (*q != NUL) + // Shorten "remain". + if (*q != NUL) { STRMOVE(remain, q - 1); - else { + } else { xfree(remain); remain = NULL; } } - /* If the result is a relative path name, make it explicitly relative to - * the current directory if and only if the argument had this form. */ + // If the result is a relative path name, make it explicitly relative to + // the current directory if and only if the argument had this form. if (!vim_ispathsep(*p)) { if (is_relative_to_current && *p != NUL @@ -13416,42 +13452,40 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (p[1] == '.' && (p[2] == NUL || vim_ispathsep(p[2])))))) { - /* Prepend "./". */ - cpy = concat_str((char_u *)"./", p); + // Prepend "./". + cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p); xfree(p); p = cpy; } else if (!is_relative_to_current) { - /* Strip leading "./". */ + // Strip leading "./". q = p; - while (q[0] == '.' && vim_ispathsep(q[1])) + while (q[0] == '.' && vim_ispathsep(q[1])) { q += 2; - if (q > p) + } + if (q > p) { STRMOVE(p, p + 2); + } } } - /* Ensure that the result will have no trailing path separator - * if the argument had none. But keep "/" or "//". */ + // Ensure that the result will have no trailing path separator + // if the argument had none. But keep "/" or "//". if (!has_trailing_pathsep) { - q = p + STRLEN(p); - if (after_pathsep((char *)p, (char *)q)) - *path_tail_with_sep(p) = NUL; + q = p + strlen(p); + if (after_pathsep(p, q)) { + *path_tail_with_sep((char_u *)p) = NUL; + } } - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)p; + xfree(buf); } # else - rettv->vval.v_string = vim_strsave(p); + rettv->vval.v_string = (char_u *)xstrdup(p); # endif #endif simplify_filename(rettv->vval.v_string); - -#ifdef HAVE_READLINK -fail: - xfree(buf); -#endif - rettv->v_type = VAR_STRING; } /* @@ -13542,7 +13576,6 @@ static int get_search_arg(typval_T *varp, int *flagsp) static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) { int flags; - char_u *pat; pos_T pos; pos_T save_cursor; bool save_p_ws = p_ws; @@ -13554,10 +13587,11 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) int options = SEARCH_KEEP; int subpatnum; - pat = get_tv_string(&argvars[0]); - dir = get_search_arg(&argvars[1], flagsp); /* may set p_ws */ - if (dir == 0) + const char *const pat = tv_get_string(&argvars[0]); + dir = get_search_arg(&argvars[1], flagsp); // May set p_ws. + if (dir == 0) { goto theend; + } flags = *flagsp; if (flags & SP_START) { options |= SEARCH_START; @@ -13592,13 +13626,13 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) */ if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0) || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[1])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[1])); goto theend; } pos = save_cursor = curwin->w_cursor; - subpatnum = searchit(curwin, curbuf, &pos, dir, pat, 1L, - options, RE_SEARCH, (linenr_T)lnum_stop, &tm); + subpatnum = searchit(curwin, curbuf, &pos, dir, (char_u *)pat, 1, + options, RE_SEARCH, (linenr_T)lnum_stop, &tm); if (subpatnum != FAIL) { if (flags & SP_SUBPAT) retval = subpatnum; @@ -13655,7 +13689,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; @@ -13722,7 +13756,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) Error err = ERROR_INIT; Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - (char *)get_tv_string(&argvars[1]), + tv_get_string(&argvars[1]), args, &err); @@ -13797,7 +13831,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Copy arguments to the vector if (argsl > 0) { for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup((char *) get_tv_string(&arg->li_tv)); + argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); } } @@ -13962,7 +13996,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) */ if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[3])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } @@ -14232,7 +14266,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_invarg)); return; } else { - rettv->vval.v_string = vim_strsave(get_tv_string(argvars)); + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string(argvars)); } } else { rettv->vval.v_string = (char_u *)server_address_new(); @@ -14613,7 +14647,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Note: there are three number buffers involved: // - group_buf below. // - numbuf in tv_dict_get_string(). - // - mybuf in get_tv_string(). + // - mybuf in tv_get_string(). // // If you change this code make sure that buffers will not get // accidentally reused. @@ -14623,8 +14657,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int id = (int)tv_dict_get_number(d, "id"); dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); const char *const conceal = (conceal_di != NULL - ? (const char *)get_tv_string( - &conceal_di->di_tv) + ? tv_get_string(&conceal_di->di_tv) : NULL); if (i == 0) { if (match_add(curwin, group, @@ -14899,11 +14932,11 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) /// f_sha256 - sha256({string}) function static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = get_tv_string(&argvars[0]); - const char_u *hash = sha256_bytes(p, (int) STRLEN(p) , NULL, 0); + const char *p = tv_get_string(&argvars[0]); + const char *hash = sha256_bytes((const uint8_t *)p, strlen(p) , NULL, 0); // make a copy of the hash (sha256_bytes returns a static buffer) - rettv->vval.v_string = (char_u *) xstrdup((char *) hash); + rettv->vval.v_string = (char_u *)xstrdup(hash); rettv->v_type = VAR_STRING; } @@ -14913,7 +14946,8 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_string = vim_strsave_shellescape( - get_tv_string(&argvars[0]), non_zero_arg(&argvars[1]), true); + (const char_u *)tv_get_string(&argvars[0]), non_zero_arg(&argvars[1]), + true); rettv->v_type = VAR_STRING; } @@ -14930,11 +14964,9 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - - p = get_tv_string(&argvars[0]); - rettv->vval.v_string = vim_strsave(p); - simplify_filename(rettv->vval.v_string); /* simplify in place */ + const char *const p = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)xstrdup(p); + simplify_filename(rettv->vval.v_string); // Simplify in place. rettv->v_type = VAR_STRING; } @@ -14950,7 +14982,7 @@ typedef struct { bool item_compare_numeric; bool item_compare_numbers; bool item_compare_float; - char_u *item_compare_func; + const char *item_compare_func; partial_T *item_compare_partial; dict_T *item_compare_selfdict; bool item_compare_func_err; @@ -15058,7 +15090,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) typval_T rettv; typval_T argv[3]; int dummy; - char_u *func_name; + const char *func_name; partial_T *partial = sortinfo->item_compare_partial; // shortcut after failure in previous call; compare all items equal @@ -15072,7 +15104,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (partial == NULL) { func_name = sortinfo->item_compare_func; } else { - func_name = partial_name(partial); + func_name = (const char *)partial_name(partial); } // Copy the values. This is needed to be able to set v_lock to VAR_FIXED @@ -15081,7 +15113,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) copy_tv(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func(func_name, + res = call_func((const char_u *)func_name, (int)STRLEN(func_name), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, sortinfo->item_compare_selfdict); @@ -15167,7 +15199,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[1].v_type != VAR_UNKNOWN) { /* optional second argument: {func} */ if (argvars[1].v_type == VAR_FUNC) { - info.item_compare_func = argvars[1].vval.v_string; + info.item_compare_func = (const char *)argvars[1].vval.v_string; } else if (argvars[1].v_type == VAR_PARTIAL) { info.item_compare_partial = argvars[1].vval.v_partial; } else { @@ -15180,7 +15212,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (i == 1) { info.item_compare_ic = true; } else if (argvars[1].v_type != VAR_NUMBER) { - info.item_compare_func = get_tv_string(&argvars[1]); + info.item_compare_func = tv_get_string(&argvars[1]); } else if (i != 0) { EMSG(_(e_invarg)); goto theend; @@ -15189,16 +15221,16 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (*info.item_compare_func == NUL) { // empty string means default sort info.item_compare_func = NULL; - } else if (STRCMP(info.item_compare_func, "n") == 0) { + } else if (strcmp(info.item_compare_func, "n") == 0) { info.item_compare_func = NULL; info.item_compare_numeric = true; - } else if (STRCMP(info.item_compare_func, "N") == 0) { + } else if (strcmp(info.item_compare_func, "N") == 0) { info.item_compare_func = NULL; info.item_compare_numbers = true; - } else if (STRCMP(info.item_compare_func, "f") == 0) { + } else if (strcmp(info.item_compare_func, "f") == 0) { info.item_compare_func = NULL; info.item_compare_float = true; - } else if (STRCMP(info.item_compare_func, "i") == 0) { + } else if (strcmp(info.item_compare_func, "i") == 0) { info.item_compare_func = NULL; info.item_compare_ic = true; } @@ -15332,11 +15364,9 @@ static void f_reltimefloat(typval_T *argvars , typval_T *rettv, FunPtr fptr) */ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - rettv->v_type = VAR_STRING; - s = get_tv_string(&argvars[0]); - rettv->vval.v_string = eval_soundfold(s); + const char *const s = tv_get_string(&argvars[0]); + rettv->vval.v_string = (char_u *)eval_soundfold(s); } /* @@ -15387,7 +15417,6 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; bool typeerr = false; int maxcount; garray_T ga; @@ -15397,7 +15426,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv); if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { - str = get_tv_string(&argvars[0]); + const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = get_tv_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) @@ -15410,15 +15439,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else maxcount = 25; - spell_suggest_list(&ga, str, maxcount, need_capital, false); + spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); - for (int i = 0; i < ga.ga_len; ++i) { - str = ((char_u **)ga.ga_data)[i]; + for (int i = 0; i < ga.ga_len; i++) { + char *p = ((char **)ga.ga_data)[i]; li = tv_list_item_alloc(); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = str; + li->li_tv.vval.v_string = (char_u *)p; tv_list_append(rettv->vval.v_list, li); } ga_clear(&ga); @@ -15427,8 +15456,6 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *str; - char_u *end; char_u *pat = NULL; regmatch_T regmatch; char_u patbuf[NUMBUFLEN]; @@ -15442,7 +15469,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_cpo = p_cpo; p_cpo = (char_u *)""; - str = get_tv_string(&argvars[0]); + const char *str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { pat = get_tv_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { @@ -15464,29 +15491,33 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { - if (*str == NUL) - match = FALSE; /* empty item at the end */ - else - match = vim_regexec_nl(®match, str, col); - if (match) - end = regmatch.startp[0]; - else - end = str + STRLEN(str); + if (*str == NUL) { + match = false; // Empty item at the end. + } else { + match = vim_regexec_nl(®match, (char_u *)str, col); + } + const char *end; + if (match) { + end = (const char *)regmatch.startp[0]; + } else { + end = str + strlen(str); + } if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 - && *str != NUL && match && end < - regmatch.endp[0])) { - tv_list_append_string(rettv->vval.v_list, (const char *)str, end - str); + && *str != NUL + && match + && end < (const char *)regmatch.endp[0])) { + tv_list_append_string(rettv->vval.v_list, str, end - str); } if (!match) break; - /* Advance to just after the match. */ - if (regmatch.endp[0] > str) + // Advance to just after the match. + if (regmatch.endp[0] > (char_u *)str) { col = 0; - else { - /* Don't get stuck at the same match. */ + } else { + // Don't get stuck at the same match. col = (*mb_ptr2len)(regmatch.endp[0]); } - str = regmatch.endp[0]; + str = (const char *)regmatch.endp[0]; } vim_regfree(regmatch.regprog); @@ -15500,11 +15531,12 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); - if (*p == '+') + if (*p == '+') { p = skipwhite(p + 1); - (void) string2float((char *) p, &rettv->vval.v_float); + } + (void)string2float((char *)p, &rettv->vval.v_float); rettv->v_type = VAR_FLOAT; } @@ -15512,7 +15544,6 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int base = 10; - char_u *p; long n; int what; @@ -15524,22 +15555,26 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - p = skipwhite(get_tv_string(&argvars[0])); + char_u *p = skipwhite((const char_u *)tv_get_string(&argvars[0])); if (*p == '+') { p = skipwhite(p + 1); } switch (base) { - case 2: - what = STR2NR_BIN + STR2NR_FORCE; + case 2: { + what = STR2NR_BIN | STR2NR_FORCE; break; - case 8: - what = STR2NR_OCT + STR2NR_FORCE; + } + case 8: { + what = STR2NR_OCT | STR2NR_FORCE; break; - case 16: - what = STR2NR_HEX + STR2NR_FORCE; + } + case 16: { + what = STR2NR_HEX | STR2NR_FORCE; break; - default: + } + default: { what = 0; + } } vim_str2nr(p, NULL, NULL, what, &n, NULL, 0); rettv->vval.v_number = n; @@ -15550,17 +15585,16 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u result_buf[256]; time_t seconds; - char_u *p; rettv->v_type = VAR_STRING; - p = get_tv_string(&argvars[0]); - if (argvars[1].v_type == VAR_UNKNOWN) + char *p = (char *)tv_get_string(&argvars[0]); + if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); - else + } else { seconds = (time_t)get_tv_number(&argvars[1]); + } struct tm curtime; struct tm *curtime_ptr = os_localtime_r(&seconds, &curtime); @@ -15574,23 +15608,27 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) conv.vc_type = CONV_NONE; enc = enc_locale(); convert_setup(&conv, p_enc, enc); - if (conv.vc_type != CONV_NONE) - p = string_convert(&conv, p, NULL); - if (p != NULL) - (void)strftime((char *)result_buf, sizeof(result_buf), - (char *)p, curtime_ptr); - else + if (conv.vc_type != CONV_NONE) { + p = (char *)string_convert(&conv, (char_u *)p, NULL); + } + char result_buf[256]; + if (p != NULL) { + (void)strftime(result_buf, sizeof(result_buf), p, curtime_ptr); + } else { result_buf[0] = NUL; + } - if (conv.vc_type != CONV_NONE) + if (conv.vc_type != CONV_NONE) { xfree(p); + } convert_setup(&conv, enc, p_enc); - if (conv.vc_type != CONV_NONE) - rettv->vval.v_string = string_convert(&conv, result_buf, NULL); - else - rettv->vval.v_string = vim_strsave(result_buf); + if (conv.vc_type != CONV_NONE) { + rettv->vval.v_string = string_convert(&conv, (char_u *)result_buf, NULL); + } else { + rettv->vval.v_string = (char_u *)xstrdup(result_buf); + } - /* Release conversion descriptors */ + // Release conversion descriptors. convert_setup(&conv, NULL, NULL); xfree(enc); } @@ -15672,8 +15710,7 @@ static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = (varnumber_T)(STRLEN( - get_tv_string(&argvars[0]))); + rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0])); } /* @@ -15681,7 +15718,7 @@ static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; int (*func_mb_ptr2char_adv)(char_u **pp); @@ -15694,8 +15731,8 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv(&s); - ++len; + func_mb_ptr2char_adv((char_u **)&s); + len++; } rettv->vval.v_number = len; } @@ -15706,13 +15743,14 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); int col = 0; - if (argvars[1].v_type != VAR_UNKNOWN) + if (argvars[1].v_type != VAR_UNKNOWN) { col = get_tv_number(&argvars[1]); + } - rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, s) - col); + rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); } /* @@ -15720,15 +15758,15 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s = get_tv_string(&argvars[0]); + const char *const s = tv_get_string(&argvars[0]); - rettv->vval.v_number = (varnumber_T) mb_string2cells(s); + rettv->vval.v_number = (varnumber_T)mb_string2cells((const char_u *)s); } // "strcharpart()" function static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *const p = get_tv_string(&argvars[0]); + const char *const p = tv_get_string(&argvars[0]); const size_t slen = STRLEN(p); int nbyte = 0; @@ -15737,7 +15775,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { - nbyte += MB_CPTR2LEN(p + nbyte); + nbyte += MB_CPTR2LEN((const char_u *)p + nbyte); nchar--; } } else { @@ -15753,7 +15791,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (off < 0) { len += 1; } else { - len += MB_CPTR2LEN(p + off); + len += (size_t)MB_CPTR2LEN((const char_u *)p + off); } charlen--; } @@ -15776,7 +15814,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + nbyte, len); + rettv->vval.v_string = (char_u *)xstrndup(p + nbyte, (size_t)len); } /* @@ -15784,39 +15822,37 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p; - int n; - int len; - int slen; bool error = false; - p = get_tv_string(&argvars[0]); - slen = (int)STRLEN(p); + const char *const p = tv_get_string(&argvars[0]); + const size_t slen = strlen(p); - n = get_tv_number_chk(&argvars[1], &error); - if (error) + varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T len; + if (error) { len = 0; - else if (argvars[2].v_type != VAR_UNKNOWN) + } else if (argvars[2].v_type != VAR_UNKNOWN) { len = get_tv_number(&argvars[2]); - else - len = slen - n; /* default len: all bytes that are available. */ + } else { + len = slen - n; // Default len: all bytes that are available. + } - /* - * Only return the overlap between the specified part and the actual - * string. - */ + // Only return the overlap between the specified part and the actual + // string. if (n < 0) { len += n; n = 0; - } else if (n > slen) + } else if (n > (varnumber_T)slen) { n = slen; - if (len < 0) + } + if (len < 0) { len = 0; - else if (n + len > slen) + } else if (n + len > (varnumber_T)slen) { len = slen - n; + } rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strnsave(p + n, len); + rettv->vval.v_string = (char_u *)xmemdupz(p + n, (size_t)len); } /* @@ -15871,7 +15907,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = transstr(get_tv_string(&argvars[0])); + rettv->vval.v_string = transstr((char_u *)tv_get_string(&argvars[0])); } /* @@ -15962,20 +15998,17 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = NULL; - int id; - char_u *what; - char_u *mode; - char_u modebuf[NUMBUFLEN]; + const int id = get_tv_number(&argvars[0]); + const char *const what = tv_get_string(&argvars[1]); int modec; - - id = get_tv_number(&argvars[0]); - what = get_tv_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - mode = get_tv_string_buf(&argvars[2], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode = (const char *)get_tv_string_buf(&argvars[2], + (char_u *)modebuf); modec = TOLOWER_ASC(mode[0]); - if (modec != 'c' && modec != 'g') - modec = 0; /* replace invalid with current */ + if (modec != 'c' && modec != 'g') { + modec = 0; // Replace invalid with current. + } } else if (ui_rgb_attached()) { modec = 'g'; } else { @@ -15983,54 +16016,56 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) } + const char *p = NULL; switch (TOLOWER_ASC(what[0])) { - case 'b': - if (TOLOWER_ASC(what[1]) == 'g') /* bg[#] */ + case 'b': { + if (TOLOWER_ASC(what[1]) == 'g') { // bg[#] + p = highlight_color(id, what, modec); + } else { // bold + p = highlight_has_attr(id, HL_BOLD, modec); + } + break; + } + case 'f': { // fg[#] or font p = highlight_color(id, what, modec); - else /* bold */ - p = highlight_has_attr(id, HL_BOLD, modec); - break; - - case 'f': /* fg[#] or font */ - p = highlight_color(id, what, modec); - break; - - case 'i': - if (TOLOWER_ASC(what[1]) == 'n') /* inverse */ + break; + } + case 'i': { + if (TOLOWER_ASC(what[1]) == 'n') { // inverse + p = highlight_has_attr(id, HL_INVERSE, modec); + } else { // italic + p = highlight_has_attr(id, HL_ITALIC, modec); + } + break; + } + case 'n': { // name + p = get_highlight_name(NULL, id - 1); + break; + } + case 'r': { // reverse p = highlight_has_attr(id, HL_INVERSE, modec); - else /* italic */ - p = highlight_has_attr(id, HL_ITALIC, modec); - break; - - case 'n': // name - p = (char_u *)get_highlight_name(NULL, id - 1); - break; - - case 'r': /* reverse */ - p = highlight_has_attr(id, HL_INVERSE, modec); - break; - - case 's': - if (TOLOWER_ASC(what[1]) == 'p') /* sp[#] */ - p = highlight_color(id, what, modec); - else /* standout */ - p = highlight_has_attr(id, HL_STANDOUT, modec); - break; - - case 'u': - if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') - /* underline */ - p = highlight_has_attr(id, HL_UNDERLINE, modec); - else - /* undercurl */ - p = highlight_has_attr(id, HL_UNDERCURL, modec); - break; + break; + } + case 's': { + if (TOLOWER_ASC(what[1]) == 'p') { // sp[#] + p = highlight_color(id, what, modec); + } else { // standout + p = highlight_has_attr(id, HL_STANDOUT, modec); + } + break; + } + case 'u': { + if (STRLEN(what) <= 5 || TOLOWER_ASC(what[5]) != 'c') { // underline + p = highlight_has_attr(id, HL_UNDERLINE, modec); + } else { // undercurl + p = highlight_has_attr(id, HL_UNDERCURL, modec); + } + break; + } } - if (p != NULL) - p = vim_strsave(p); rettv->v_type = VAR_STRING; - rettv->vval.v_string = p; + rettv->vval.v_string = (char_u *)(p == NULL ? p : xstrdup(p)); } /* @@ -16147,8 +16182,8 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get input to the shell command (if any), and its length - ssize_t input_len; - char *input = (char *) save_tv_as_string(&argvars[1], &input_len, false); + ptrdiff_t input_len; + char *input = save_tv_as_string(&argvars[1], &input_len, false); if (input_len < 0) { assert(input == NULL); return; @@ -16359,15 +16394,14 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *tag_pattern; - - tag_pattern = get_tv_string(&argvars[0]); + const char *const tag_pattern = tv_get_string(&argvars[0]); - rettv->vval.v_number = FALSE; - if (*tag_pattern == NUL) + rettv->vval.v_number = false; + if (*tag_pattern == NUL) { return; + } - (void)get_tags(tv_list_alloc_ret(rettv), tag_pattern); + (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern); } /* @@ -16391,7 +16425,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - char *cmd; + const char *cmd; bool executable = true; char **argv = tv_to_argv(&argvars[0], &cmd, &executable); if (!argv) { @@ -16614,7 +16648,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_DICT || (dict = argvars[2].vval.v_dict) == NULL) { - EMSG2(_(e_invarg2), get_tv_string(&argvars[2])); + EMSG2(_(e_invarg2), tv_get_string(&argvars[2])); return; } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); @@ -16740,7 +16774,7 @@ void timer_teardown(void) */ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *p = vim_strsave(get_tv_string(&argvars[0])); + char_u *p = (char_u *)xstrdup(tv_get_string(&argvars[0])); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -16772,7 +16806,7 @@ static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = strup_save(get_tv_string(&argvars[0])); + rettv->vval.v_string = (char_u *)strup_save(tv_get_string(&argvars[0])); } /* @@ -16780,56 +16814,48 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *in_str; - char_u *fromstr; - char_u *tostr; - char_u *p; - int inlen; int fromlen; int tolen; int idx; - char_u *cpstr; - int cplen; - int first = TRUE; char_u buf[NUMBUFLEN]; char_u buf2[NUMBUFLEN]; garray_T ga; - in_str = get_tv_string(&argvars[0]); - fromstr = get_tv_string_buf_chk(&argvars[1], buf); - tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *in_str = tv_get_string(&argvars[0]); + const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); + const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); - /* Default return value: empty string. */ + // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if (fromstr == NULL || tostr == NULL) - return; /* type error; errmsg already given */ + if (fromstr == NULL || tostr == NULL) { + return; // type error; errmsg already given + } ga_init(&ga, (int)sizeof(char), 80); - if (!has_mbyte) - /* not multi-byte: fromstr and tostr must be the same length */ + if (!has_mbyte) { + // Not multi-byte: fromstr and tostr must be the same length. if (STRLEN(fromstr) != STRLEN(tostr)) { -error: - EMSG2(_(e_invarg2), fromstr); - ga_clear(&ga); - return; + goto error; } + } - /* fromstr and tostr have to contain the same number of chars */ + // fromstr and tostr have to contain the same number of chars. + bool first = true; while (*in_str != NUL) { if (has_mbyte) { - inlen = (*mb_ptr2len)(in_str); - cpstr = in_str; - cplen = inlen; + const char *cpstr = in_str; + const int inlen = (*mb_ptr2len)((const char_u *)in_str); + int cplen = inlen; idx = 0; - for (p = fromstr; *p != NUL; p += fromlen) { + for (const char_u *p = fromstr; *p != NUL; p += fromlen) { fromlen = (*mb_ptr2len)(p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { for (p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); if (idx-- == 0) { cplen = tolen; - cpstr = p; + cpstr = (char *)p; break; } } @@ -16841,16 +16867,17 @@ error: } if (first && cpstr == in_str) { - /* Check that fromstr and tostr have the same number of - * (multi-byte) characters. Done only once when a character - * of in_str doesn't appear in fromstr. */ - first = FALSE; - for (p = tostr; *p != NUL; p += tolen) { + // Check that fromstr and tostr have the same number of + // (multi-byte) characters. Done only once when a character + // of in_str doesn't appear in fromstr. + first = false; + for (const char_u *p = tostr; *p != NUL; p += tolen) { tolen = (*mb_ptr2len)(p); - --idx; + idx--; } - if (idx != 0) + if (idx != 0) { goto error; + } } ga_grow(&ga, cplen); @@ -16859,13 +16886,14 @@ error: in_str += inlen; } else { - /* When not using multi-byte chars we can do it faster. */ - p = vim_strchr(fromstr, *in_str); - if (p != NULL) + // When not using multi-byte chars we can do it faster. + char_u *p = vim_strchr(fromstr, *in_str); + if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); - else + } else { ga_append(&ga, *in_str); - ++in_str; + } + in_str++; } } @@ -16873,6 +16901,11 @@ error: ga_append(&ga, NUL); rettv->vval.v_string = ga.ga_data; + return; +error: + EMSG2(_(e_invarg2), fromstr); + ga_clear(&ga); + return; } /* @@ -16918,20 +16951,18 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - { - char_u *fname = get_tv_string(&argvars[0]); + const char *const fname = tv_get_string(&argvars[0]); - if (*fname == NUL) { - /* If there is no file name there will be no undo file. */ - rettv->vval.v_string = NULL; - } else { - char *ffname = FullName_save((char *)fname, false); + if (*fname == NUL) { + // If there is no file name there will be no undo file. + rettv->vval.v_string = NULL; + } else { + char *ffname = FullName_save(fname, false); - if (ffname != NULL) { - rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); - } - xfree(ffname); + if (ffname != NULL) { + rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); } + xfree(ffname); } } @@ -17290,7 +17321,7 @@ void init_static_list(staticList10_T *sl) /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. -static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) +static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { if (tv->v_type == VAR_UNKNOWN) { @@ -17301,9 +17332,9 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) // For types other than list, let get_tv_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char_u *ret = get_tv_string_chk(tv); - if (ret && (*len = STRLEN(ret))) { - ret = vim_strsave(ret); + char *ret = (char *)get_tv_string_chk(tv); + if (ret && (*len = strlen(ret))) { + ret = xstrdup(ret); } else { ret = NULL; *len = -1; @@ -17315,17 +17346,17 @@ static char_u *save_tv_as_string(typval_T *tv, ssize_t *len, bool endnl) *len = 0; list_T *list = tv->vval.v_list; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += STRLEN(get_tv_string(&li->li_tv)) + 1; + *len += strlen(tv_get_string(&li->li_tv)) + 1; } if (*len == 0) { return NULL; } - char_u *ret = xmalloc(*len + endnl); - char_u *end = ret; + char *ret = xmalloc(*len + endnl); + char *end = ret; for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (char_u *s = get_tv_string(&li->li_tv); *s != NUL; s++) { + for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } if (endnl || li->li_next != NULL) { @@ -17879,7 +17910,7 @@ long get_vim_var_nr(int idx) FUNC_ATTR_PURE */ char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { - return get_tv_string(&vimvars[idx].vv_tv); + return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } /* @@ -18499,28 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -/// Get the string value of a variable -/// -/// @warning For number and special values it uses a single, static buffer. It -/// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use get_tv_string() output after -/// calling it again. -/// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but -/// return NULL on error. -/// -/// @param[in] varp Varible to get value of. -/// -/// @return Variable value if it is VAR_STRING variable, number converted to -/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty -/// string. -char_u *get_tv_string(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - return get_tv_string_buf(varp, mybuf); -} - char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { @@ -18787,7 +18796,7 @@ static hashtab_T *find_var_ht(const char *name, const size_t name_len, /* * Get the string value of a (global/local) variable. - * Note: see get_tv_string() for how long the pointer remains valid. + * Note: see tv_get_string() for how long the pointer remains valid. * Returns NULL when it doesn't exist. */ char_u *get_var_value(const char *const name) @@ -18798,7 +18807,7 @@ char_u *get_var_value(const char *const name) if (v == NULL) { return NULL; } - return get_tv_string(&v->di_tv); + return (char_u *)tv_get_string(&v->di_tv); } /* @@ -19016,9 +19025,9 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) if (ht == &vimvarht) { if (v->di_tv.v_type == VAR_STRING) { xfree(v->di_tv.vval.v_string); - if (copy || tv->v_type != VAR_STRING) - v->di_tv.vval.v_string = vim_strsave(get_tv_string(tv)); - else { + if (copy || tv->v_type != VAR_STRING) { + v->di_tv.vval.v_string = (char_u *)xstrdup(tv_get_string(tv)); + } else { // Take over the string to avoid an extra alloc/free. v->di_tv.vval.v_string = tv->vval.v_string; tv->vval.v_string = NULL; @@ -19439,7 +19448,6 @@ void ex_execute(exarg_T *eap) int ret = OK; char_u *p; garray_T ga; - int len; int save_did_emsg; ga_init(&ga, 1, 80); @@ -19461,12 +19469,13 @@ void ex_execute(exarg_T *eap) } if (!eap->skip) { - p = get_tv_string(&rettv); - len = (int)STRLEN(p); + const char *const argstr = tv_get_string(&rettv); + const size_t len = strlen(argstr); ga_grow(&ga, len + 2); - if (!GA_EMPTY(&ga)) + if (!GA_EMPTY(&ga)) { ((char_u *)(ga.ga_data))[ga.ga_len++] = ' '; - STRCPY((char_u *)(ga.ga_data) + ga.ga_len, p); + } + memcpy((char_u *)(ga.ga_data) + ga.ga_len, argstr, len + 1); ga.ga_len += len; } @@ -21920,8 +21929,9 @@ int store_session_globals(FILE *fd) && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) { // Escape special characters with a backslash. Turn a LF and // CR into \n and \r. - char_u *const p = vim_strsave_escaped(get_tv_string(&this_var->di_tv), - (char_u *)"\\\"\n\r"); + char_u *const p = vim_strsave_escaped( + (const char_u *)tv_get_string(&this_var->di_tv), + (const char_u *)"\\\"\n\r"); for (char_u *t = p; *t != NUL; t++) { if (*t == '\n') { *t = 'n'; @@ -21992,7 +22002,7 @@ void ex_oldfiles(exarg_T *eap) for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { msg_outnum(++nr); MSG_PUTS(": "); - msg_outtrans(get_tv_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(&li->li_tv)); msg_clr_eos(); msg_putchar('\n'); ui_flush(); /* output one line at a time */ @@ -22007,15 +22017,15 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - char *p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr); if (p == NULL) { return; } - p = (char *)expand_env_save((char_u *)p); - eap->arg = (char_u *)p; + char *const s = (char *)expand_env_save((char_u *)p); + eap->arg = (char_u *)s; eap->cmdidx = CMD_edit; do_exedit(eap, NULL); - xfree(p); + xfree(s); } } } @@ -22728,7 +22738,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) arguments->lv_refcount++; int dummy; - (void)call_func((uint8_t *)func, + (void)call_func((const char_u *)func, name_len, &rettv, 2, @@ -22750,7 +22760,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) return rettv; } -bool eval_has_provider(char *name) +bool eval_has_provider(const char *name) { #define check_provider(name) \ if (has_##name == -1) { \ diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 7f6fd76c46..070bc35bd5 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -2,7 +2,7 @@ #define NVIM_EVAL_H #include "nvim/hashtab.h" // For hashtab_T -#include "nvim/buffer_defs.h" // For scid_T +#include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" // For exarg_T #include "nvim/eval/typval.h" #include "nvim/profile.h" diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index ab48ace400..41b55e4a57 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -77,10 +77,10 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, if (tv2->v_type == VAR_FLOAT) { break; } - char *s = (char *)get_tv_string(tv1); + const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; - s = (char *)concat_str((char_u *)s, - get_tv_string_buf(tv2, (char_u *)numbuf)); + char *const s = (char *)concat_str( + (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 62460bcc3a..cbeb2e059c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -733,7 +733,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) /// @param[in] n Index in a list. /// /// @return [allocated] Copy of the list item string value. -char *tv_list_find_str(list_T *l, int n) +const char *tv_list_find_str(list_T *l, int n) FUNC_ATTR_MALLOC { const listitem_T *const li = tv_list_find(l, n - 1); @@ -741,7 +741,7 @@ char *tv_list_find_str(list_T *l, int n) EMSGN(_(e_listidx), n); return NULL; } - return (char *)get_tv_string(&li->li_tv); + return tv_get_string(&li->li_tv); } /// Locate item in a list and return its index @@ -2014,3 +2014,27 @@ bool tv_check_str_or_nr(const typval_T *const tv) assert(false); return false; } + +//{{{2 Get + +/// Get the string value of a variable +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// get_tv_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string(const typval_T *const varp) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char_u mybuf[NUMBUFLEN]; + return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); +} diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 0eef2ddaac..5645772124 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -205,6 +205,8 @@ struct dictvar_S { /// Type used for script ID typedef int scid_T; +/// Format argument for scid_T +#define PRIdSCID "d" // Structure to hold info for a function that is currently being executed. typedef struct funccall_S funccall_T; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 87fe52c119..5209dfc451 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -270,7 +270,7 @@ do_exmode ( /* * Execute a simple command line. Used for translated commands like "*". */ -int do_cmdline_cmd(char *cmd) +int do_cmdline_cmd(const char *cmd) { return do_cmdline((char_u *)cmd, NULL, NULL, DOCMD_NOWAIT|DOCMD_KEYTYPED); @@ -9347,8 +9347,8 @@ static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) *p = '/'; } - /* escape special characters */ - p = vim_strsave_fnameescape(sname, FALSE); + // Escape special characters. + p = (char_u *)vim_strsave_fnameescape((const char *)sname, false); xfree(sname); /* write the result */ diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 7a34a181e2..3f71ae1795 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1149,23 +1149,25 @@ void ex_endwhile(exarg_T *eap) */ void ex_throw(exarg_T *eap) { - char_u *arg = eap->arg; - char_u *value; + const char *arg = (const char *)eap->arg; + char *value; - if (*arg != NUL && *arg != '|' && *arg != '\n') - value = eval_to_string_skip(arg, &eap->nextcmd, eap->skip); - else { + if (*arg != NUL && *arg != '|' && *arg != '\n') { + value = eval_to_string_skip(arg, (const char **)&eap->nextcmd, + (bool)eap->skip); + } else { EMSG(_(e_argreq)); value = NULL; } - /* On error or when an exception is thrown during argument evaluation, do - * not throw. */ + // On error or when an exception is thrown during argument evaluation, do + // not throw. if (!eap->skip && value != NULL) { - if (throw_exception(value, ET_USER, NULL) == FAIL) + if (throw_exception((char_u *)value, ET_USER, NULL) == FAIL) { xfree(value); - else + } else { do_throw(eap->cstack); + } } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 872b7fe365..3bd8d580ab 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -8,6 +8,7 @@ #include #include +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -960,7 +961,7 @@ static int command_line_handle_key(CommandLineState *s) return command_line_not_changed(s); case Ctrl_HAT: - if (map_to_exists_mode((char_u *)"", LANGMAP, false)) { + if (map_to_exists_mode("", LANGMAP, false)) { // ":lmap" mappings exists, toggle use of mappings. State ^= LANGMAP; if (s->b_im_ptr != NULL) { @@ -3120,9 +3121,10 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o #endif } #ifdef BACKSLASH_IN_FILENAME - p = vim_strsave_fnameescape(files[i], FALSE); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], false); #else - p = vim_strsave_fnameescape(files[i], xp->xp_shell); + p = (char_u *)vim_strsave_fnameescape((const char *)files[i], + xp->xp_shell); #endif xfree(files[i]); files[i] = p; @@ -3152,42 +3154,49 @@ void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char_u **files, int o } } -/* - * Escape special characters in "fname" for when used as a file name argument - * after a Vim command, or, when "shell" is non-zero, a shell command. - * Returns the result in allocated memory. - */ -char_u *vim_strsave_fnameescape(char_u *fname, int shell) FUNC_ATTR_NONNULL_RET +/// Escape special characters in a file name for use as a command argument +/// +/// @param[in] fname File name to escape. +/// @param[in] shell What to escape for: if false, escapes for VimL command, +/// if true then it escapes for a shell command. +/// +/// @return [allocated] escaped file name. +char *vim_strsave_fnameescape(const char *const fname, const bool shell) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *p; #ifdef BACKSLASH_IN_FILENAME -#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`%#'\"|!<") - char_u buf[20]; +#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" + char_u buf[sizeof(PATH_ESC_CHARS)]; int j = 0; - /* Don't escape '[', '{' and '!' if they are in 'isfname'. */ - for (p = PATH_ESC_CHARS; *p != NUL; ++p) - if ((*p != '[' && *p != '{' && *p != '!') || !vim_isfilec(*p)) - buf[j++] = *p; + // Don't escape '[', '{' and '!' if they are in 'isfname'. + for (const char *s = PATH_ESC_CHARS; *s != NUL; s++) { + if ((*s != '[' && *s != '{' && *s != '!') || !vim_isfilec(*s)) { + buf[j++] = *s; + } + } buf[j] = NUL; - p = vim_strsave_escaped(fname, buf); + char *p = (char *)vim_strsave_escaped((const char_u *)fname, + (const char_u *)buf); #else #define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") #define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") - p = vim_strsave_escaped(fname, shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS); + char *p = (char *)vim_strsave_escaped( + (const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); if (shell && csh_like_shell()) { - /* For csh and similar shells need to put two backslashes before '!'. - * One is taken by Vim, one by the shell. */ - char_u *s = vim_strsave_escaped(p, (char_u *)"!"); + // For csh and similar shells need to put two backslashes before '!'. + // One is taken by Vim, one by the shell. + char *s = (char *)vim_strsave_escaped((const char_u *)p, + (const char_u *)"!"); xfree(p); p = s; } #endif - /* '>' and '+' are special at the start of some commands, e.g. ":edit" and - * ":write". "cd -" has a special meaning. */ + // '>' and '+' are special at the start of some commands, e.g. ":edit" and + // ":write". "cd -" has a special meaning. if (*p == '>' || *p == '+' || (*p == '-' && p[1] == NUL)) { - escape_fname(&p); + escape_fname((char_u **)&p); } return p; @@ -4197,9 +4206,11 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u keep; garray_T ga; - retstr = call_user_expand_func(call_func_retstr, xp, num_file, file); - if (retstr == NULL) + retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, + num_file, file); + if (retstr == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); for (s = retstr; *s != NUL; s = e) { @@ -4237,9 +4248,11 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) listitem_T *li; garray_T ga; - retlist = call_user_expand_func(call_func_retlist, xp, num_file, file); - if (retlist == NULL) + retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, + num_file, file); + if (retlist == NULL) { return FAIL; + } ga_init(&ga, (int)sizeof(char *), 3); /* Loop over the items in the list. */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d948e20b32..67ac9f9957 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -428,7 +428,7 @@ readfile ( } if (!read_buffer && !read_stdin) { - perm = os_getperm(fname); + perm = os_getperm((const char *)fname); #ifdef UNIX // On Unix it is possible to read a directory, so we have to // check for it before os_open(). @@ -2606,10 +2606,10 @@ buf_write ( newfile = TRUE; perm = -1; } else { - perm = os_getperm(fname); - if (perm < 0) - newfile = TRUE; - else if (os_isdir(fname)) { + perm = os_getperm((const char *)fname); + if (perm < 0) { + newfile = true; + } else if (os_isdir(fname)) { errnum = (char_u *)"E502: "; errmsg = (char_u *)_("is a directory"); goto fail; @@ -3628,7 +3628,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm(fname) & 0777); + os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4548,9 +4548,9 @@ int put_time(FILE *fd, time_t time_) /// os_rename() only works if both files are on the same file system, this /// function will (attempts to?) copy the file across if rename fails -- webb -// +/// /// @return -1 for failure, 0 for success -int vim_rename(char_u *from, char_u *to) +int vim_rename(const char_u *from, const char_u *to) { int fd_in; int fd_out; @@ -4569,10 +4569,12 @@ int vim_rename(char_u *from, char_u *to) * the file name differs we need to go through a temp file. */ if (fnamecmp(from, to) == 0) { - if (p_fic && STRCMP(path_tail(from), path_tail(to)) != 0) + if (p_fic && (STRCMP(path_tail((char_u *)from), path_tail((char_u *)to)) + != 0)) { use_tmp_file = true; - else + } else { return 0; + } } // Fail if the "from" file doesn't exist. Avoids that "to" is deleted. @@ -4638,9 +4640,9 @@ int vim_rename(char_u *from, char_u *to) /* * Rename() failed, try copying the file. */ - perm = os_getperm(from); + perm = os_getperm((const char *)from); #ifdef HAVE_ACL - /* For systems that support ACL: get the ACL from the original file. */ + // For systems that support ACL: get the ACL from the original file. acl = mch_get_acl(from); #endif fd_in = os_open((char *)from, O_RDONLY, 0); @@ -5261,7 +5263,7 @@ static void vim_maketempdir(void) /// Delete "name" and everything in it, recursively. /// @param name The path which should be deleted. /// @return 0 for success, -1 if some file was not deleted. -int delete_recursive(char_u *name) +int delete_recursive(const char *name) { int result = 0; @@ -5275,7 +5277,7 @@ int delete_recursive(char_u *name) EW_DIR | EW_FILE | EW_SILENT | EW_ALLLINKS | EW_DODOT | EW_EMPTYOK) == OK) { for (int i = 0; i < file_count; i++) { - if (delete_recursive(files[i]) != 0) { + if (delete_recursive((const char *)files[i]) != 0) { result = -1; } } @@ -5285,9 +5287,9 @@ int delete_recursive(char_u *name) } xfree(exp); - os_rmdir((char *)name); + os_rmdir(name); } else { - result = os_remove((char *)name) == 0 ? 0 : -1; + result = os_remove(name) == 0 ? 0 : -1; } return result; @@ -5299,7 +5301,7 @@ void vim_deltempdir(void) if (vim_tempdir != NULL) { // remove the trailing path separator path_tail(vim_tempdir)[-1] = NUL; - delete_recursive(vim_tempdir); + delete_recursive((const char *)vim_tempdir); xfree(vim_tempdir); vim_tempdir = NULL; } diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 076ee13a80..b64f089b96 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -3218,82 +3218,99 @@ showmap ( ui_flush(); /* show one line at a time */ } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "modechars". - * Recognize termcap codes in "str". - * Also checks mappings local to the current buffer. - */ -int map_to_exists(char_u *str, char_u *modechars, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] str String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] modechars Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +bool map_to_exists(const char *const str, const char *const modechars, + const bool abbr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int mode = 0; - char_u *rhs; - char_u *buf; int retval; - rhs = replace_termcodes(str, STRLEN(str), &buf, false, true, false, - CPO_TO_CPO_FLAGS); - - if (vim_strchr(modechars, 'n') != NULL) - mode |= NORMAL; - if (vim_strchr(modechars, 'v') != NULL) - mode |= VISUAL + SELECTMODE; - if (vim_strchr(modechars, 'x') != NULL) - mode |= VISUAL; - if (vim_strchr(modechars, 's') != NULL) - mode |= SELECTMODE; - if (vim_strchr(modechars, 'o') != NULL) - mode |= OP_PENDING; - if (vim_strchr(modechars, 'i') != NULL) - mode |= INSERT; - if (vim_strchr(modechars, 'l') != NULL) - mode |= LANGMAP; - if (vim_strchr(modechars, 'c') != NULL) - mode |= CMDLINE; - - retval = map_to_exists_mode(rhs, mode, abbr); + char_u *buf; + char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, + false, true, false, + CPO_TO_CPO_FLAGS); + +#define MAPMODE(mode, modechars, chr, modeflags) \ + do { \ + if (strchr(modechars, chr) != NULL) { \ + mode |= modeflags; \ + } \ + } while (0) + MAPMODE(mode, modechars, 'n', NORMAL); + MAPMODE(mode, modechars, 'v', VISUAL|SELECTMODE); + MAPMODE(mode, modechars, 'x', VISUAL); + MAPMODE(mode, modechars, 's', SELECTMODE); + MAPMODE(mode, modechars, 'o', OP_PENDING); + MAPMODE(mode, modechars, 'i', INSERT); + MAPMODE(mode, modechars, 'l', LANGMAP); + MAPMODE(mode, modechars, 'c', CMDLINE); +#undef MAPMODE + + retval = map_to_exists_mode((const char *)rhs, mode, abbr); xfree(buf); return retval; } -/* - * Return TRUE if a map exists that has "str" in the rhs for mode "mode". - * Also checks mappings local to the current buffer. - */ -int map_to_exists_mode(char_u *rhs, int mode, int abbr) +/// Check if a map exists that has given string in the rhs +/// +/// Also checks mappings local to the current buffer. +/// +/// @param[in] rhs String which mapping must have in the rhs. Termcap codes +/// are recognized in this argument. +/// @param[in] mode Mode(s) in which mappings are checked. +/// @param[in] abbr true if checking abbreviations in place of mappings. +/// +/// @return true if there is at least one mapping with given parameters. +int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr) { mapblock_T *mp; int hash; - int expand_buffer = FALSE; + bool expand_buffer = false; validate_maphash(); - /* Do it twice: once for global maps and once for local maps. */ - for (;; ) { - for (hash = 0; hash < 256; ++hash) { + // Do it twice: once for global maps and once for local maps. + for (;;) { + for (hash = 0; hash < 256; hash++) { if (abbr) { - if (hash > 0) /* there is only one abbr list */ + if (hash > 0) { // There is only one abbr list. break; - if (expand_buffer) + } + if (expand_buffer) { mp = curbuf->b_first_abbr; - else + } else { mp = first_abbr; - } else if (expand_buffer) + } + } else if (expand_buffer) { mp = curbuf->b_maphash[hash]; - else + } else { mp = maphash[hash]; + } for (; mp; mp = mp->m_next) { if ((mp->m_mode & mode) - && strstr((char *)mp->m_str, (char *)rhs) != NULL) - return TRUE; + && strstr((char *)mp->m_str, rhs) != NULL) { + return true; + } } } - if (expand_buffer) + if (expand_buffer) { break; - expand_buffer = TRUE; + } + expand_buffer = true; } - return FALSE; + return false; } /* diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 19d97ecfef..4cb05ffc12 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -369,7 +369,6 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) { int colorindex; uint32_t fg_color; - char *color; pattr->bold = (highlight_has_attr(hl_id, HL_BOLD, modec) != NULL); pattr->italic = (highlight_has_attr(hl_id, HL_ITALIC, modec) != NULL); @@ -377,11 +376,12 @@ static void prt_get_attr(int hl_id, prt_text_attr_T *pattr, int modec) pattr->undercurl = (highlight_has_attr(hl_id, HL_UNDERCURL, modec) != NULL); { - color = (char *)highlight_color(hl_id, (char_u *)"fg", modec); - if (color == NULL) + const char *color = highlight_color(hl_id, "fg", modec); + if (color == NULL) { colorindex = 0; - else + } else { colorindex = atoi(color); + } if (colorindex >= 0 && colorindex < t_colors) fg_color = prt_get_term_color(colorindex); diff --git a/src/nvim/message.c b/src/nvim/message.c index bf54284881..423f5a27e7 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -718,7 +718,7 @@ int delete_first_msg(void) void ex_messages(void *const eap_p) FUNC_ATTR_NONNULL_ALL { - exarg_T *eap = (exarg_T *)eap_p; + const exarg_T *const eap = (const exarg_T *)eap_p; struct msg_hist *p; int c = 0; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index de6167c7fc..259dcc523c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -147,7 +147,7 @@ void channel_from_connection(SocketWatcher *watcher) /// @param name The event name, an arbitrary string /// @param args Array with event arguments /// @return True if the event was sent successfully, false otherwise. -bool channel_send_event(uint64_t id, char *name, Array args) +bool channel_send_event(uint64_t id, const char *name, Array args) { Channel *channel = NULL; @@ -160,7 +160,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) if (channel) { if (channel->pending_requests) { // Pending request, queue the notification for later sending. - String method = cstr_as_string(name); + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); kv_push(channel->delayed_notifications, buffer); } else { @@ -182,7 +182,7 @@ bool channel_send_event(uint64_t id, char *name, Array args) /// @param[out] error True if the return value is an error /// @return Whatever the remote method returned Object channel_send_call(uint64_t id, - char *method_name, + const char *method_name, Array args, Error *err) { @@ -519,10 +519,10 @@ static void send_error(Channel *channel, uint64_t id, char *err) static void send_request(Channel *channel, uint64_t id, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, id, method, @@ -532,10 +532,10 @@ static void send_request(Channel *channel, } static void send_event(Channel *channel, - char *name, + const char *name, Array args) { - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); channel_write(channel, serialize_request(channel->id, 0, method, @@ -544,7 +544,7 @@ static void send_event(Channel *channel, 1)); } -static void broadcast_event(char *name, Array args) +static void broadcast_event(const char *name, Array args) { kvec_t(Channel *) subscribed = KV_INITIAL_VALUE; Channel *channel; @@ -560,7 +560,7 @@ static void broadcast_event(char *name, Array args) goto end; } - String method = {.size = strlen(name), .data = name}; + const String method = cstr_as_string((char *)name); WBuffer *buffer = serialize_request(0, 0, method, @@ -728,7 +728,7 @@ static void call_set_error(Channel *channel, char *msg) static WBuffer *serialize_request(uint64_t channel_id, uint64_t request_id, - String method, + const String method, Array args, msgpack_sbuffer *sbuffer, size_t refcount) diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 5137b375f0..808bb863fd 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -322,7 +322,7 @@ void msgpack_rpc_from_float(Float result, msgpack_packer *res) msgpack_pack_double(res, result); } -void msgpack_rpc_from_string(String result, msgpack_packer *res) +void msgpack_rpc_from_string(const String result, msgpack_packer *res) FUNC_ATTR_NONNULL_ARG(2) { msgpack_pack_str(res, result.size); @@ -478,7 +478,7 @@ Object msgpack_rpc_handle_invalid_arguments(uint64_t channel_id, /// Serializes a msgpack-rpc request or notification(id == 0) void msgpack_rpc_serialize_request(uint64_t request_id, - String method, + const String method, Array args, msgpack_packer *pac) FUNC_ATTR_NONNULL_ARG(4) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 4cca5ec948..6ef929120e 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2075,7 +2075,6 @@ static void op_colon(oparg_T *oap) */ static void op_function(oparg_T *oap) { - char_u *(argv[1]); int save_virtual_op = virtual_op; if (*p_opfunc == NUL) @@ -2089,16 +2088,16 @@ static void op_function(oparg_T *oap) decl(&curbuf->b_op_end); } - if (oap->motion_type == kMTBlockWise) { - argv[0] = (char_u *)"block"; - } else if (oap->motion_type == kMTLineWise) { - argv[0] = (char_u *)"line"; - } else { - argv[0] = (char_u *)"char"; - } + const char_u *const argv[1] = { + (const char_u *)(((const char *const[]) { + [kMTBlockWise] = "block", + [kMTLineWise] = "line", + [kMTCharWise] = "char", + })[oap->motion_type]), + }; - /* Reset virtual_op so that 'virtualedit' can be changed in the - * function. */ + // Reset virtual_op so that 'virtualedit' can be changed in the + // function. virtual_op = MAYBE; (void)call_func_retnr(p_opfunc, 1, argv, false); @@ -4757,7 +4756,7 @@ static void nv_ident(cmdarg_T *cap) ptr = vim_strnsave(ptr, n); if (kp_ex) { // Escape the argument properly for an Ex command - p = vim_strsave_fnameescape(ptr, false); + p = (char_u *)vim_strsave_fnameescape((const char *)ptr, false); } else { // Escape the argument properly for a shell command p = vim_strsave_shellescape(ptr, true, true); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2beeae7ec6..0372bc8a8c 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -91,11 +91,11 @@ int os_dirname(char_u *buf, size_t len) /// Check if the given path is a directory and not a symlink to a directory. /// @return `true` if `name` is a directory and NOT a symlink to a directory. /// `false` if `name` is not a directory or if an error occurred. -bool os_isrealdir(const char_u *name) +bool os_isrealdir(const char *name) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - if (uv_fs_lstat(&fs_loop, &request, (char *)name, NULL) != kLibuvSuccess) { + if (uv_fs_lstat(&fs_loop, &request, name, NULL) != kLibuvSuccess) { return false; } if (S_ISLNK(request.statbuf.st_mode)) { @@ -111,7 +111,7 @@ bool os_isrealdir(const char_u *name) bool os_isdir(const char_u *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm(name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; } @@ -236,7 +236,8 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) pathext); #else // Must have path separator, cannot execute files in the current directory. - bool ok = gettail_dir(name) != name && is_executable((char *)name); + const bool ok = ((const char_u *)gettail_dir((const char *)name) != name + && is_executable((char *)name)); #endif if (ok) { if (abspath != NULL) { @@ -254,7 +255,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) static bool is_executable(const char *name) FUNC_ATTR_NONNULL_ALL { - int32_t mode = os_getperm((char_u *)name); + int32_t mode = os_getperm((const char *)name); if (mode < 0) { return false; @@ -606,11 +607,11 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// Get the file permissions for a given file. /// /// @return libuv error code on error. -int32_t os_getperm(const char_u *name) +int32_t os_getperm(const char *name) FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; - int stat_result = os_stat((char *)name, &statbuf); + int stat_result = os_stat(name, &statbuf); if (stat_result == kLibuvSuccess) { return (int32_t)statbuf.st_mode; } else { @@ -979,13 +980,13 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points /// to and return that name in allocated memory. /// Otherwise NULL is returned. -char *os_resolve_shortcut(char_u *fname) +char *os_resolve_shortcut(const char *fname) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; OLECHAR wsz[MAX_PATH]; char *rfname = NULL; - int len; IShellLinkW *pslw = NULL; WIN32_FIND_DATAW ffdw; @@ -994,7 +995,7 @@ char *os_resolve_shortcut(char_u *fname) if (fname == NULL) { return rfname; } - len = (int)STRLEN(fname); + const size_t len = strlen(fname); if (len <= 4 || STRNICMP(fname + len - 4, ".lnk", 4) != 0) { return rfname; } @@ -1006,7 +1007,7 @@ char *os_resolve_shortcut(char_u *fname) &IID_IShellLinkW, (void **)&pslw); if (hr == S_OK) { WCHAR *p; - int conversion_result = utf8_to_utf16((char *)fname, &p); + const int conversion_result = utf8_to_utf16(fname, &p); if (conversion_result != 0) { EMSG2("utf8_to_utf16 failed: %s", uv_strerror(conversion_result)); } @@ -1036,7 +1037,7 @@ char *os_resolve_shortcut(char_u *fname) ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR)); hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); if (hr == S_OK && wsz[0] != NUL) { - int conversion_result = utf16_to_utf8(wsz, &rfname); + const int conversion_result = utf16_to_utf8(wsz, &rfname); if (conversion_result != 0) { EMSG2("utf16_to_utf8 failed: %s", uv_strerror(conversion_result)); } diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index ed3410ffe5..acd86f06dc 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -110,14 +110,14 @@ void mch_copy_sec(char_u *from_file, char_u *to_file) // Return a pointer to the ACL of file "fname" in allocated memory. // Return NULL if the ACL is not available for whatever reason. -vim_acl_T mch_get_acl(char_u *fname) +vim_acl_T mch_get_acl(const char_u *fname) { vim_acl_T ret = NULL; return ret; } // Set the ACL of file "fname" to "acl" (unless it's NULL). -void mch_set_acl(char_u *fname, vim_acl_T aclent) +void mch_set_acl(const char_u *fname, vim_acl_T aclent) { if (aclent == NULL) return; diff --git a/src/nvim/path.c b/src/nvim/path.c index 2bd87b608e..e92261f4fd 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -159,7 +159,7 @@ const char_u *invocation_path_tail(const char_u *invocation, size_t *len) /// @param fname A file path. (Must be != NULL.) /// @return Pointer to first found path separator + 1. /// An empty string, if `fname` doesn't contain a path separator, -char_u *path_next_component(char_u *fname) +const char *path_next_component(const char *fname) { assert(fname != NULL); while (*fname != NUL && !vim_ispathsep(*fname)) { @@ -431,7 +431,7 @@ bool add_pathsep(char *p) /// /// @return [allocated] Copy of absolute path to `fname` or NULL when /// `fname` is NULL. -char *FullName_save(char *fname, bool force) +char *FullName_save(const char *fname, bool force) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { if (fname == NULL) { @@ -906,9 +906,9 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) in_curdir = xcalloc((size_t)gap->ga_len, sizeof(char_u *)); for (int i = 0; i < gap->ga_len && !got_int; i++) { - char_u *path = fnames[i]; + char_u *path = fnames[i]; int is_in_curdir; - char_u *dir_end = gettail_dir(path); + char_u *dir_end = (char_u *)gettail_dir((const char *)path); char_u *pathsep_p; char_u *path_cutoff; @@ -1010,18 +1010,22 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) ga_remove_duplicate_strings(gap); } -/// Return the end of the directory name, on the first path -/// separator: -/// "/path/file", "/path/dir/", "/path//dir", "/file" -/// ^ ^ ^ ^ -char_u *gettail_dir(const char_u *fname) +/// Find end of the directory name +/// +/// @param[in] fname File name to process. +/// +/// @return end of the directory name, on the first path separator: +/// +/// "/path/file", "/path/dir/", "/path//dir", "/file" +/// ^ ^ ^ ^ +const char *gettail_dir(const char *const fname) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - const char_u *dir_end = fname; - const char_u *next_dir_end = fname; + const char *dir_end = fname; + const char *next_dir_end = fname; bool look_for_sep = true; - const char_u *p; - for (p = fname; *p != NUL; ) { + for (const char *p = fname; *p != NUL; ) { if (vim_ispathsep(*p)) { if (look_for_sep) { next_dir_end = p; @@ -1034,7 +1038,7 @@ char_u *gettail_dir(const char_u *fname) } mb_ptr_adv(p); } - return (char_u *)dir_end; + return dir_end; } @@ -1553,8 +1557,8 @@ void simplify_filename(char_u *filename) p = tail; /* skip to char after ".." or "../" */ } } else { - ++components; /* simple path component */ - p = path_next_component(p); + components++; // Simple path component. + p = (char_u *)path_next_component((const char *)p); } } while (*p != NUL); } diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index 7670b64468..c72dafd08e 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -259,11 +259,11 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE]) /// /// @returns hex digest of "buf[buf_len]" in a static array. /// if "salt" is not NULL also do "salt[salt_len]". -char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, - const char_u *restrict salt, size_t salt_len) +const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, + const uint8_t *restrict salt, size_t salt_len) { char_u sha256sum[SHA256_SUM_SIZE]; - static char_u hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL + static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL context_sha256_T ctx; sha256_self_test(); @@ -277,7 +277,7 @@ char_u *sha256_bytes(const char_u *restrict buf, size_t buf_len, sha256_finish(&ctx, sha256sum); for (size_t j = 0; j < SHA256_SUM_SIZE; j++) { - snprintf((char *) hexit + j * SHA_STEP, SHA_STEP+1, "%02x", sha256sum[j]); + snprintf(hexit + j * SHA_STEP, SHA_STEP + 1, "%02x", sha256sum[j]); } hexit[sizeof(hexit) - 1] = '\0'; return hexit; @@ -308,7 +308,7 @@ bool sha256_self_test(void) context_sha256_T ctx; char_u buf[1000]; char_u sha256sum[SHA256_SUM_SIZE]; - char_u *hexit; + const char *hexit; static bool sha256_self_tested = false; static bool failures = false; @@ -320,8 +320,8 @@ bool sha256_self_test(void) for (size_t i = 0; i < 3; i++) { if (i < 2) { - hexit = sha256_bytes((char_u *) sha_self_test_msg[i], - STRLEN(sha_self_test_msg[i]), + hexit = sha256_bytes((uint8_t *)sha_self_test_msg[i], + strlen(sha_self_test_msg[i]), NULL, 0); STRCPY(output, hexit); } else { diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 2fe042cda8..0f04a5e9cf 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -82,8 +82,6 @@ KHASH_SET_INIT_STR(strset) (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) #define convert_setup(vcp, from, to) \ (convert_setup(vcp, (char_u *)from, (char_u *)to)) -#define os_getperm(f) \ - (os_getperm((char_u *) f)) #define os_isdir(f) (os_isdir((char_u *) f)) #define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) #define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 56f0350aef..84bee9b97f 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -3232,7 +3232,7 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) list_T *list; listitem_T *li; int score; - char_u *p; + const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. @@ -3244,9 +3244,10 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) if (li->li_tv.v_type == VAR_LIST) { // Get the word and the score from the items. score = get_spellword(li->li_tv.vval.v_list, &p); - if (score >= 0 && score <= su->su_maxscore) - add_suggestion(su, &su->su_ga, p, su->su_badlen, - score, 0, true, su->su_sallang, false); + if (score >= 0 && score <= su->su_maxscore) { + add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, + score, 0, true, su->su_sallang, false); + } } tv_list_unref(list); } @@ -5616,7 +5617,7 @@ static void add_suggestion ( suginfo_T *su, garray_T *gap, // either su_ga or su_sga - char_u *goodword, + const char_u *goodword, int badlenarg, // len of bad word replaced with "goodword" int score, int altscore, @@ -5630,13 +5631,11 @@ add_suggestion ( int badlen; // len of bad word changed suggest_T *stp; suggest_T new_sug; - int i; - char_u *pgood, *pbad; // Minimize "badlen" for consistency. Avoids that changing "the the" to // "thee the" is added next to changing the first "the" the "thee". - pgood = goodword + STRLEN(goodword); - pbad = su->su_badptr + badlenarg; + const char_u *pgood = goodword + STRLEN(goodword); + char_u *pbad = su->su_badptr + badlenarg; for (;; ) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); @@ -5656,9 +5655,10 @@ add_suggestion ( // the first "the" to itself. return; - if (GA_EMPTY(gap)) + int i; + if (GA_EMPTY(gap)) { i = -1; - else { + } else { // Check if the word is already there. Also check the length that is // being replaced "thes," -> "these" is a different suggestion from // "thes" -> "these". @@ -5857,27 +5857,31 @@ cleanup_suggestions ( return maxscore; } -// Soundfold a string, for soundfold(). -// Result is in allocated memory, NULL for an error. -char_u *eval_soundfold(char_u *word) +/// Soundfold a string, for soundfold() +/// +/// @param[in] word Word to soundfold. +/// +/// @return [allocated] soundfolded string or NULL in case of error. May return +/// copy of the input string if soundfolding is not +/// supported by any of the languages in &spellang. +char *eval_soundfold(const char *const word) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - langp_T *lp; - char_u sound[MAXWLEN]; - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { // Use the sound-folding of the first language that supports it. - for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { - lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); + for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) { + langp_T *const lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // soundfold the word - spell_soundfold(lp->lp_slang, word, false, sound); - return vim_strsave(sound); + char_u sound[MAXWLEN]; + spell_soundfold(lp->lp_slang, (char_u *)word, false, sound); + return xstrdup((const char *)sound); } } } // No language with sound folding, return word as-is. - return vim_strsave(word); + return xstrdup(word); } /// Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". diff --git a/src/nvim/strings.c b/src/nvim/strings.c index b964fed35a..9cfa126a56 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -291,30 +291,33 @@ void vim_strup(char_u *p) } } -/* - * Make string "s" all upper-case and return it in allocated memory. - * Handles multi-byte characters as well as possible. - */ -char_u *strup_save(const char_u *orig) +/// Make given string all upper-case +/// +/// Handels multi-byte characters as good as possible. +/// +/// @param[in] orig Input string. +/// +/// @return [allocated] upper-cased string. +char *strup_save(const char *const orig) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *res = vim_strsave(orig); + char *res = xstrdup(orig); - char_u *p = res; + char *p = res; while (*p != NUL) { int l; if (enc_utf8) { - int c = utf_ptr2char(p); + int c = utf_ptr2char((const char_u *)p); int uc = utf_toupper(c); - /* Reallocate string when byte count changes. This is rare, - * thus it's OK to do another malloc()/free(). */ - l = utf_ptr2len(p); + // Reallocate string when byte count changes. This is rare, + // thus it's OK to do another malloc()/free(). + l = utf_ptr2len((const char_u *)p); int newl = utf_char2len(uc); if (newl != l) { // TODO(philix): use xrealloc() in strup_save() - char_u *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); + char *s = xmalloc(STRLEN(res) + (size_t)(1 + newl - l)); memcpy(s, res, (size_t)(p - res)); STRCPY(s + (p - res) + newl, p + l); p = s + (p - res); @@ -322,12 +325,13 @@ char_u *strup_save(const char_u *orig) res = s; } - utf_char2bytes(uc, p); + utf_char2bytes(uc, (char_u *)p); p += newl; - } else if (has_mbyte && (l = (*mb_ptr2len)(p)) > 1) - p += l; /* skip multi-byte character */ - else { - *p = (char_u) TOUPPER_LOC(*p); // note that toupper() can be a macro + } else if (has_mbyte && (l = (*mb_ptr2len)((const char_u *)p)) > 1) { + p += l; // Skip multi-byte character. + } else { + // note that toupper() can be a macro + *p = (char)(uint8_t)TOUPPER_LOC(*p); p++; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 632a5bf2a5..3f84b8080f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6930,21 +6930,21 @@ static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg return didh; } -/* - * Return "1" if highlight group "id" has attribute "flag". - * Return NULL otherwise. - */ -char_u * -highlight_has_attr ( - int id, - int flag, - int modec // 'g' for GUI, 'c' for cterm -) +/// Check whether highlight group has attribute +/// +/// @param[in] id Highilght group to check. +/// @param[in] flag Attribute to check. +/// @param[in] modec 'g' for GUI, 'c' for term. +/// +/// @return "1" if highlight group has attribute, NULL otherwise. +const char *highlight_has_attr(const int id, const int flag, const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { int attr; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } if (modec == 'g') { attr = HL_TABLE()[id - 1].sg_gui; @@ -6952,39 +6952,42 @@ highlight_has_attr ( attr = HL_TABLE()[id - 1].sg_cterm; } - if (attr & flag) - return (char_u *)"1"; - return NULL; + return (attr & flag) ? "1" : NULL; } -/* - * Return color name of highlight group "id". - */ -char_u * -highlight_color ( - int id, - char_u *what, /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */ - int modec /* 'g' for GUI, 'c' for cterm, 't' for term */ -) +/// Return color name of the given highlight group +/// +/// @param[in] id Highlight group to work with. +/// @param[in] what What to return: one of "font", "fg", "bg", "sp", "fg#", +/// "bg#" or "sp#". +/// @param[in] modec 'g' for GUI, 'c' for cterm and 't' for term. +/// +/// @return color name, possibly in a static buffer. Buffer will be overwritten +/// on next highlight_color() call. May return NULL. +const char *highlight_color(const int id, const char *const what, + const int modec) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - static char_u name[20]; + static char name[20]; int n; - int fg = FALSE; - int sp = FALSE; - int font = FALSE; + bool fg = false; + bool sp = false; + bool font = false; - if (id <= 0 || id > highlight_ga.ga_len) + if (id <= 0 || id > highlight_ga.ga_len) { return NULL; + } - if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') - fg = TRUE; - else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' - && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') - font = TRUE; - else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') - sp = TRUE; - else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) + if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g') { + fg = true; + } else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o' + && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't') { + font = true; + } else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p') { + sp = true; + } else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g')) { return NULL; + } if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { @@ -6997,19 +7000,20 @@ highlight_color ( if (n < 0 || n > 0xffffff) { return NULL; } - snprintf((char *)name, sizeof(name), "#%06x", n); + snprintf(name, sizeof(name), "#%06x", n); return name; } if (fg) { - return HL_TABLE()[id - 1].sg_rgb_fg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; } if (sp) { - return HL_TABLE()[id - 1].sg_rgb_sp_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; } - return HL_TABLE()[id - 1].sg_rgb_bg_name; + return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } - if (font || sp) + if (font || sp) { return NULL; + } if (modec == 'c') { if (fg) { n = HL_TABLE()[id - 1].sg_cterm_fg - 1; @@ -7019,10 +7023,10 @@ highlight_color ( if (n < 0) { return NULL; } - snprintf((char *)name, sizeof(name), "%d", n); + snprintf(name, sizeof(name), "%d", n); return name; } - /* term doesn't have color */ + // term doesn't have color. return NULL; } @@ -7133,7 +7137,7 @@ int syn_name2id(const char_u *name) /* * Return TRUE if highlight group "name" exists. */ -int highlight_exists(char_u *name) +int highlight_exists(const char_u *name) { return syn_name2id(name) > 0; } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index ba687bf6da..010ef2c8fa 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1081,7 +1081,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, */ perm = 0600; if (buf->b_ffname != NULL) { - perm = os_getperm(buf->b_ffname); + perm = os_getperm((const char *)buf->b_ffname); if (perm < 0) { perm = 0600; } diff --git a/src/nvim/version.c b/src/nvim/version.c index dd583f6ffd..f1d39b8492 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -73,7 +73,7 @@ static char *features[] = { }; // clang-format off -static int included_patches[] = { +static const int included_patches[] = { // 2367,NA // 2366 NA // 2365 NA @@ -2461,10 +2461,10 @@ static char *(extra_patches[]) = { /// @param version Version string like "1.3.42" /// /// @return true if Nvim is at or above the version. -bool has_nvim_version(char *version_str) - FUNC_ATTR_NONNULL_ALL +bool has_nvim_version(const char *const version_str) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char *p = version_str; + const char *p = version_str; int major = 0; int minor = 0; int patch = 0; @@ -2473,7 +2473,7 @@ bool has_nvim_version(char *version_str) return false; } major = atoi(p); - p = strchr(p, '.'); // Find the next dot. + p = strchr(p, '.'); // Find the next dot. if (p) { p++; // Advance past the dot. @@ -2481,7 +2481,7 @@ bool has_nvim_version(char *version_str) return false; } minor = atoi(p); - p = strchr(p, '.'); + p = strchr(p, '.'); if (p) { p++; if (!ascii_isdigit(*p)) { -- cgit From 949f09bdbba592a12629c71e20ff7bb49a21db6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 21 Aug 2016 08:47:56 +0300 Subject: eval: Move get_tv_string_buf() to eval/typval.c --- src/nvim/eval.c | 184 +++++++++++++++++++++++------------------------ src/nvim/eval/executor.c | 3 +- src/nvim/eval/typval.c | 36 ++++++++-- src/nvim/ex_docmd.c | 13 +++- 4 files changed, 130 insertions(+), 106 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7e40f5e828..d26ff5e41d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -3531,8 +3531,6 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) int type_is = FALSE; /* TRUE for "is" and "isnot" */ int len = 2; long n1, n2; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; int ic; /* @@ -3737,31 +3735,33 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) case TYPE_NOMATCH: break; /* avoid gcc warning */ } } else { - s1 = get_tv_string_buf(rettv, buf1); - s2 = get_tv_string_buf(&var2, buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = tv_get_string_buf(&var2, buf2); if (type != TYPE_MATCH && type != TYPE_NOMATCH) { - i = mb_strcmp_ic((bool)ic, (const char *)s1, (const char *)s2); + i = mb_strcmp_ic((bool)ic, s1, s2); } else { i = 0; } n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (i == 0); break; - case TYPE_NEQUAL: n1 = (i != 0); break; - case TYPE_GREATER: n1 = (i > 0); break; - case TYPE_GEQUAL: n1 = (i >= 0); break; - case TYPE_SMALLER: n1 = (i < 0); break; - case TYPE_SEQUAL: n1 = (i <= 0); break; - - case TYPE_MATCH: - case TYPE_NOMATCH: - n1 = pattern_match(s2, s1, ic); - if (type == TYPE_NOMATCH) { - n1 = !n1; + case TYPE_EQUAL: n1 = (i == 0); break; + case TYPE_NEQUAL: n1 = (i != 0); break; + case TYPE_GREATER: n1 = (i > 0); break; + case TYPE_GEQUAL: n1 = (i >= 0); break; + case TYPE_SMALLER: n1 = (i < 0); break; + case TYPE_SEQUAL: n1 = (i <= 0); break; + + case TYPE_MATCH: + case TYPE_NOMATCH: { + n1 = pattern_match((char_u *)s2, (char_u *)s1, ic); + if (type == TYPE_NOMATCH) { + n1 = !n1; + } + break; } - break; - - case TYPE_UNKNOWN: break; /* avoid gcc warning */ + case TYPE_UNKNOWN: break; // Avoid gcc warning. } } tv_clear(rettv); @@ -3794,8 +3794,6 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) int op; long n1, n2; float_T f1 = 0, f2 = 0; - char_u *s1, *s2; - char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN]; char_u *p; /* @@ -3842,14 +3840,17 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (op == '.') { - s1 = get_tv_string_buf(rettv, buf1); /* already checked */ - s2 = get_tv_string_buf_chk(&var2, buf2); - if (s2 == NULL) { // Type error ? + char buf1[NUMBUFLEN]; + char_u buf2[NUMBUFLEN]; + // s1 already checked + const char *const s1 = tv_get_string_buf(rettv, buf1); + const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); return FAIL; } - p = concat_str(s1, s2); + p = concat_str((const char_u *)s1, (const char_u *)s2); tv_clear(rettv); rettv->v_type = VAR_STRING; rettv->vval.v_string = p; @@ -7565,20 +7566,21 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int num = 0; - char_u *dbpath = NULL; - char_u *prepend = NULL; - char_u buf[NUMBUFLEN]; + const char *dbpath = NULL; + const char *prepend = NULL; + char buf[NUMBUFLEN]; if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { num = (int)get_tv_number(&argvars[0]); - dbpath = (char_u *)tv_get_string(&argvars[1]); + dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { - prepend = get_tv_string_buf(&argvars[2], buf); + prepend = tv_get_string_buf(&argvars[2], buf); } } - rettv->vval.v_number = cs_connection(num, dbpath, prepend); + rettv->vval.v_number = cs_connection(num, (char_u *)dbpath, + (char_u *)prepend); } /// "cursor(lnum, col)" function, or @@ -7661,9 +7663,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "delete()" function static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - char_u *flags; - rettv->vval.v_number = -1; if (check_restricted() || check_secure()) { return; @@ -7675,19 +7674,21 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + char nbuf[NUMBUFLEN]; + const char *flags; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } else { - flags = (char_u *)""; + flags = ""; } if (*flags == NUL) { // delete a file rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "d") == 0) { + } else if (strcmp(flags, "d") == 0) { // delete an empty directory rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; - } else if (STRCMP(flags, "rf") == 0) { + } else if (strcmp(flags, "rf") == 0) { // delete a directory recursively rettv->vval.v_number = delete_recursive(name); } else { @@ -7905,11 +7906,11 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; rettv->vval.v_string = vim_strsave_escaped( (const char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string_buf(&argvars[1], buf)); rettv->v_type = VAR_STRING; } @@ -8253,19 +8254,18 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - - /* This is not allowed in the sandbox. If the commands would still be - * executed in the sandbox it would be OK, but it probably happens later, - * when "sandbox" is no longer set. */ - if (check_secure()) + // This is not allowed in the sandbox. If the commands would still be + // executed in the sandbox it would be OK, but it probably happens later, + // when "sandbox" is no longer set. + if (check_secure()) { return; + } const char *const keys = tv_get_string(&argvars[0]); - + char nbuf[NUMBUFLEN]; const char *flags = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - flags = (const char *)get_tv_string_buf(&argvars[1], nbuf); + flags = tv_get_string_buf(&argvars[1], nbuf); } nvim_feedkeys(cstr_as_string((char *)keys), @@ -10740,11 +10740,11 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *mode; const char *const name = tv_get_string(&argvars[0]); bool abbr = false; - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; if (argvars[1].v_type == VAR_UNKNOWN) { mode = "nvo"; } else { - mode = (const char *)get_tv_string_buf(&argvars[1], buf); + mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = get_tv_number(&argvars[2]); } @@ -10763,17 +10763,16 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType histype; - char_u *str; - char_u buf[NUMBUFLEN]; rettv->vval.v_number = false; if (check_restricted() || check_secure()) { return; } - str = get_tv_string_chk(&argvars[0]); // NULL on type error + char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { - str = get_tv_string_buf(&argvars[1], buf); + char buf[NUMBUFLEN]; + str = (char_u *)tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); add_to_history(histype, str, false, NUL); @@ -10789,7 +10788,6 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u buf[NUMBUFLEN]; char_u *str; str = get_tv_string_chk(&argvars[0]); // NULL on type error @@ -10801,11 +10799,12 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int) get_tv_number(&argvars[1])); + (int)get_tv_number(&argvars[1])); } else { // string given: remove all matching entries + char buf[NUMBUFLEN]; n = del_history_entry(get_histtype(str, STRLEN(str), false), - get_tv_string_buf(&argvars[1], buf)); + (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; } @@ -10895,12 +10894,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const str = tv_get_string(&argvars[0]); - char_u buf1[NUMBUFLEN]; + char buf1[NUMBUFLEN]; char_u *const from = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[1], buf1))); - char_u buf2[NUMBUFLEN]; + (char_u *)tv_get_string_buf(&argvars[1], buf1))); + char buf2[NUMBUFLEN]; char_u *const to = enc_canonize(enc_skip( - get_tv_string_buf(&argvars[2], buf2))); + (char_u *)tv_get_string_buf(&argvars[2], buf2))); vimconv.vc_type = CONV_NONE; convert_setup(&vimconv, from, to); @@ -10983,7 +10982,6 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) char_u *prompt = get_tv_string_chk(&argvars[0]); char_u *p = NULL; int c; - char_u buf[NUMBUFLEN]; int cmd_silent_save = cmd_silent; char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; @@ -11013,9 +11011,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmdline_row = msg_row; if (argvars[1].v_type != VAR_UNKNOWN) { + char_u buf[NUMBUFLEN]; defstr = get_tv_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) + if (defstr != NULL) { stuffReadbuffSpec(defstr); + } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { char_u *xp_name; @@ -11026,8 +11026,9 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) rettv->vval.v_string = NULL; xp_name = get_tv_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) + if (xp_name == NULL) { return; + } xp_namelen = (int)STRLEN(xp_name); @@ -11047,9 +11048,11 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) } if (inputdialog && rettv->vval.v_string == NULL && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(get_tv_string_buf( - &argvars[2], buf)); + && argvars[2].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + rettv->vval.v_string = (char_u *)xstrdup(tv_get_string_buf( + &argvars[2], buf)); + } xfree(xp_arg); @@ -12535,21 +12538,21 @@ static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *dir; - char_u buf[NUMBUFLEN]; int prot = 0755; rettv->vval.v_number = FAIL; if (check_restricted() || check_secure()) return; - dir = get_tv_string_buf(&argvars[0], buf); - if (*dir == NUL) + char buf[NUMBUFLEN]; + const char *const dir = tv_get_string_buf(&argvars[0], buf); + if (*dir == NUL) { rettv->vval.v_number = FAIL; - else { - if (*path_tail(dir) == NUL) - /* remove trailing slashes */ - *path_tail_with_sep(dir) = NUL; + } else { + if (*path_tail((char_u *)dir) == NUL) { + // Remove trailing slashes. + *path_tail_with_sep((char_u *)dir) = NUL; + } if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { @@ -12557,7 +12560,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; - int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); + int ret = os_mkdir_recurse(dir, prot, &failed_dir); if (ret != 0) { EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); xfree(failed_dir); @@ -12840,14 +12843,13 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; { - char_u buf[NUMBUFLEN]; int len; int saved_did_emsg = did_emsg; - char *fmt; - /* Get the required length, allocate the buffer and do it for real. */ - did_emsg = FALSE; - fmt = (char *)get_tv_string_buf(&argvars[0], buf); + // Get the required length, allocate the buffer and do it for real. + did_emsg = false; + char buf[NUMBUFLEN]; + const char *fmt = tv_get_string_buf(&argvars[0], buf); len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { char *s = xmalloc(len + 1); @@ -13270,14 +13272,13 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - if (check_restricted() || check_secure()) { rettv->vval.v_number = -1; } else { + char buf[NUMBUFLEN]; rettv->vval.v_number = vim_rename( - (char_u *)tv_get_string(&argvars[0]), - get_tv_string_buf(&argvars[1], buf)); + (const char_u *)tv_get_string(&argvars[0]), + (const char_u *)tv_get_string_buf(&argvars[1], buf)); } } @@ -16003,8 +16004,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int modec; if (argvars[2].v_type != VAR_UNKNOWN) { char modebuf[NUMBUFLEN]; - const char *const mode = (const char *)get_tv_string_buf(&argvars[2], - (char_u *)modebuf); + const char *const mode = tv_get_string_buf(&argvars[2], modebuf); modec = TOLOWER_ASC(mode[0]); if (modec != 'c' && modec != 'g') { modec = 0; // Replace invalid with current. @@ -18530,14 +18530,6 @@ static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) // TODO(ZyX-I): move to eval/typval -char_u *get_tv_string_buf(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT -{ - char_u *const res = get_tv_string_buf_chk(varp, buf); - - return res != NULL ? res : (char_u *)""; -} - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 41b55e4a57..d2d0873792 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -80,7 +80,8 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *tvs = tv_get_string(tv1); char numbuf[NUMBUFLEN]; char *const s = (char *)concat_str( - (const char_u *)tvs, get_tv_string_buf(tv2, (char_u *)numbuf)); + (const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2, + numbuf)); tv_clear(tv1); tv1->v_type = VAR_STRING; tv1->vval.v_string = (char_u *)s; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index cbeb2e059c..2c2d0ecaab 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1127,7 +1127,7 @@ const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, if (di == NULL) { return NULL; } - return (const char *)get_tv_string_buf(&di->di_tv, (char_u *)numbuf); + return tv_get_string_buf(&di->di_tv, numbuf); } /// Get a function from a dictionary @@ -1948,8 +1948,8 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, case VAR_STRING: { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; - const char *s1 = (const char *)get_tv_string_buf(tv1, (char_u *)buf1); - const char *s2 = (const char *)get_tv_string_buf(tv2, (char_u *)buf2); + const char *s1 = tv_get_string_buf(tv1, buf1); + const char *s2 = tv_get_string_buf(tv2, buf2); return mb_strcmp_ic((bool)ic, s1, s2) == 0; } case VAR_SPECIAL: { @@ -2021,7 +2021,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use -/// get_tv_string_buf() if you need to use tv_get_string() output after +/// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but @@ -2035,6 +2035,30 @@ bool tv_check_str_or_nr(const typval_T *const tv) const char *tv_get_string(const typval_T *const varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - static char_u mybuf[NUMBUFLEN]; - return (const char *)get_tv_string_buf((typval_T *)varp, mybuf); + static char mybuf[NUMBUFLEN]; + return tv_get_string_buf((typval_T *)varp, mybuf); +} + +/// Get the string value of a variable +/// +/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// return NULL on error. +/// +/// @param[in] varp Varible to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Variable value if it is VAR_STRING variable, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty +/// string. +const char *tv_get_string_buf(const typval_T *const varp, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT +{ + const char *const res = (const char *)get_tv_string_buf_chk( + (typval_T *)varp, (char_u *)buf); + + return res != NULL ? res : ""; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 5209dfc451..6b661cff11 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7724,7 +7724,7 @@ static void ex_mkrc(exarg_T *eap) /* When using 'viewdir' may have to create the directory. */ if (using_vdir && !os_isdir(p_vdir)) { - vim_mkdir_emsg(p_vdir, 0755); + vim_mkdir_emsg((const char *)p_vdir, 0755); } fd = open_exfile((char_u *) fname, eap->forceit, WRITEBIN); @@ -7836,10 +7836,17 @@ static void ex_mkrc(exarg_T *eap) xfree(viewFile); } -int vim_mkdir_emsg(char_u *name, int prot) +/// Try creating a directory, give error message on failure +/// +/// @param[in] name Directory to create. +/// @param[in] prot Directory permissions. +/// +/// @return OK in case of success, FAIL otherwise. +int vim_mkdir_emsg(const char *const name, const int prot) + FUNC_ATTR_NONNULL_ALL { int ret; - if ((ret = os_mkdir((char *)name, prot)) != 0) { + if ((ret = os_mkdir(name, prot)) != 0) { EMSG3(_(e_mkdir), name, os_strerror(ret)); return FAIL; } -- cgit From 7ee5cc7429017a4a08a8b62b628bea156fea0008 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:09:29 +0300 Subject: eval: Move get_tv_lnum and get_tv_float to eval/typval.h Additionally - Rename former tv_get_float to tv_get_float_chk due to name conflict (former get_tv_float is better suited for being tv_get_float). - Add E907 error to get_tv_float() and test that it is being raised when appropriate. --- src/nvim/eval.c | 261 +++++++++++++++---------------------- src/nvim/eval/typval.c | 94 +++++++++++-- src/nvim/eval/typval.h | 9 +- test/functional/eval/sort_spec.lua | 41 ++++++ 4 files changed, 234 insertions(+), 171 deletions(-) create mode 100644 test/functional/eval/sort_spec.lua diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d26ff5e41d..6307dd8abc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6498,7 +6498,7 @@ static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T (*function)(float_T) = (float_T (*)(float_T))fptr; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { rettv->vval.v_float = function(f); } else { rettv->vval.v_float = 0.0; @@ -6610,7 +6610,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) u_sync(TRUE); } - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 0 && lnum <= curbuf->b_ml.ml_line_count && u_save(lnum, lnum + 1) == OK) { @@ -6957,7 +6957,7 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = atan2(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -7333,7 +7333,7 @@ static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) linenr_T lnum; pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_c_indent(); @@ -7613,7 +7613,7 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) set_curswant = false; } } else { - line = get_tv_lnum(argvars); + line = tv_get_lnum(argvars); col = get_tv_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { coladd = get_tv_number_chk(&argvars[2], NULL); @@ -7808,7 +7808,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, get_tv_lnum(argvars)); + rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); } /* @@ -7816,7 +7816,7 @@ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum = get_tv_lnum(argvars); + linenr_T lnum = tv_get_lnum(argvars); static linenr_T prev_lnum = 0; static int changedtick = 0; static int fnum = 0; @@ -8557,7 +8557,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - if (tv_get_float(argvars, &f)) { + if (tv_get_float_chk(argvars, &f)) { if (f < VARNUMBER_MIN) { rettv->vval.v_number = VARNUMBER_MIN; } else if (f > VARNUMBER_MAX) { @@ -8577,7 +8577,7 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = fmod(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -8629,16 +8629,16 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end) { - linenr_T lnum; - linenr_T first, last; - - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { - if (hasFoldingWin(curwin, lnum, &first, &last, FALSE, NULL)) { - if (end) + linenr_T first; + linenr_T last; + if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) { + if (end) { rettv->vval.v_number = (varnumber_T)last; - else + } else { rettv->vval.v_number = (varnumber_T)first; + } return; } } @@ -8666,11 +8666,10 @@ static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = foldLevel(lnum); + } } /* @@ -8734,7 +8733,6 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *text; char_u buf[51]; foldinfo_T foldinfo; @@ -8742,10 +8740,11 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - lnum = get_tv_lnum(argvars); - /* treat illegal types and illegal string values for {lnum} the same */ - if (lnum < 0) + linenr_T lnum = tv_get_lnum(argvars); + // Treat illegal types and illegal string values for {lnum} the same. + if (lnum < 0) { lnum = 0; + } fold_count = foldedCount(curwin, lnum, &foldinfo); if (fold_count > 0) { text = get_foldtext(curwin, lnum, lnum + fold_count - 1, @@ -9782,17 +9781,16 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; linenr_T end; - int retlist; + bool retlist; - lnum = get_tv_lnum(argvars); + const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { end = 0; - retlist = FALSE; + retlist = false; } else { - end = get_tv_lnum(&argvars[1]); - retlist = TRUE; + end = tv_get_lnum(&argvars[1]); + retlist = true; } get_buffer_lines(curbuf, lnum, end, retlist, rettv); @@ -10920,13 +10918,12 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { rettv->vval.v_number = get_indent_lnum(lnum); - else + } else { rettv->vval.v_number = -1; + } } /* @@ -11975,15 +11972,15 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + const linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = ml_find_line_or_offset(curbuf, lnum, NULL); - if (rettv->vval.v_number >= 0) - ++rettv->vval.v_number; + } + if (rettv->vval.v_number >= 0) { + rettv->vval.v_number++; + } } /* @@ -11991,17 +11988,15 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos; - linenr_T lnum; - - pos = curwin->w_cursor; - lnum = get_tv_lnum(argvars); + const pos_T pos = curwin->w_cursor; + const linenr_T lnum = tv_get_lnum(argvars); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) { curwin->w_cursor.lnum = lnum; rettv->vval.v_number = get_lisp_indent(); curwin->w_cursor = pos; - } else + } else { rettv->vval.v_number = -1; + } } /* @@ -12746,13 +12741,14 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { linenr_T lnum; - for (lnum = get_tv_lnum(argvars);; ++lnum) { + for (lnum = tv_get_lnum(argvars);; lnum++) { if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; break; } - if (*skipwhite(ml_get(lnum)) != NUL) + if (*skipwhite(ml_get(lnum)) != NUL) { break; + } } rettv->vval.v_number = lnum; } @@ -12812,7 +12808,7 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) float_T fy; rettv->v_type = VAR_FLOAT; - if (tv_get_float(argvars, &fx) && tv_get_float(&argvars[1], &fy)) { + if (tv_get_float_chk(argvars, &fx) && tv_get_float_chk(&argvars[1], &fy)) { rettv->vval.v_float = pow(fx, fy); } else { rettv->vval.v_float = 0.0; @@ -12824,14 +12820,14 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - - lnum = get_tv_lnum(argvars); - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) + linenr_T lnum = tv_get_lnum(argvars); + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { lnum = 0; - else - while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) - --lnum; + } else { + while (lnum >= 1 && *skipwhite(ml_get(lnum)) == NUL) { + lnum--; + } + } rettv->vval.v_number = lnum; } @@ -14428,14 +14424,13 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; - lnum = get_tv_lnum(&argvars[0]); + linenr_T lnum = tv_get_lnum(&argvars[0]); if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; @@ -15017,8 +15012,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } if (sortinfo->item_compare_float) { - float_T v1 = get_tv_float(tv1); - float_T v2 = get_tv_float(tv2); + const float_T v1 = tv_get_float(tv1); + const float_T v2 = tv_get_float(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15977,19 +15972,18 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id = 0; - long lnum; - long col; - int trans; - bool transerr = false; + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ - trans = get_tv_number_chk(&argvars[2], &transerr); + bool transerr = false; + const int trans = get_tv_number_chk(&argvars[2], &transerr); + int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count - && col >= 0 && col < (long)STRLEN(ml_get(lnum))) - id = syn_get_id(curwin, lnum, (colnr_T)col, trans, NULL, FALSE); + && col >= 0 && (size_t)col < STRLEN(ml_get(lnum))) { + id = syn_get_id(curwin, lnum, col, trans, NULL, false); + } rettv->vval.v_number = id; } @@ -16090,8 +16084,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; int syntax_flags = 0; int cchar; int matchid = 0; @@ -16100,15 +16092,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, FALSE, NULL, FALSE); + && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { + (void)syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character @@ -16137,21 +16130,19 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long lnum; - long col; - rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; - lnum = get_tv_lnum(argvars); /* -1 on type error */ - col = get_tv_number(&argvars[1]) - 1; /* -1 on type error */ + // -1 on type error (both) + const linenr_T lnum = tv_get_lnum(argvars); + const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 - && col <= (long)STRLEN(ml_get(lnum))) { + && (size_t)col <= STRLEN(ml_get(lnum))) { tv_list_alloc_ret(rettv); - (void)syn_get_id(curwin, lnum, (colnr_T)col, false, NULL, true); + (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -17457,31 +17448,35 @@ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Translate a String variable into a position. - * Returns NULL when there is an error. - */ -static pos_T * -var2fpos ( - typval_T *varp, - int dollar_lnum, /* TRUE when $ is last line */ - int *fnum /* set to fnum for '0, 'A, etc. */ -) +/// Translate a VimL object into a position +/// +/// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid +/// type. +/// +/// @param[in] tv Object to translate. +/// @param[in] dollar_lnum True when "$" is last line. +/// @param[out] ret_fnum Set to fnum for marks. +/// +/// @return Pointer to position or NULL in case of error (e.g. invalid type). +pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, + int *const ret_fnum) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { char_u *name; static pos_T pos; pos_T *pp; - /* Argument can be [lnum, col, coladd]. */ - if (varp->v_type == VAR_LIST) { + // Argument can be [lnum, col, coladd]. + if (tv->v_type == VAR_LIST) { list_T *l; int len; bool error = false; listitem_T *li; - l = varp->vval.v_list; - if (l == NULL) + l = tv->vval.v_list; + if (l == NULL) { return NULL; + } // Get the line number. pos.lnum = tv_list_find_nr(l, 0L, &error); @@ -17521,20 +17516,24 @@ var2fpos ( return &pos; } - name = get_tv_string_chk(varp); - if (name == NULL) + name = get_tv_string_chk(tv); + if (name == NULL) { return NULL; - if (name[0] == '.') /* cursor */ + } + if (name[0] == '.') { // Cursor. return &curwin->w_cursor; - if (name[0] == 'v' && name[1] == NUL) { /* Visual start */ - if (VIsual_active) + } + if (name[0] == 'v' && name[1] == NUL) { // Visual start. + if (VIsual_active) { return &VIsual; + } return &curwin->w_cursor; } - if (name[0] == '\'') { /* mark */ - pp = getmark_buf_fnum(curbuf, name[1], FALSE, fnum); - if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) + if (name[0] == '\'') { // Mark. + pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; + } return pp; } @@ -18465,54 +18464,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -static float_T get_tv_float(typval_T *varp) -{ - switch (varp->v_type) { - case VAR_NUMBER: - return (float_T)(varp->vval.v_number); - case VAR_FLOAT: - return varp->vval.v_float; - break; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E891: Using a Funcref as a Float")); - break; - case VAR_STRING: - EMSG(_("E892: Using a String as a Float")); - break; - case VAR_LIST: - EMSG(_("E893: Using a List as a Float")); - break; - case VAR_DICT: - EMSG(_("E894: Using a Dictionary as a Float")); - break; - default: - EMSG2(_(e_intern2), "get_tv_float()"); - break; - } - return 0; -} - -/* - * Get the lnum from the first argument. - * Also accepts ".", "$", etc., but that only works for the current buffer. - * Returns -1 on error. - */ -static linenr_T get_tv_lnum(typval_T *argvars) -{ - typval_T rettv; - linenr_T lnum; - - lnum = get_tv_number_chk(&argvars[0], NULL); - if (lnum == 0) { /* no valid number, try using line() */ - rettv.v_type = VAR_NUMBER; - f_line(argvars, &rettv, NULL); - lnum = rettv.vval.v_number; - tv_clear(&rettv); - } - return lnum; -} - /* * Get the lnum from the first argument. * Also accepts "$", then "buf" is used. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2c2d0ecaab..6a16b3869a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -16,6 +16,7 @@ #include "nvim/hashtab.h" #include "nvim/vim.h" #include "nvim/ascii.h" +#include "nvim/pos.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -1730,11 +1731,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// -/// @param[in,out] varp Value to free. -void tv_clear(typval_T *varp) +/// @param[in,out] tv Value to free. +void tv_clear(typval_T *tv) { - if (varp != NULL && varp->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(varp, varp, "tv_clear argument"); + if (tv != NULL && tv->v_type != VAR_UNKNOWN) { + const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); (void)evn_ret; assert(evn_ret == OK); } @@ -2017,7 +2018,72 @@ bool tv_check_str_or_nr(const typval_T *const tv) //{{{2 Get -/// Get the string value of a variable +/// Get the line number from VimL object +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string like ".", "$", … (works with current buffer +/// only). +/// +/// @return Line number or -1 or 0. +linenr_T tv_get_lnum(const typval_T *const tv) +{ + linenr_T lnum = get_tv_number_chk(tv, NULL); + if (lnum == 0) { // No valid number, try using same function as line() does. + int fnum; + pos_T *const fp = var2fpos(tv, true, &fnum); + if (fp != NULL) { + lnum = fp->lnum; + } + } + return lnum; +} + +/// Get the floating-point value of a VimL object +/// +/// Raises an error if object is not number or floating-point. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Floating-point value of the variable or zero. +float_T tv_get_float(const typval_T *const tv) +{ + switch (tv->v_type) { + case VAR_NUMBER: { + return (float_T)(tv->vval.v_number); + } + case VAR_FLOAT: { + return tv->vval.v_float; + } + case VAR_PARTIAL: + case VAR_FUNC: { + EMSG(_("E891: Using a Funcref as a Float")); + break; + } + case VAR_STRING: { + EMSG(_("E892: Using a String as a Float")); + break; + } + case VAR_LIST: { + EMSG(_("E893: Using a List as a Float")); + break; + } + case VAR_DICT: { + EMSG(_("E894: Using a Dictionary as a Float")); + break; + } + case VAR_SPECIAL: { + EMSG(_("E907: Using a special value as a Float")); + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "get_tv_float()"); + break; + } + } + return 0; +} + +/// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to get_tv_string may reuse it. Use @@ -2027,38 +2093,38 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string(const typval_T *const varp) +const char *tv_get_string(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { static char mybuf[NUMBUFLEN]; - return tv_get_string_buf((typval_T *)varp, mybuf); + return tv_get_string_buf((typval_T *)tv, mybuf); } -/// Get the string value of a variable +/// Get the string value of a VimL object /// /// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but /// return NULL on error. /// -/// @param[in] varp Varible to get value of. +/// @param[in] tv Object to get value of. /// @param buf Buffer used to hold numbers and special variables converted to /// string. When function encounters one of these stringified value /// will be written to buf and buf will be returned. /// /// Buffer must have NUMBUFLEN size. /// -/// @return Variable value if it is VAR_STRING variable, number converted to +/// @return Object value if it is VAR_STRING object, number converted to /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty /// string. -const char *tv_get_string_buf(const typval_T *const varp, char *const buf) +const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)varp, (char_u *)buf); + (typval_T *)tv, (char_u *)buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 5645772124..3c294b45b8 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -14,6 +14,7 @@ #include "nvim/profile.h" // for proftime_T #include "nvim/pos.h" // for linenr_T #include "nvim/gettext.h" +#include "nvim/message.h" /// Type used for VimL VAR_NUMBER values typedef int varnumber_T; @@ -373,7 +374,8 @@ extern bool tv_in_free_unref_items; } \ }) -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) REAL_FATTR_NONNULL_ALL REAL_FATTR_WARN_UNUSED_RESULT; // FIXME circular dependency, cannot import message.h. @@ -381,11 +383,14 @@ bool emsgf(const char *const fmt, ...); /// Get the float value /// +/// Raises an error if object is not number or floating-point. +/// /// @param[in] tv VimL object to get value from. /// @param[out] ret_f Location where resulting float is stored. /// /// @return true in case of success, false if tv is not a number or float. -static inline bool tv_get_float(const typval_T *const tv, float_T *const ret_f) +static inline bool tv_get_float_chk(const typval_T *const tv, + float_T *const ret_f) { if (tv->v_type == VAR_FLOAT) { *ret_f = tv->vval.v_float; diff --git a/test/functional/eval/sort_spec.lua b/test/functional/eval/sort_spec.lua new file mode 100644 index 0000000000..4e5a0afba4 --- /dev/null +++ b/test/functional/eval/sort_spec.lua @@ -0,0 +1,41 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local NIL = helpers.NIL +local eval = helpers.eval +local clear = helpers.clear +local meths = helpers.meths +local funcs = helpers.funcs +local command = helpers.command +local exc_exec = helpers.exc_exec + +before_each(clear) + +describe('sort()', function() + it('errors out when sorting special values', function() + eq('Vim(call):E907: Using a special value as a Float', + exc_exec('call sort([v:true, v:false], "f")')) + end) + + it('sorts “wrong” values between -0.0001 and 0.0001, preserving order', + function() + meths.set_var('list', {true, false, NIL, {}, {a=42}, 'check', + 0.0001, -0.0001}) + command('call insert(g:list, function("tr"))') + local error_lines = funcs.split( + funcs.execute('silent! call sort(g:list, "f")'), '\n') + local errors = {} + for _, err in ipairs(error_lines) do + errors[err] = true + end + eq({ + ['E891: Using a Funcref as a Float']=true, + ['E892: Using a String as a Float']=true, + ['E893: Using a List as a Float']=true, + ['E894: Using a Dictionary as a Float']=true, + ['E907: Using a special value as a Float']=true, + }, errors) + eq('[-1.0e-4, function(\'tr\'), v:true, v:false, v:null, [], {\'a\': 42}, \'check\', 1.0e-4]', + eval('string(g:list)')) + end) +end) -- cgit From 1b3e13da5be48739ac4291208e421b68e607cc0f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:18:29 +0300 Subject: eval: Refactor get_tv_lnum_buf --- src/nvim/eval.c | 50 +++++++++++++++++++++++++++----------------------- src/nvim/eval/typval.c | 2 ++ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6307dd8abc..2a7314d969 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9190,13 +9190,34 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli } } +/// Get the line number from VimL object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +static linenr_T tv_get_lnum_buf(const typval_T *const tv, + const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return get_tv_number_chk(tv, NULL); +} + /* * "getbufline()" function */ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; - linenr_T end; buf_T *buf = NULL; if (tv_check_str_or_nr(&argvars[0])) { @@ -9205,12 +9226,10 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) emsg_off--; } - lnum = get_tv_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type == VAR_UNKNOWN) { - end = lnum; - } else { - end = get_tv_lnum_buf(&argvars[2], buf); - } + const linenr_T lnum = tv_get_lnum_buf(&argvars[1], buf); + const linenr_T end = (argvars[2].v_type == VAR_UNKNOWN + ? lnum + : tv_get_lnum_buf(&argvars[2], buf)); get_buffer_lines(buf, lnum, end, true, rettv); } @@ -18464,21 +18483,6 @@ varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) return n; } -/* - * Get the lnum from the first argument. - * Also accepts "$", then "buf" is used. - * Returns 0 on error. - */ -static linenr_T get_tv_lnum_buf(typval_T *argvars, buf_T *buf) -{ - if (argvars[0].v_type == VAR_STRING - && argvars[0].vval.v_string != NULL - && argvars[0].vval.v_string[0] == '$' - && buf != NULL) - return buf->b_ml.ml_line_count; - return get_tv_number_chk(&argvars[0], NULL); -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6a16b3869a..38032762dc 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2026,6 +2026,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) /// /// @return Line number or -1 or 0. linenr_T tv_get_lnum(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { linenr_T lnum = get_tv_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. @@ -2046,6 +2047,7 @@ linenr_T tv_get_lnum(const typval_T *const tv) /// /// @return Floating-point value of the variable or zero. float_T tv_get_float(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { switch (tv->v_type) { case VAR_NUMBER: { -- cgit From 233b0c93bba66492d7b8b61f8ac61082f03668a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 08:55:58 +0300 Subject: eval: Move get_tv_number[_chk] to eval/typval.c --- src/nvim/eval.c | 659 ++++++++++++++++++++++------------------------- src/nvim/eval/executor.c | 8 +- src/nvim/eval/typval.c | 130 +++++++++- src/nvim/strings.c | 2 +- src/nvim/window.c | 20 +- 5 files changed, 446 insertions(+), 373 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2a7314d969..676df1a301 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -954,7 +954,7 @@ eval_to_bool ( } else { *error = false; if (!skip) { - retval = (get_tv_number_chk(&tv, error) != 0); + retval = (tv_get_number_chk(&tv, error) != 0); tv_clear(&tv); } } @@ -1081,10 +1081,10 @@ int eval_to_number(char_u *expr) ++emsg_off; - if (eval1(&p, &rettv, TRUE) == FAIL) + if (eval1(&p, &rettv, true) == FAIL) { retval = -1; - else { - retval = get_tv_number_chk(&rettv, NULL); + } else { + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); } --emsg_off; @@ -1174,9 +1174,10 @@ int get_spellword(list_T *list, const char **pp) *pp = tv_get_string(&li->li_tv); li = li->li_next; - if (li == NULL) + if (li == NULL) { return -1; - return get_tv_number(&li->li_tv); + } + return tv_get_number(&li->li_tv); } /* @@ -1284,7 +1285,7 @@ call_func_retnr ( if (call_vim_function(func, argc, argv, safe, TRUE, &rettv) == FAIL) return -1; - retval = get_tv_number_chk(&rettv, NULL); + retval = tv_get_number_chk(&rettv, NULL); tv_clear(&rettv); return retval; } @@ -1912,7 +1913,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite((const char_u *)p)) == NULL)) { EMSG(_(e_letunexp)); } else { - long n; int opt_type; long numval; char_u *stringval = NULL; @@ -1921,8 +1921,8 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char c1 = *p; *p = NUL; - n = get_tv_number(tv); - s = get_tv_string_chk(tv); /* != NULL if number or string */ + varnumber_T n = tv_get_number(tv); + s = get_tv_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { opt_type = get_option_value(arg, &numval, &stringval, opt_flags); if ((opt_type == 1 && *op == '.') @@ -2284,13 +2284,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } lp->ll_tv = &lp->ll_di->di_tv; } else { - /* - * Get the number and item for the only or first index of the List. - */ + // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = get_tv_number(&var1); // Is number or string. + lp->ll_n1 = tv_get_number(&var1); // Is number or string. tv_clear(&var1); } lp->ll_dict = NULL; @@ -2319,7 +2317,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Otherwise "lp->ll_n2" is set to the second index. */ if (lp->ll_range && !lp->ll_empty2) { - lp->ll_n2 = get_tv_number(&var2); // Is number or string. + lp->ll_n2 = tv_get_number(&var2); // Is number or string. tv_clear(&var2); if (lp->ll_n2 < 0) { ni = tv_list_find(lp->ll_list, lp->ll_n2); @@ -3320,7 +3318,7 @@ static int eval1(char_u **arg, typval_T *rettv, int evaluate) if (evaluate) { bool error = false; - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3395,7 +3393,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) result = FALSE; while ((*arg)[0] == '|' && (*arg)[1] == '|') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) != 0) { + if (tv_get_number_chk(rettv, &error) != 0) { result = true; } tv_clear(rettv); @@ -3416,7 +3414,7 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && !result) { - if (get_tv_number_chk(&var2, &error) != 0) { + if (tv_get_number_chk(&var2, &error) != 0) { result = true; } tv_clear(&var2); @@ -3464,7 +3462,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) result = TRUE; while ((*arg)[0] == '&' && (*arg)[1] == '&') { if (evaluate && first) { - if (get_tv_number_chk(rettv, &error) == 0) { + if (tv_get_number_chk(rettv, &error) == 0) { result = false; } tv_clear(rettv); @@ -3485,7 +3483,7 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) * Compute the result. */ if (evaluate && result) { - if (get_tv_number_chk(&var2, &error) == 0) { + if (tv_get_number_chk(&var2, &error) == 0) { result = false; } tv_clear(&var2); @@ -3694,25 +3692,27 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) && type != TYPE_MATCH && type != TYPE_NOMATCH) { float_T f1, f2; - if (rettv->v_type == VAR_FLOAT) + if (rettv->v_type == VAR_FLOAT) { f1 = rettv->vval.v_float; - else - f1 = get_tv_number(rettv); - if (var2.v_type == VAR_FLOAT) + } else { + f1 = tv_get_number(rettv); + } + if (var2.v_type == VAR_FLOAT) { f2 = var2.vval.v_float; - else - f2 = get_tv_number(&var2); - n1 = FALSE; + } else { + f2 = tv_get_number(&var2); + } + n1 = false; switch (type) { - case TYPE_EQUAL: n1 = (f1 == f2); break; - case TYPE_NEQUAL: n1 = (f1 != f2); break; - case TYPE_GREATER: n1 = (f1 > f2); break; - case TYPE_GEQUAL: n1 = (f1 >= f2); break; - case TYPE_SMALLER: n1 = (f1 < f2); break; - case TYPE_SEQUAL: n1 = (f1 <= f2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (f1 == f2); break; + case TYPE_NEQUAL: n1 = (f1 != f2); break; + case TYPE_GREATER: n1 = (f1 > f2); break; + case TYPE_GEQUAL: n1 = (f1 >= f2); break; + case TYPE_SMALLER: n1 = (f1 < f2); break; + case TYPE_SEQUAL: n1 = (f1 <= f2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } /* @@ -3721,18 +3721,18 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) */ else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER) && type != TYPE_MATCH && type != TYPE_NOMATCH) { - n1 = get_tv_number(rettv); - n2 = get_tv_number(&var2); + n1 = tv_get_number(rettv); + n2 = tv_get_number(&var2); switch (type) { - case TYPE_EQUAL: n1 = (n1 == n2); break; - case TYPE_NEQUAL: n1 = (n1 != n2); break; - case TYPE_GREATER: n1 = (n1 > n2); break; - case TYPE_GEQUAL: n1 = (n1 >= n2); break; - case TYPE_SMALLER: n1 = (n1 < n2); break; - case TYPE_SEQUAL: n1 = (n1 <= n2); break; - case TYPE_UNKNOWN: - case TYPE_MATCH: - case TYPE_NOMATCH: break; /* avoid gcc warning */ + case TYPE_EQUAL: n1 = (n1 == n2); break; + case TYPE_NEQUAL: n1 = (n1 != n2); break; + case TYPE_GREATER: n1 = (n1 > n2); break; + case TYPE_GEQUAL: n1 = (n1 >= n2); break; + case TYPE_SMALLER: n1 = (n1 < n2); break; + case TYPE_SEQUAL: n1 = (n1 <= n2); break; + case TYPE_UNKNOWN: + case TYPE_MATCH: + case TYPE_NOMATCH: break; } } else { char buf1[NUMBUFLEN]; @@ -3872,7 +3872,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f1 = rettv->vval.v_float; n1 = 0; } else { - n1 = get_tv_number_chk(rettv, &error); + n1 = tv_get_number_chk(rettv, &error); if (error) { /* This can only happen for "list + non-list". For * "non-list + ..." or "something - ...", we returned @@ -3887,7 +3887,7 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); if (error) { tv_clear(rettv); tv_clear(&var2); @@ -3968,8 +3968,9 @@ eval6 ( f1 = rettv->vval.v_float; use_float = TRUE; n1 = 0; - } else - n1 = get_tv_number_chk(rettv, &error); + } else { + n1 = tv_get_number_chk(rettv, &error); + } tv_clear(rettv); if (error) { return FAIL; @@ -3994,7 +3995,7 @@ eval6 ( f2 = var2.vval.v_float; n2 = 0; } else { - n2 = get_tv_number_chk(&var2, &error); + n2 = tv_get_number_chk(&var2, &error); tv_clear(&var2); if (error) { return FAIL; @@ -4296,7 +4297,7 @@ static int eval7( if (rettv->v_type == VAR_FLOAT) { f = rettv->vval.v_float; } else { - val = get_tv_number_chk(rettv, &error); + val = tv_get_number_chk(rettv, &error); } if (error) { tv_clear(rettv); @@ -4457,14 +4458,14 @@ eval_index ( if (evaluate) { n1 = 0; if (!empty1 && rettv->v_type != VAR_DICT) { - n1 = get_tv_number(&var1); + n1 = tv_get_number(&var1); tv_clear(&var1); } if (range) { if (empty2) { n2 = -1; } else { - n2 = get_tv_number(&var2); + n2 = tv_get_number(&var2); tv_clear(&var2); } } @@ -6543,13 +6544,14 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) varnumber_T n; bool error = false; - n = get_tv_number_chk(&argvars[0], &error); - if (error) + n = tv_get_number_chk(&argvars[0], &error); + if (error) { rettv->vval.v_number = -1; - else if (n > 0) + } else if (n > 0) { rettv->vval.v_number = n; - else + } else { rettv->vval.v_number = -n; + } } } @@ -6578,8 +6580,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - & get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + & tv_get_number_chk(&argvars[1], NULL); } @@ -6680,11 +6682,13 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) int idx; if (argvars[0].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) - rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx])); - else + idx = tv_get_number_chk(&argvars[0], NULL); + if (idx >= 0 && idx < ARGCOUNT) { + rettv->vval.v_string = (char_u *)xstrdup( + (const char *)alist_name(&ARGLIST[idx])); + } else { rettv->vval.v_string = NULL; + } rettv->v_type = VAR_STRING; } else { tv_list_alloc_ret(rettv); @@ -6855,9 +6859,9 @@ static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) void assert_inrange(typval_T *argvars) { bool error = false; - const varnumber_T lower = get_tv_number_chk(&argvars[0], &error); - const varnumber_T upper = get_tv_number_chk(&argvars[1], &error); - const varnumber_T actual = get_tv_number_chk(&argvars[2], &error); + const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); + const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); + const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); if (error) { return; @@ -6884,7 +6888,7 @@ static void assert_bool(typval_T *argvars, bool is_true) garray_T ga; if ((argvars[0].v_type != VAR_NUMBER - || (get_tv_number_chk(&argvars[0], &error) == 0) == is_true + || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true || error) && (argvars[0].v_type != VAR_SPECIAL || (argvars[0].vval.v_special @@ -7118,7 +7122,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // new buffer. if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error) != 0 + && tv_get_number_chk(&argvars[1], &error) != 0 && !error && (name = get_tv_string_chk(&argvars[0])) != NULL && !error) { @@ -7176,7 +7180,7 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long boff = get_tv_number(&argvars[0]) - 1; + long boff = tv_get_number(&argvars[0]) - 1; if (boff < 0) { rettv->vval.v_number = -1; } else { @@ -7192,7 +7196,7 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp) long idx; str = get_tv_string_chk(&argvars[0]); - idx = get_tv_number_chk(&argvars[1], NULL); + idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; if (str == NULL || idx < 0) return; @@ -7314,7 +7318,7 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int utf8 = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - utf8 = get_tv_number_chk(&argvars[1], NULL); + utf8 = tv_get_number_chk(&argvars[1], NULL); } rettv->vval.v_number = (utf8 ? *utf_ptr2char : *mb_ptr2char)( @@ -7392,8 +7396,6 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int startcol; - if ((State & INSERT) == 0) { EMSG(_("E785: complete() can only be used in Insert mode")); return; @@ -7409,9 +7411,10 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - startcol = get_tv_number_chk(&argvars[0], NULL); - if (startcol <= 0) + const int startcol = tv_get_number_chk(&argvars[0], NULL); + if (startcol <= 0) { return; + } set_completion(startcol - 1, argvars[1].vval.v_list); } @@ -7459,7 +7462,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (buttons == NULL) error = TRUE; if (argvars[2].v_type != VAR_UNKNOWN) { - def = get_tv_number_chk(&argvars[2], &error); + def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { typestr = get_tv_string_buf_chk(&argvars[3], buf2); if (typestr == NULL) @@ -7511,9 +7514,9 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - ic = get_tv_number_chk(&argvars[2], &error); + ic = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - idx = get_tv_number_chk(&argvars[3], &error); + idx = tv_get_number_chk(&argvars[3], &error); if (!error) { li = tv_list_find(l, idx); if (li == NULL) { @@ -7538,9 +7541,10 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool error = false; if (argvars[2].v_type != VAR_UNKNOWN) { - ic = get_tv_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) + ic = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { EMSG(_(e_invarg)); + } } todo = error ? 0 : (int)d->dv_hashtab.ht_used; @@ -7572,7 +7576,7 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_UNKNOWN) { - num = (int)get_tv_number(&argvars[0]); + num = (int)tv_get_number(&argvars[0]); dbpath = tv_get_string(&argvars[1]); if (argvars[2].v_type != VAR_UNKNOWN) { prepend = tv_get_string_buf(&argvars[2], buf); @@ -7614,9 +7618,9 @@ static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else { line = tv_get_lnum(argvars); - col = get_tv_number_chk(&argvars[1], NULL); + col = tv_get_number_chk(&argvars[1], NULL); if (argvars[2].v_type != VAR_UNKNOWN) { - coladd = get_tv_number_chk(&argvars[2], NULL); + coladd = tv_get_number_chk(&argvars[2], NULL); } } if (line < 0 || col < 0 @@ -7649,10 +7653,11 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int noref = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - noref = get_tv_number_chk(&argvars[1], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) { + noref = tv_get_number_chk(&argvars[1], NULL); + } if (noref < 0 || noref > 1) { - EMSG(_(e_invarg)); + emsgf(_(e_invarg)); } else { var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() @@ -7851,11 +7856,12 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (hlID == HLF_CHD || hlID == HLF_TXD) { - col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */ - if (col >= change_start && col <= change_end) - hlID = HLF_TXD; /* changed text */ - else - hlID = HLF_CHD; /* changed line */ + col = tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}. + if (col >= change_start && col <= change_end) { + hlID = HLF_TXD; // Changed text. + } else { + hlID = HLF_CHD; // Changed line. + } } rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; } @@ -8123,7 +8129,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[2], &error) + && tv_get_number_chk(&argvars[2], &error) && !error) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; @@ -8145,8 +8151,9 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* When the optional second argument is non-zero, don't remove matches * for 'wildignore' and don't put matches for 'suffixes' at the end. */ if (argvars[1].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[1], &error)) + && tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (!error) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; @@ -8191,9 +8198,10 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); - if (error) - return; /* type error; errmsg already given */ + before = tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } if (before == l1->lv_len) { item = NULL; @@ -8315,7 +8323,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) path = p; if (argvars[2].v_type != VAR_UNKNOWN) { - count = get_tv_number_chk(&argvars[2], &error); + count = tv_get_number_chk(&argvars[2], &error); } } } @@ -8512,10 +8520,10 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) bool error = false; // filter(): when expr is zero remove the item - *remp = (get_tv_number_chk(&rettv, &error) == 0); + *remp = (tv_get_number_chk(&rettv, &error) == 0); tv_clear(&rettv); // On type error, nothing has been removed; return FAIL to stop the - // loop. The error message was given by get_tv_number_chk(). + // loop. The error message was given by tv_get_number_chk(). if (error) { goto theend; } @@ -8946,12 +8954,13 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "garbagecollect()" function static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - /* This is postponed until we are back at the toplevel, because we may be - * using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". */ - want_garbage_collect = TRUE; + // This is postponed until we are back at the toplevel, because we may be + // using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]". + want_garbage_collect = true; - if (argvars[0].v_type != VAR_UNKNOWN && get_tv_number(&argvars[0]) == 1) - garbage_collect_at_exit = TRUE; + if (argvars[0].v_type != VAR_UNKNOWN && tv_get_number(&argvars[0]) == 1) { + garbage_collect_at_exit = true; + } } /* @@ -8969,7 +8978,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL) { bool error = false; - li = tv_list_find(l, get_tv_number_chk(&argvars[1], &error)); + li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { tv = &li->li_tv; } @@ -9110,23 +9119,24 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) filtered = true; di = tv_dict_find(sel_d, S_LEN("buflisted")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_buflisted = true; } di = tv_dict_find(sel_d, S_LEN("bufloaded")); - if (di != NULL && get_tv_number(&di->di_tv)) { + if (di != NULL && tv_get_number(&di->di_tv)) { sel_bufloaded = true; } } } else if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one buffer. Argument specifies the buffer - (void)get_tv_number(&argvars[0]); // issue errmsg if type error - emsg_off++; - argbuf = get_buf_tv(&argvars[0], false); - emsg_off--; - if (argbuf == NULL) { - return; + if (tv_check_num(&argvars[0])) { // issue errmsg if type error + emsg_off++; + argbuf = get_buf_tv(&argvars[0], false); + emsg_off--; + if (argbuf == NULL) { + return; + } } } @@ -9210,7 +9220,7 @@ static linenr_T tv_get_lnum_buf(const typval_T *const tv, && buf != NULL) { return buf->b_ml.ml_line_count; } - return get_tv_number_chk(tv, NULL); + return tv_get_number_chk(tv, NULL); } /* @@ -9321,7 +9331,7 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } n = safe_vgetc(); - } else if (get_tv_number_chk(&argvars[0], &error) == 1) { + } else if (tv_get_number_chk(&argvars[0], &error) == 1) { // getchar(1): only check if char avail n = vpeekc_any(); } else if (error || vpeekc_any() == NUL) { @@ -9332,8 +9342,9 @@ static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = safe_vgetc(); } - if (n == K_IGNORE) + if (n == K_IGNORE) { continue; + } break; } no_mapping--; @@ -9460,7 +9471,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) | WILD_NO_BEEP; if (argvars[2].v_type != VAR_UNKNOWN) { - filtered = get_tv_number_chk(&argvars[2], NULL); + filtered = (bool)tv_get_number_chk(&argvars[2], NULL); } if (p_wic) { @@ -9970,9 +9981,9 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) strregname = get_tv_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { - arg2 = get_tv_number_chk(&argvars[1], &error); + arg2 = tv_get_number_chk(&argvars[1], &error); if (!error && argvars[2].v_type != VAR_UNKNOWN) { - return_list = get_tv_number_chk(&argvars[2], &error); + return_list = tv_get_number_chk(&argvars[2], &error); } } } else { @@ -10062,7 +10073,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page - tparg = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tparg == NULL) { return; } @@ -10099,7 +10110,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = NULL; const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the // first window in the tabpage, otherwise the window is not valid. @@ -10216,7 +10227,7 @@ find_win_by_nr ( tabpage_T *tp /* NULL for current tab page */ ) { - int nr = (int)get_tv_number_chk(vp, NULL); + int nr = (int)tv_get_number_chk(vp, NULL); if (nr < 0) { return NULL; @@ -10251,7 +10262,7 @@ static win_T *find_tabwin(typval_T *wvp, typval_T *tvp) if (wvp->v_type != VAR_UNKNOWN) { if (tvp->v_type != VAR_UNKNOWN) { - long n = get_tv_number(tvp); + long n = tv_get_number(tvp); if (n >= 0) { tp = find_tabpage(n); } @@ -10291,10 +10302,11 @@ getwinvar ( tabpage_T *oldtabpage = NULL; bool done = false; - if (off == 1) - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); - else + if (off == 1) { + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); + } else { tp = curtab; + } win = find_win_by_nr(&argvars[off], tp); const char *varname = (const char *)get_tv_string_chk( &argvars[off + 1]); @@ -10363,15 +10375,16 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) * for 'wildignore' and don't put matches for 'suffixes' at the end. */ rettv->v_type = VAR_STRING; if (argvars[1].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[1], &error)) + if (tv_get_number_chk(&argvars[1], &error)) { options |= WILD_KEEP_ALL; + } if (argvars[2].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[3].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[3], &error)) { + && tv_get_number_chk(&argvars[3], &error)) { options |= WILD_ALLLINKS; } } @@ -10410,17 +10423,17 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { // When the optional second argument is non-zero, don't remove matches // for 'wildignore' and don't put matches for 'suffixes' at the end. - if (get_tv_number_chk(&argvars[2], &error)) { + if (tv_get_number_chk(&argvars[2], &error)) { flags |= WILD_KEEP_ALL; } if (argvars[3].v_type != VAR_UNKNOWN) { - if (get_tv_number_chk(&argvars[3], &error)) { + if (tv_get_number_chk(&argvars[3], &error)) { rettv->v_type = VAR_LIST; rettv->vval.v_list = NULL; } if (argvars[4].v_type != VAR_UNKNOWN - && get_tv_number_chk(&argvars[4], &error)) { + && tv_get_number_chk(&argvars[4], &error)) { flags |= WILD_ALLLINKS; } } @@ -10763,7 +10776,7 @@ static void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { mode = tv_get_string_buf(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); + abbr = tv_get_number(&argvars[2]); } } @@ -10816,7 +10829,7 @@ static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry n = del_history_idx(get_histtype(str, STRLEN(str), false), - (int)get_tv_number(&argvars[1])); + (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries char buf[NUMBUFLEN]; @@ -10843,7 +10856,7 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { - idx = (int)get_tv_number_chk(&argvars[1], NULL); + idx = (int)tv_get_number_chk(&argvars[1], NULL); } // -1 on type error rettv->vval.v_string = vim_strsave(get_history_entry(type, idx)); @@ -10968,12 +10981,14 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Start at specified item. Use the cached index that tv_list_find() // sets, so that a negative number also works. - item = tv_list_find(l, get_tv_number_chk(&argvars[2], &error)); + item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) - ic = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = tv_get_number_chk(&argvars[3], &error); + } + if (error) { item = NULL; + } } for (; item != NULL; item = item->li_next, ++idx) @@ -11186,7 +11201,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - before = get_tv_number_chk(&argvars[2], &error); + before = tv_get_number_chk(&argvars[2], &error); } if (error) { // type error; errmsg already given @@ -11213,7 +11228,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = ~get_tv_number_chk(&argvars[0], NULL); + rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } /* @@ -12051,9 +12066,10 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (argvars[1].v_type != VAR_UNKNOWN) { which = get_tv_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { - abbr = get_tv_number(&argvars[2]); - if (argvars[3].v_type != VAR_UNKNOWN) - get_dict = get_tv_number(&argvars[3]); + abbr = tv_get_number(&argvars[2]); + if (argvars[3].v_type != VAR_UNKNOWN) { + get_dict = tv_get_number(&argvars[3]); + } } } else which = (char_u *)""; @@ -12175,9 +12191,10 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start = get_tv_number_chk(&argvars[2], &error); - if (error) + start = tv_get_number_chk(&argvars[2], &error); + if (error) { goto theend; + } if (l != NULL) { li = tv_list_find(l, start); if (li == NULL) { @@ -12200,10 +12217,12 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - if (argvars[3].v_type != VAR_UNKNOWN) - nth = get_tv_number_chk(&argvars[3], &error); - if (error) + if (argvars[3].v_type != VAR_UNKNOWN) { + nth = tv_get_number_chk(&argvars[3], &error); + } + if (error) { goto theend; + } } regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); @@ -12331,9 +12350,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (grp == NULL || pat == NULL) return; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12387,9 +12406,9 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *conceal_char = NULL; if (argvars[2].v_type != VAR_UNKNOWN) { - prio = get_tv_number_chk(&argvars[2], &error); + prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - id = get_tv_number_chk(&argvars[3], &error); + id = tv_get_number_chk(&argvars[3], &error); if (argvars[4].v_type != VAR_UNKNOWN) { if (argvars[4].v_type != VAR_DICT) { EMSG(_(e_dictreq)); @@ -12424,7 +12443,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv); - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); if (id >= 1 && id <= 3) { matchitem_T *m; @@ -12446,7 +12465,7 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = match_delete(curwin, - (int)get_tv_number(&argvars[0]), TRUE); + (int)tv_get_number(&argvars[0]), true); } /* @@ -12493,14 +12512,16 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) if (l != NULL) { li = l->lv_first; if (li != NULL) { - n = get_tv_number_chk(&li->li_tv, &error); + n = tv_get_number_chk(&li->li_tv, &error); for (;; ) { li = li->li_next; - if (li == NULL) + if (li == NULL) { break; - i = get_tv_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) + } + i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { n = i; + } } } } @@ -12516,7 +12537,7 @@ static void max_min(typval_T *argvars, typval_T *rettv, int domax) for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { todo--; - i = get_tv_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); + i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); if (first) { n = i; first = FALSE; @@ -12570,7 +12591,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) { - prot = get_tv_number_chk(&argvars[2], NULL); + prot = tv_get_number_chk(&argvars[2], NULL); } if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { char *failed_dir; @@ -12782,14 +12803,16 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (has_mbyte) { int utf8 = 0; - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = get_tv_number_chk(&argvars[1], NULL); - if (utf8) - buf[(*utf_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; - else - buf[(*mb_char2bytes)((int)get_tv_number(&argvars[0]), buf)] = NUL; + if (argvars[1].v_type != VAR_UNKNOWN) { + utf8 = tv_get_number_chk(&argvars[1], NULL); + } + if (utf8) { + buf[(*utf_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } else { + buf[(*mb_char2bytes)((int)tv_get_number(&argvars[0]), buf)] = NUL; + } } else { - buf[0] = (char_u)get_tv_number(&argvars[0]); + buf[0] = (char_u)tv_get_number(&argvars[0]); buf[1] = NUL; } rettv->v_type = VAR_STRING; @@ -12801,8 +12824,8 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - | get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + | tv_get_number_chk(&argvars[1], NULL); } /* @@ -12911,14 +12934,15 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) long i; bool error = false; - start = get_tv_number_chk(&argvars[0], &error); + start = tv_get_number_chk(&argvars[0], &error); if (argvars[1].v_type == VAR_UNKNOWN) { end = start - 1; start = 0; } else { - end = get_tv_number_chk(&argvars[1], &error); - if (argvars[2].v_type != VAR_UNKNOWN) - stride = get_tv_number_chk(&argvars[2], &error); + end = tv_get_number_chk(&argvars[1], &error); + if (argvars[2].v_type != VAR_UNKNOWN) { + stride = tv_get_number_chk(&argvars[2], &error); + } } if (error) { @@ -12959,7 +12983,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) binary = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - maxline = get_tv_number(&argvars[2]); + maxline = tv_get_number(&argvars[2]); } } @@ -13239,7 +13263,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { bool error = false; - idx = get_tv_number_chk(&argvars[1], &error); + idx = tv_get_number_chk(&argvars[1], &error); if (error) { // Type error: do nothing, errmsg already given. } else if ((item = tv_list_find(l, idx)) == NULL) { @@ -13251,8 +13275,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) *rettv = item->li_tv; xfree(item); } else { - /* Remove range of items, return list with values. */ - end = get_tv_number_chk(&argvars[2], &error); + // Remove range of items, return list with values. + end = tv_get_number_chk(&argvars[2], &error); if (error) { // Type error: do nothing. } else if ((item2 = tv_list_find(l, end)) == NULL) { @@ -13302,7 +13326,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - varnumber_T n = get_tv_number(&argvars[1]); + varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { tv_list_alloc_ret(rettv); while (n-- > 0) { @@ -13621,13 +13645,15 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) /* Optional arguments: line number to stop searching and timeout. */ if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[2], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[2], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[3].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[3], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[3], NULL); + if (time_limit < 0) { goto theend; + } } } @@ -13889,17 +13915,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else + } else { c = ScreenAttrs[LineOffset[row] + col]; + } rettv->vval.v_number = c; } @@ -13908,17 +13933,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int row; - int col; int off; int c; - row = get_tv_number_chk(&argvars[0], NULL) - 1; - col = get_tv_number_chk(&argvars[1], NULL) - 1; + const int row = tv_get_number_chk(&argvars[0], NULL) - 1; + const int col = tv_get_number_chk(&argvars[1], NULL) - 1; if (row < 0 || row >= screen_Rows - || col < 0 || col >= screen_Columns) + || col < 0 || col >= screen_Columns) { c = -1; - else { + } else { off = LineOffset[row] + col; if (enc_utf8 && ScreenLinesUC[off] != 0) c = ScreenLinesUC[off]; @@ -13969,9 +13992,10 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) char_u *name = get_tv_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - locally = get_tv_number_chk(&argvars[1], &error) == 0; - if (!error && argvars[2].v_type != VAR_UNKNOWN) - thisblock = get_tv_number_chk(&argvars[2], &error) != 0; + locally = tv_get_number_chk(&argvars[1], &error) == 0; + if (!error && argvars[2].v_type != VAR_UNKNOWN) { + thisblock = tv_get_number_chk(&argvars[2], &error) != 0; + } } if (!error && name != NULL) rettv->vval.v_number = find_decl(name, STRLEN(name), locally, @@ -14027,13 +14051,15 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) else { skip = get_tv_string_buf_chk(&argvars[4], nbuf3); if (argvars[5].v_type != VAR_UNKNOWN) { - lnum_stop = get_tv_number_chk(&argvars[5], NULL); - if (lnum_stop < 0) + lnum_stop = tv_get_number_chk(&argvars[5], NULL); + if (lnum_stop < 0) { goto theend; + } if (argvars[6].v_type != VAR_UNKNOWN) { - time_limit = get_tv_number_chk(&argvars[6], NULL); - if (time_limit < 0) + time_limit = tv_get_number_chk(&argvars[6], NULL); + if (time_limit < 0) { goto theend; + } } } } @@ -14337,7 +14363,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) aucmd_prepbuf(&aco, buf); varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); @@ -14385,12 +14411,12 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) di = tv_dict_find(d, S_LEN("forward")); if (di != NULL) { - set_csearch_direction(get_tv_number(&di->di_tv) ? FORWARD : BACKWARD); + set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD); } di = tv_dict_find(d, S_LEN("until")); if (di != NULL) { - set_csearch_until(!!get_tv_number(&di->di_tv)); + set_csearch_until(!!tv_get_number(&di->di_tv)); } } } @@ -14400,10 +14426,11 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int pos = (int)get_tv_number(&argvars[0]) - 1; + const int pos = (int)tv_get_number(&argvars[0]) - 1; - if (pos >= 0) + if (pos >= 0) { rettv->vval.v_number = set_cmdline_pos(pos); + } } @@ -14854,7 +14881,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - tabpage_T *const tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; @@ -14904,7 +14931,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tabpage_T *tp = NULL; if (off == 1) { - tp = find_tabpage((int)get_tv_number_chk(&argvars[0], NULL)); + tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); } else { tp = curtab; } @@ -14923,7 +14950,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) bool error = false; varname++; - numval = get_tv_number_chk(varp, &error); + numval = tv_get_number_chk(varp, &error); char_u nbuf[NUMBUFLEN]; char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { @@ -15024,8 +15051,8 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) typval_T *tv2 = &si2->item->li_tv; if (sortinfo->item_compare_numbers) { - long v1 = get_tv_number(tv1); - long v2 = get_tv_number(tv2); + const varnumber_T v1 = tv_get_number(tv1); + const varnumber_T v2 = tv_get_number(tv2); return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; } @@ -15138,7 +15165,7 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) if (res == FAIL) { res = ITEM_COMPARE_FAIL; } else { - res = get_tv_number_chk(&rettv, &sortinfo->item_compare_func_err); + res = tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); } if (sortinfo->item_compare_func_err) { res = ITEM_COMPARE_FAIL; // return value has wrong type @@ -15220,7 +15247,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } else { bool error = false; - i = get_tv_number_chk(&argvars[1], &error); + i = tv_get_number_chk(&argvars[1], &error); if (error) { goto theend; // type error; errmsg already given } @@ -15443,13 +15470,15 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { - maxcount = get_tv_number_chk(&argvars[1], &typeerr); - if (maxcount <= 0) + maxcount = tv_get_number_chk(&argvars[1], &typeerr); + if (maxcount <= 0) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { - need_capital = get_tv_number_chk(&argvars[2], &typeerr); - if (typeerr) + need_capital = tv_get_number_chk(&argvars[2], &typeerr); + if (typeerr) { return; + } } } else maxcount = 25; @@ -15491,7 +15520,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) typeerr = true; } if (argvars[2].v_type != VAR_UNKNOWN) { - keepempty = (bool)get_tv_number_chk(&argvars[2], &typeerr); + keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } if (pat == NULL || *pat == NUL) @@ -15563,7 +15592,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr) int what; if (argvars[1].v_type != VAR_UNKNOWN) { - base = get_tv_number(&argvars[1]); + base = tv_get_number(&argvars[1]); if (base != 2 && base != 8 && base != 10 && base != 16) { EMSG(_(e_invarg)); return; @@ -15608,7 +15637,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_UNKNOWN) { seconds = time(NULL); } else { - seconds = (time_t)get_tv_number(&argvars[1]); + seconds = (time_t)tv_get_number(&argvars[1]); } struct tm curtime; @@ -15658,9 +15687,8 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (str == NULL) { return; } - bool error = false; - varnumber_T charidx = get_tv_number_chk(&argvars[1], &error); + varnumber_T charidx = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15699,11 +15727,13 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = get_tv_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) + start_idx = tv_get_number_chk(&argvars[2], &error); + if (error || start_idx >= (int)STRLEN(haystack)) { return; - if (start_idx >= 0) + } + if (start_idx >= 0) { haystack += start_idx; + } } pos = (char_u *)strstr((char *)haystack, (char *)needle); @@ -15739,7 +15769,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) int (*func_mb_ptr2char_adv)(char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { - skipcc = get_tv_number_chk(&argvars[1], NULL); + skipcc = tv_get_number_chk(&argvars[1], NULL); } if (skipcc < 0 || skipcc > 1) { EMSG(_(e_invarg)); @@ -15762,7 +15792,7 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) int col = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - col = get_tv_number(&argvars[1]); + col = tv_get_number(&argvars[1]); } rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col); @@ -15786,7 +15816,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int nbyte = 0; bool error = false; - varnumber_T nchar = get_tv_number_chk(&argvars[1], &error); + varnumber_T nchar = tv_get_number_chk(&argvars[1], &error); if (!error) { if (nchar > 0) { while (nchar > 0 && (size_t)nbyte < slen) { @@ -15799,7 +15829,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } int len = 0; if (argvars[2].v_type != VAR_UNKNOWN) { - int charlen = get_tv_number(&argvars[2]); + int charlen = tv_get_number(&argvars[2]); while (charlen > 0 && nbyte + len < (int)slen) { int off = nbyte + len; @@ -15842,12 +15872,12 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const p = tv_get_string(&argvars[0]); const size_t slen = strlen(p); - varnumber_T n = get_tv_number_chk(&argvars[1], &error); + varnumber_T n = tv_get_number_chk(&argvars[1], &error); varnumber_T len; if (error) { len = 0; } else if (argvars[2].v_type != VAR_UNKNOWN) { - len = get_tv_number(&argvars[2]); + len = tv_get_number(&argvars[2]); } else { len = slen - n; // Default len: all bytes that are available. } @@ -15891,12 +15921,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) haystack_len = (int)STRLEN(haystack); if (argvars[2].v_type != VAR_UNKNOWN) { - /* Third argument: upper limit for index */ - end_idx = get_tv_number_chk(&argvars[2], NULL); - if (end_idx < 0) - return; /* can never find a match */ - } else + // Third argument: upper limit for index. + end_idx = tv_get_number_chk(&argvars[2], NULL); + if (end_idx < 0) { + return; // Can never find a match. + } + } else { end_idx = haystack_len; + } if (*needle == NUL) { /* Empty string matches past the end. */ @@ -15931,7 +15963,7 @@ static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - int no = (int)get_tv_number_chk(&argvars[0], &error); + int no = (int)tv_get_number_chk(&argvars[0], &error); if (error) { return; } @@ -15943,7 +15975,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) int retList = 0; if (argvars[1].v_type != VAR_UNKNOWN) { - retList = get_tv_number_chk(&argvars[1], &error); + retList = tv_get_number_chk(&argvars[1], &error); if (error) { return; } @@ -15993,10 +16025,10 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; bool transerr = false; - const int trans = get_tv_number_chk(&argvars[2], &transerr); + const int trans = tv_get_number_chk(&argvars[2], &transerr); int id = 0; if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16012,7 +16044,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const int id = get_tv_number(&argvars[0]); + const int id = (int)tv_get_number(&argvars[0]); const char *const what = tv_get_string(&argvars[1]); int modec; if (argvars[2].v_type != VAR_UNKNOWN) { @@ -16086,14 +16118,13 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int id; + int id = tv_get_number(&argvars[0]); - id = get_tv_number(&argvars[0]); - - if (id > 0) + if (id > 0) { id = syn_get_final_id(id); - else + } else { id = 0; + } rettv->vval.v_number = id; } @@ -16113,7 +16144,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; memset(str, NUL, sizeof(str)); @@ -16154,7 +16185,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) // -1 on type error (both) const linenr_T lnum = tv_get_lnum(argvars); - const colnr_T col = (colnr_T)get_tv_number(&argvars[1]) - 1; + const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1; if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count @@ -16232,7 +16263,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (retlist) { int keepempty = 0; if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - keepempty = get_tv_number(&argvars[2]); + keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); rettv->vval.v_list->lv_refcount++; @@ -16277,15 +16308,15 @@ static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tabpage_T *tp; win_T *wp = NULL; - if (argvars[0].v_type == VAR_UNKNOWN) + if (argvars[0].v_type == VAR_UNKNOWN) { wp = firstwin; - else { - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp != NULL) + } else { + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp != NULL) { wp = (tp == curtab) ? firstwin : tp->tp_firstwin; + } } if (wp != NULL) { tv_list_alloc_ret(rettv); @@ -16367,13 +16398,12 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - tabpage_T *tp; - - tp = find_tabpage((int)get_tv_number(&argvars[0])); - if (tp == NULL) + tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0])); + if (tp == NULL) { nr = 0; - else + } else { nr = get_winnr(tp, &argvars[1]); + } rettv->vval.v_number = nr; } @@ -16648,7 +16678,7 @@ static bool set_ref_in_callback(Callback *callback, int copyID, /// "timer_start(timeout, callback, opts)" function static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - long timeout = get_tv_number(&argvars[0]); + const long timeout = tv_get_number(&argvars[0]); timer_T *timer; int repeat = 1; dict_T *dict; @@ -16663,7 +16693,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dictitem_T *const di = tv_dict_find(dict, S_LEN("repeat")); if (di != NULL) { - repeat = get_tv_number(&di->di_tv); + repeat = tv_get_number(&di->di_tv); if (repeat == 0) { repeat = 1; } @@ -16703,7 +16733,7 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - timer_T *timer = pmap_get(uint64_t)(timers, get_tv_number(&argvars[0])); + timer_T *timer = pmap_get(uint64_t)(timers, tv_get_number(&argvars[0])); if (timer == NULL) { return; @@ -17177,29 +17207,29 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { dictitem_T *di; if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) { - curwin->w_cursor.lnum = get_tv_number(&di->di_tv); + curwin->w_cursor.lnum = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) { - curwin->w_cursor.col = get_tv_number(&di->di_tv); + curwin->w_cursor.col = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) { - curwin->w_cursor.coladd = get_tv_number(&di->di_tv); + curwin->w_cursor.coladd = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) { - curwin->w_curswant = get_tv_number(&di->di_tv); + curwin->w_curswant = tv_get_number(&di->di_tv); curwin->w_set_curswant = false; } if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) { - set_topline(curwin, get_tv_number(&di->di_tv)); + set_topline(curwin, tv_get_number(&di->di_tv)); } if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) { - curwin->w_topfill = get_tv_number(&di->di_tv); + curwin->w_topfill = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) { - curwin->w_leftcol = get_tv_number(&di->di_tv); + curwin->w_leftcol = tv_get_number(&di->di_tv); } if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) { - curwin->w_skipcol = get_tv_number(&di->di_tv); + curwin->w_skipcol = tv_get_number(&di->di_tv); } check_cursor(); @@ -17462,8 +17492,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = get_tv_number_chk(&argvars[0], NULL) - ^ get_tv_number_chk(&argvars[1], NULL); + rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL) + ^ tv_get_number_chk(&argvars[1], NULL); } @@ -18408,83 +18438,6 @@ void free_tv(typval_T *varp) // TODO(ZyX-I): move to eval/typval -/// Get the number value of a variable -/// -/// @note Use get_tv_number_chk() if you need to determine whether there was an -/// error. -/// -/// @param[in] varp Variable to get value from. -/// -/// @return Number value: vim_str2nr() output for VAR_STRING variables, value -/// for VAR_NUMBER variables, -1 for other types. -varnumber_T get_tv_number(const typval_T *const varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - bool error = false; - return get_tv_number_chk(varp, &error); -} - -varnumber_T get_tv_number_chk(const typval_T *const varp, bool *const denote) -{ - varnumber_T n = 0; - - switch (varp->v_type) { - case VAR_NUMBER: { - return varp->vval.v_number; - } - case VAR_FLOAT: { - EMSG(_("E805: Using a Float as a Number")); - break; - } - case VAR_PARTIAL: - case VAR_FUNC: { - EMSG(_("E703: Using a Funcref as a Number")); - break; - } - case VAR_STRING: { - if (varp->vval.v_string != NULL) { - long nr; - vim_str2nr(varp->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); - n = (varnumber_T)nr; - } - return n; - } - case VAR_LIST: { - EMSG(_("E745: Using a List as a Number")); - break; - } - case VAR_DICT: { - EMSG(_("E728: Using a Dictionary as a Number")); - break; - } - case VAR_SPECIAL: { - switch (varp->vval.v_special) { - case kSpecialVarTrue: { - return 1; - } - case kSpecialVarFalse: - case kSpecialVarNull: { - return 0; - } - } - break; - } - case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_number(UNKNOWN)"); - break; - } - } - if (denote == NULL) { - // useful for values that must be unsigned - n = -1; - } else { - *denote = true; - } - return n; -} - -// TODO(ZyX-I): move to eval/typval - /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! char_u *get_tv_string_chk(const typval_T *varp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT @@ -18981,7 +18934,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } return; } else if (v->di_tv.v_type == VAR_NUMBER) { - v->di_tv.vval.v_number = get_tv_number(tv); + v->di_tv.vval.v_number = tv_get_number(tv); if (strcmp(varname, "searchforward") == 0) { set_search_direction(v->di_tv.vval.v_number ? '/' : '?'); } else if (strcmp(varname, "hlsearch") == 0) { diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index d2d0873792..ec6c86ac64 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -50,7 +50,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } if (*op == '+' || *op == '-') { // nr += nr or nr -= nr - varnumber_T n = get_tv_number(tv1); + varnumber_T n = tv_get_number(tv1); if (tv2->v_type == VAR_FLOAT) { float_T f = n; @@ -64,9 +64,9 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, tv1->vval.v_float = f; } else { if (*op == '+') { - n += get_tv_number(tv2); + n += tv_get_number(tv2); } else { - n -= get_tv_number(tv2); + n -= tv_get_number(tv2); } tv_clear(tv1); tv1->v_type = VAR_NUMBER; @@ -96,7 +96,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, } const float_T f = (tv2->v_type == VAR_FLOAT ? tv2->vval.v_float - : get_tv_number(tv2)); + : tv_get_number(tv2)); if (*op == '+') { tv1->vval.v_float += f; } else { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 38032762dc..087e76de10 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -17,6 +17,7 @@ #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/pos.h" +#include "nvim/charset.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck @@ -725,7 +726,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) } return -1; } - return get_tv_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(&li->li_tv, ret_error); } /// Get list item l[n - 1] as a string @@ -1087,7 +1088,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) if (di == NULL) { return 0; } - return get_tv_number(&di->di_tv); + return tv_get_number(&di->di_tv); } /// Get a string item from a dictionary @@ -1971,7 +1972,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// Check that given value is a number or string /// -/// Error messages are compatible with get_tv_number() previously used for the +/// Error messages are compatible with tv_get_number() previously used for the /// same purpose in buf*() functions. Special values are not accepted (previous /// behaviour: silently fail to find buffer). /// @@ -2016,8 +2017,127 @@ bool tv_check_str_or_nr(const typval_T *const tv) return false; } +#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_UNKNOWN]=N_("E685: using an invalid value as a Number"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a number or can be converted to it +/// +/// Error messages are compatible with tv_get_number() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_num(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(num_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get +/// Get the number value of a VimL object +/// +/// @note Use tv_get_number_chk() if you need to determine whether there was an +/// error. +/// +/// @param[in] tv Object to get value from. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 for other types. +varnumber_T tv_get_number(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool error = false; + return tv_get_number_chk(tv, &error); +} + +/// Get the number value of a VimL object +/// +/// @param[in] tv Object to get value from. +/// @param[out] ret_error If type error occurred then `true` will be written +/// to this location. Otherwise it is not touched. +/// +/// @note Needs to be initialized to `false` to be +/// useful. +/// +/// @return Number value: vim_str2nr() output for VAR_STRING objects, value +/// for VAR_NUMBER objects, -1 (ret_error == NULL) or 0 (otherwise) for +/// other types. +varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + switch (tv->v_type) { + case VAR_FUNC: + case VAR_PARTIAL: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: { + EMSG(_(num_errors[tv->v_type])); + break; + } + case VAR_NUMBER: { + return tv->vval.v_number; + } + case VAR_STRING: { + varnumber_T n = 0; + if (tv->vval.v_string != NULL) { + long nr; + vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &nr, NULL, 0); + n = (varnumber_T)nr; + } + return n; + } + case VAR_SPECIAL: { + switch (tv->vval.v_special) { + case kSpecialVarTrue: { + return 1; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + return 0; + } + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + break; + } + } + if (ret_error != NULL) { + *ret_error = true; + } + return (ret_error == NULL ? -1 : 0); +} + /// Get the line number from VimL object /// /// @param[in] tv Object to get value from. Is expected to be a number or @@ -2028,7 +2148,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) linenr_T tv_get_lnum(const typval_T *const tv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - linenr_T lnum = get_tv_number_chk(tv, NULL); + linenr_T lnum = tv_get_number_chk(tv, NULL); if (lnum == 0) { // No valid number, try using same function as line() does. int fnum; pos_T *const fp = var2fpos(tv, true, &fnum); @@ -2078,7 +2198,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float()"); + EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 9cfa126a56..e20979c307 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -576,7 +576,7 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) } else { (*idxp)++; bool err = false; - n = (varnumber_T)get_tv_number_chk(&tvs[idx], &err); + n = tv_get_number_chk(&tvs[idx], &err); if (err) { n = 0; } diff --git a/src/nvim/window.c b/src/nvim/window.c index a3b0e6fc2d..6020159af9 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5593,7 +5593,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, if (subli == NULL) { goto fail; } - lnum = get_tv_number_chk(&subli->li_tv, &error); + lnum = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5604,13 +5604,13 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, m->pos.pos[i].lnum = lnum; subli = subli->li_next; if (subli != NULL) { - col = get_tv_number_chk(&subli->li_tv, &error); + col = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } subli = subli->li_next; if (subli != NULL) { - len = get_tv_number_chk(&subli->li_tv, &error); + len = tv_get_number_chk(&subli->li_tv, &error); if (error) { goto fail; } @@ -5810,14 +5810,14 @@ int win_getid(typval_T *argvars) if (argvars[0].v_type == VAR_UNKNOWN) { return curwin->handle; } - int winnr = get_tv_number(&argvars[0]); + int winnr = tv_get_number(&argvars[0]); win_T *wp; if (winnr > 0) { if (argvars[1].v_type == VAR_UNKNOWN) { wp = firstwin; } else { tabpage_T *tp = NULL; - int tabnr = get_tv_number(&argvars[1]); + int tabnr = tv_get_number(&argvars[1]); FOR_ALL_TABS(tp2) { if (--tabnr == 0) { tp = tp2; @@ -5844,7 +5844,7 @@ int win_getid(typval_T *argvars) int win_gotoid(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5879,7 +5879,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) { int winnr = 1; int tabnr = 1; - int id = get_tv_number(&argvars[0]); + handle_T id = (handle_T)tv_get_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); tv_list_append_number(list, tabnr); @@ -5888,7 +5888,7 @@ void win_id2tabwin(typval_T *argvars, list_T *list) win_T * win_id2wp(typval_T *argvars) { - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { @@ -5902,7 +5902,7 @@ win_T * win_id2wp(typval_T *argvars) int win_id2win(typval_T *argvars) { int nr = 1; - int id = get_tv_number(&argvars[0]); + int id = tv_get_number(&argvars[0]); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->handle == id) { @@ -5915,7 +5915,7 @@ int win_id2win(typval_T *argvars) void win_findbuf(typval_T *argvars, list_T *list) { - int bufnr = get_tv_number(&argvars[0]); + int bufnr = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer->b_fnum == bufnr) { -- cgit From 50ebd1dff5c4e995c4f7e7980870e43d9defabc6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 28 Aug 2016 09:15:28 +0300 Subject: eval: Move free_tv to eval/typval.h, remove most of its usages --- src/nvim/api/vim.c | 11 ++++++----- src/nvim/eval.c | 53 ++++++-------------------------------------------- src/nvim/eval/typval.c | 40 +++++++++++++++++++++++++++++++++++++ src/nvim/ex_eval.c | 15 +++++++------- src/nvim/quickfix.c | 13 ++++++------- 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index db2f25a2a6..975446057c 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -182,19 +182,20 @@ Object nvim_eval(String expr, Error *err) Object rv = OBJECT_INIT; // Evaluate the expression try_start(); - typval_T *expr_result = eval_expr((char_u *)expr.data, NULL); - if (!expr_result) { + typval_T rettv; + if (eval0((char_u *)expr.data, &rettv, NULL, true) == FAIL) { api_set_error(err, Exception, "Failed to evaluate expression"); } if (!try_end(err)) { // No errors, convert the result - rv = vim_to_object(expr_result); + rv = vim_to_object(&rettv); } - // Free the vim object - free_tv(expr_result); + // Free the Vim object + tv_clear(&rettv); + return rv; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 676df1a301..65bb90fc15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -719,13 +719,12 @@ int current_func_returned(void) */ void set_internal_string_var(char_u *name, char_u *value) { - char_u *val = vim_strsave(value); - typval_T *tvp = xcalloc(1, sizeof(typval_T)); + const typval_T tv = { + .v_type = VAR_STRING, + .vval.v_string = value, + }; - tvp->v_type = VAR_STRING; - tvp->vval.v_string = val; - set_var((const char *)name, tvp, false); - free_tv(tvp); + set_var((const char *)name, (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -3264,7 +3263,7 @@ typedef enum { * Note: "rettv.v_lock" is not set. * Return OK or FAIL. */ -static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) +int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; char_u *p; @@ -18404,38 +18403,6 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -/* - * Free the memory for a variable type-value. - */ -void free_tv(typval_T *varp) -{ - if (varp != NULL) { - switch (varp->v_type) { - case VAR_FUNC: - func_unref(varp->vval.v_string); - // FALLTHROUGH - case VAR_STRING: - xfree(varp->vval.v_string); - break; - case VAR_PARTIAL: - partial_unref(varp->vval.v_partial); - break; - case VAR_LIST: - tv_list_unref(varp->vval.v_list); - break; - case VAR_DICT: - tv_dict_unref(varp->vval.v_dict); - break; - case VAR_SPECIAL: - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_UNKNOWN: - break; - } - xfree(varp); - } -} - // TODO(ZyX-I): move to eval/typval /// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! @@ -21525,14 +21492,6 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) return idx < 0; } -/* - * Free the variable with a pending return value. - */ -void discard_pending_return(void *rettv) -{ - free_tv((typval_T *)rettv); -} - /* * Generate a return command for producing the value of "rettv". The result * is an allocated string. Used by report_pending() for verbose messages. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 087e76de10..ca635dcae9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1742,6 +1742,46 @@ void tv_clear(typval_T *tv) } } +//{{{3 Free + +/// Free allocated VimL object and value stored inside +/// +/// @param tv Object to free. +void tv_free(typval_T *tv) +{ + if (tv != NULL) { + switch (tv->v_type) { + case VAR_PARTIAL: { + partial_unref(tv->vval.v_partial); + break; + } + case VAR_FUNC: { + func_unref(tv->vval.v_string); + // FALLTHROUGH + } + case VAR_STRING: { + xfree(tv->vval.v_string); + break; + } + case VAR_LIST: { + tv_list_unref(tv->vval.v_list); + break; + } + case VAR_DICT: { + tv_dict_unref(tv->vval.v_dict); + break; + } + case VAR_SPECIAL: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_UNKNOWN: { + break; + } + } + xfree(tv); + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 3f71ae1795..65112c4dd8 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -14,6 +14,7 @@ #include "nvim/ex_eval.h" #include "nvim/charset.h" #include "nvim/eval.h" +#include "nvim/eval/typval.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/message.h" @@ -21,8 +22,6 @@ #include "nvim/regexp.h" #include "nvim/strings.h" - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.c.generated.h" #endif @@ -59,12 +58,14 @@ * is an error exception.) - The macros can be defined as expressions checking * for a variable that is allowed to be changed during execution of a script. */ -/* Values used for the Vim release. */ -# define THROW_ON_ERROR TRUE -# define THROW_ON_ERROR_TRUE -# define THROW_ON_INTERRUPT TRUE -# define THROW_ON_INTERRUPT_TRUE +// Values used for the Vim release. +#define THROW_ON_ERROR true +#define THROW_ON_ERROR_TRUE +#define THROW_ON_INTERRUPT true +#define THROW_ON_INTERRUPT_TRUE + +#define discard_pending_return(p) tv_free((typval_T *)(p)) /* * When several errors appear in a row, setting "force_abort" is delayed until diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 06ac2821b0..4fa5c85abd 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4372,7 +4372,6 @@ void ex_cbuffer(exarg_T *eap) */ void ex_cexpr(exarg_T *eap) { - typval_T *tv; qf_info_T *qi = &ql_info; const char *au_name = NULL; @@ -4412,11 +4411,11 @@ void ex_cexpr(exarg_T *eap) /* Evaluate the expression. When the result is a string or a list we can * use it to fill the errorlist. */ - tv = eval_expr(eap->arg, NULL); - if (tv != NULL) { - if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) - || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL)) { - if (qf_init_ext(qi, NULL, NULL, tv, p_efm, + typval_T tv; + if (eval0(eap->arg, &tv, NULL, true) != FAIL) { + if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) + || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { @@ -4431,7 +4430,7 @@ void ex_cexpr(exarg_T *eap) } else { EMSG(_("E777: String or List expected")); } - free_tv(tv); + tv_clear(&tv); } } -- cgit From c8e63a8db84e9d9f7bd855085a87d93631504fc7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:25:24 +0300 Subject: eval: Move remaining get_tv_string* functions to eval/typval.c --- src/nvim/ascii.h | 8 +- src/nvim/charset.c | 8 +- src/nvim/edit.c | 204 ++-- src/nvim/eval.c | 1280 ++++++++++---------- src/nvim/eval/typval.c | 118 +- src/nvim/eval/typval.h | 1 + src/nvim/ex_cmds.c | 4 +- src/nvim/ex_docmd.c | 2 +- src/nvim/ex_getln.c | 31 +- src/nvim/fileio.c | 37 +- src/nvim/getchar.c | 159 ++- src/nvim/globals.h | 1 + src/nvim/mbyte.c | 4 +- src/nvim/normal.c | 71 +- src/nvim/ops.c | 60 +- src/nvim/option.c | 19 +- src/nvim/os/fs.c | 4 +- src/nvim/regexp.c | 15 +- src/nvim/screen.c | 2 +- src/nvim/spell.c | 82 +- src/nvim/spellfile.c | 54 +- src/nvim/strings.c | 11 +- src/nvim/undo.c | 4 +- test/functional/eval/input_spec.lua | 38 + test/functional/eval/match_functions_spec.lua | 15 + .../functional/ex_cmds/dict_notifications_spec.lua | 67 +- 26 files changed, 1244 insertions(+), 1055 deletions(-) create mode 100644 test/functional/eval/input_spec.lua diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 31851a84e6..37b83efb61 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -20,15 +20,13 @@ #define BS '\010' #define TAB '\011' #define NL '\012' -#define NL_STR (char_u *)"\012" +#define NL_STR "\012" #define FF '\014' #define CAR '\015' /* CR is used by Mac OS X */ #define ESC '\033' -#define ESC_STR (char_u *)"\033" -#define ESC_STR_nc "\033" +#define ESC_STR "\033" #define DEL 0x7f -#define DEL_STR (char_u *)"\177" -#define CSI 0x9b /* Control Sequence Introducer */ +#define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ #define STERM 0x9c /* String Terminator */ diff --git a/src/nvim/charset.c b/src/nvim/charset.c index cb6a9fa43c..6c22108853 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -90,7 +90,6 @@ int buf_init_chartab(buf_T *buf, int global) { int c; int c2; - char_u *p; int i; bool tilde; bool do_isalpha; @@ -144,7 +143,8 @@ int buf_init_chartab(buf_T *buf, int global) // Walk through the 'isident', 'iskeyword', 'isfname' and 'isprint' // options Each option is a list of characters, character numbers or // ranges, separated by commas, e.g.: "200-210,x,#-178,-" - for (i = global ? 0 : 3; i <= 3; ++i) { + for (i = global ? 0 : 3; i <= 3; i++) { + const char_u *p; if (i == 0) { // first round: 'isident' p = p_isi; @@ -169,7 +169,7 @@ int buf_init_chartab(buf_T *buf, int global) } if (ascii_isdigit(*p)) { - c = getdigits_int(&p); + c = getdigits_int((char_u **)&p); } else { c = mb_ptr2char_adv(&p); } @@ -179,7 +179,7 @@ int buf_init_chartab(buf_T *buf, int global) ++p; if (ascii_isdigit(*p)) { - c2 = getdigits_int(&p); + c2 = getdigits_int((char_u **)&p); } else { c2 = mb_ptr2char_adv(&p); } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index b396251051..da09aed3dc 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1423,7 +1423,7 @@ static void ins_ctrl_v(void) edit_putchar('^', TRUE); did_putchar = TRUE; } - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + AppendToRedobuff(CTRL_V_STR); add_to_showcmd_c(Ctrl_V); @@ -1977,7 +1977,6 @@ static bool ins_compl_accept_char(int c) */ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int dir, int flags) { - char_u *p; int i, c; int actual_len; /* Take multi-byte characters */ int actual_compl_length; /* into account. */ @@ -1987,11 +1986,11 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int int was_letter = FALSE; if (p_ic && curbuf->b_p_inf && len > 0) { - /* Infer case of completed part. */ + // Infer case of completed part. - /* Find actual length of completion. */ + // Find actual length of completion. if (has_mbyte) { - p = str; + const char_u *p = str; actual_len = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2002,7 +2001,7 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Find actual length of original text. */ if (has_mbyte) { - p = compl_orig_text; + const char_u *p = compl_orig_text; actual_compl_length = 0; while (*p != NUL) { mb_ptr_adv(p); @@ -2018,27 +2017,35 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int /* Allocate wide character array for the completion and fill it. */ wca = xmalloc(actual_len * sizeof(*wca)); - p = str; - for (i = 0; i < actual_len; ++i) - if (has_mbyte) - wca[i] = mb_ptr2char_adv(&p); - else - wca[i] = *(p++); + { + const char_u *p = str; + for (i = 0; i < actual_len; i++) { + if (has_mbyte) { + wca[i] = mb_ptr2char_adv(&p); + } else { + wca[i] = *(p++); + } + } + } - /* Rule 1: Were any chars converted to lower? */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) { - has_lower = TRUE; - if (vim_isupper(wca[i])) { - /* Rule 1 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) - wca[i] = vim_tolower(wca[i]); - break; + // Rule 1: Were any chars converted to lower? + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + has_lower = true; + if (vim_isupper(wca[i])) { + // Rule 1 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { + wca[i] = vim_tolower(wca[i]); + } + break; + } } } } @@ -2048,49 +2055,57 @@ int ins_compl_add_infercase(char_u *str, int len, int icase, char_u *fname, int * upper case. */ if (!has_lower) { - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { c = mb_ptr2char_adv(&p); - else + } else { c = *(p++); + } if (was_letter && vim_isupper(c) && vim_islower(wca[i])) { - /* Rule 2 is satisfied. */ - for (i = actual_compl_length; i < actual_len; ++i) + // Rule 2 is satisfied. + for (i = actual_compl_length; i < actual_len; i++) { wca[i] = vim_toupper(wca[i]); + } break; } was_letter = vim_islower(c) || vim_isupper(c); } } - /* Copy the original case of the part we typed. */ - p = compl_orig_text; - for (i = 0; i < min_len; ++i) { - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else - c = *(p++); - if (vim_islower(c)) - wca[i] = vim_tolower(wca[i]); - else if (vim_isupper(c)) - wca[i] = vim_toupper(wca[i]); + // Copy the original case of the part we typed. + { + const char_u *p = compl_orig_text; + for (i = 0; i < min_len; i++) { + if (has_mbyte) { + c = mb_ptr2char_adv(&p); + } else { + c = *(p++); + } + if (vim_islower(c)) { + wca[i] = vim_tolower(wca[i]); + } else if (vim_isupper(c)) { + wca[i] = vim_toupper(wca[i]); + } + } } - /* - * Generate encoding specific output from wide character array. - * Multi-byte characters can occupy up to five bytes more than - * ASCII characters, and we also need one byte for NUL, so stay - * six bytes away from the edge of IObuff. - */ - p = IObuff; - i = 0; - while (i < actual_len && (p - IObuff + 6) < IOSIZE) - if (has_mbyte) - p += (*mb_char2bytes)(wca[i++], p); - else - *(p++) = wca[i++]; - *p = NUL; + // Generate encoding specific output from wide character array. + // Multi-byte characters can occupy up to five bytes more than + // ASCII characters, and we also need one byte for NUL, so stay + // six bytes away from the edge of IObuff. + { + char_u *p = IObuff; + i = 0; + while (i < actual_len && (p - IObuff + 6) < IOSIZE) { + if (has_mbyte) { + p += (*mb_char2bytes)(wca[i++], p); + } else { + *(p++) = wca[i++]; + } + } + *p = NUL; + } xfree(wca); @@ -3594,7 +3609,7 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); aempty = (bool)tv_dict_get_number(tv->vval.v_dict, "empty"); } else { - word = (const char *)get_tv_string_chk(tv); + word = (const char *)tv_get_string_chk(tv); memset(cptext, 0, sizeof(cptext)); } if (word == NULL || (!aempty && *word == NUL)) { @@ -5785,15 +5800,16 @@ comp_textwidth ( */ static void redo_literal(int c) { - char_u buf[10]; + char buf[10]; - /* Only digits need special treatment. Translate them into a string of - * three digits. */ + // Only digits need special treatment. Translate them into a string of + // three digits. if (ascii_isdigit(c)) { - vim_snprintf((char *)buf, sizeof(buf), "%03d", c); + vim_snprintf(buf, sizeof(buf), "%03d", c); AppendToRedobuff(buf); - } else + } else { AppendCharToRedobuff(c); + } } // start_arrow() is called when an arrow key is used in insert mode. @@ -5822,8 +5838,8 @@ static void start_arrow_common(pos_T *end_insert_pos, bool end_change) { if (!arrow_used && end_change) { // something has been inserted AppendToRedobuff(ESC_STR); - stop_insert(end_insert_pos, FALSE, FALSE); - arrow_used = TRUE; /* this means we stopped the current insert */ + stop_insert(end_insert_pos, false, false); + arrow_used = true; // This means we stopped the current insert. } check_spell_redraw(); } @@ -5880,7 +5896,7 @@ int stop_arrow(void) vr_lines_changed = 1; } ResetRedobuff(); - AppendToRedobuff((char_u *)"1i"); /* pretend we start an insertion */ + AppendToRedobuff("1i"); // Pretend we start an insertion. new_insert_skip = 2; } else if (ins_need_undo) { if (u_save_cursor() == OK) @@ -6345,12 +6361,13 @@ stuff_inserted ( } do { - stuffReadbuff(ptr); - /* a trailing "0" is inserted as "048", "^" as "^" */ - if (last) - stuffReadbuff((char_u *)(last == '0' - ? "\026\060\064\070" - : "\026^")); + stuffReadbuff((const char *)ptr); + // A trailing "0" is inserted as "048", "^" as "^". + if (last) { + stuffReadbuff((last == '0' + ? "\026\060\064\070" + : "\026^")); + } } while (--count > 0); if (last) @@ -7143,13 +7160,12 @@ static bool ins_esc(long *count, int cmdchar, bool nomove) disabled_redraw = false; } if (!arrow_used) { - /* - * Don't append the ESC for "r" and "grx". - * When 'insertmode' is set only CTRL-L stops Insert mode. Needed for - * when "count" is non-zero. - */ - if (cmdchar != 'r' && cmdchar != 'v') - AppendToRedobuff(p_im ? (char_u *)"\014" : ESC_STR); + // Don't append the ESC for "r" and "grx". + // When 'insertmode' is set only CTRL-L stops Insert mode. Needed for + // when "count" is non-zero. + if (cmdchar != 'r' && cmdchar != 'v') { + AppendToRedobuff(p_im ? "\014" : ESC_STR); + } /* * Repeating insert may take a long time. Check for @@ -7303,7 +7319,8 @@ static bool ins_start_select(int c) // Execute the key in (insert) Select mode. stuffcharReadbuff(Ctrl_O); if (mod_mask) { - char_u buf[4] = { K_SPECIAL, KS_MODIFIER, mod_mask, NUL }; + const char buf[] = { (char)K_SPECIAL, (char)KS_MODIFIER, + (char)(uint8_t)mod_mask, NUL }; stuffReadbuff(buf); } stuffcharReadbuff(c); @@ -8111,11 +8128,11 @@ static bool ins_tab(void) return true; } - did_ai = FALSE; - did_si = FALSE; - can_si = FALSE; - can_si_back = FALSE; - AppendToRedobuff((char_u *)"\t"); + did_ai = false; + did_si = false; + can_si = false; + can_si_back = false; + AppendToRedobuff("\t"); if (p_sta && ind) { // insert tab in indent, use "shiftwidth" temp = get_sw_value(curbuf); @@ -8380,8 +8397,8 @@ static int ins_digraph(void) edit_unputchar(); } if (cc != ESC) { - AppendToRedobuff((char_u *)CTRL_V_STR); - c = getdigraph(c, cc, TRUE); + AppendToRedobuff(CTRL_V_STR); + c = getdigraph(c, cc, true); clear_showcmd(); return c; } @@ -8443,12 +8460,13 @@ static int ins_ctrl_ey(int tc) if (c != NUL) { long tw_save; - /* The character must be taken literally, insert like it - * was typed after a CTRL-V, and pretend 'textwidth' - * wasn't set. Digits, 'o' and 'x' are special after a - * CTRL-V, don't use it for these. */ - if (c < 256 && !isalnum(c)) - AppendToRedobuff((char_u *)CTRL_V_STR); /* CTRL-V */ + // The character must be taken literally, insert like it + // was typed after a CTRL-V, and pretend 'textwidth' + // wasn't set. Digits, 'o' and 'x' are special after a + // CTRL-V, don't use it for these. + if (c < 256 && !isalnum(c)) { + AppendToRedobuff(CTRL_V_STR); + } tw_save = curbuf->b_p_tw; curbuf->b_p_tw = -1; insert_special(c, TRUE, FALSE); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 65bb90fc15..3a0075e48a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -173,7 +173,6 @@ static char *e_funcref = N_("E718: Funcref required"); static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary"); static char *e_nofunc = N_("E130: Unknown function: %s"); static char *e_illvar = N_("E461: Illegal variable name: %s"); -static char *e_float_as_string = N_("E806: using Float as a String"); static const char *e_readonlyvar = N_( "E46: Cannot change read-only variable \"%.*s\""); @@ -1852,7 +1851,6 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, const char_u *const op) FUNC_ATTR_NONNULL_ARG(1, 2) FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *name; char_u *arg_end = NULL; int len; int opt_flags; @@ -1864,7 +1862,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (*arg == '$') { // Find the end of the name. arg++; - name = arg; + char *name = (char *)arg; len = get_env_len((const char_u **)&arg); if (len == 0) { EMSG2(_(e_invarg2), name - 1); @@ -1875,26 +1873,28 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, && vim_strchr(endchars, *skipwhite(arg)) == NULL) { EMSG(_(e_letunexp)); } else if (!check_secure()) { - const char_u c1 = name[len]; + const char c1 = name[len]; name[len] = NUL; - char_u *p = get_tv_string_chk(tv); + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { - char *s = vim_getenv((char *)name); + char *s = vim_getenv(name); if (s != NULL) { - p = tofree = concat_str((char_u *)s, p); + tofree = concat_str((const char_u *)s, (const char_u *)p); + p = (const char *)tofree; xfree(s); } } if (p != NULL) { - vim_setenv((char *)name, (char *)p); - if (STRICMP(name, "HOME") == 0) + vim_setenv(name, p); + if (STRICMP(name, "HOME") == 0) { init_homedir(); - else if (didset_vim && STRICMP(name, "VIM") == 0) - didset_vim = FALSE; - else if (didset_vimruntime - && STRICMP(name, "VIMRUNTIME") == 0) - didset_vimruntime = FALSE; + } else if (didset_vim && STRICMP(name, "VIM") == 0) { + didset_vim = false; + } else if (didset_vimruntime + && STRICMP(name, "VIMRUNTIME") == 0) { + didset_vimruntime = false; + } arg_end = arg; } name[len] = c1; @@ -1914,16 +1914,16 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, } else { int opt_type; long numval; - char_u *stringval = NULL; - char_u *s; + char *stringval = NULL; const char c1 = *p; *p = NUL; varnumber_T n = tv_get_number(tv); - s = get_tv_string_chk(tv); // != NULL if number or string. + const char *s = tv_get_string_chk(tv); // != NULL if number or string. if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, &stringval, opt_flags); + opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { EMSG2(_(e_letwrong), op); @@ -1935,44 +1935,45 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, n = numval - n; } } else if (opt_type == 0 && stringval != NULL) { // string - s = concat_str(stringval, s); - xfree(stringval); - stringval = s; + char *const oldstringval = stringval; + stringval = (char *)concat_str((const char_u *)stringval, + (const char_u *)s); + xfree(oldstringval); + s = stringval; } } } if (s != NULL) { - set_option_value((const char *)arg, n, (char *)s, opt_flags); + set_option_value((const char *)arg, n, s, opt_flags); arg_end = (char_u *)p; } *p = c1; xfree(stringval); } - } - /* - * ":let @r = expr": Set register contents. - */ - else if (*arg == '@') { - ++arg; - if (op != NULL && (*op == '+' || *op == '-')) - EMSG2(_(e_letwrong), op); - else if (endchars != NULL - && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) - EMSG(_(e_letunexp)); - else { - char_u *ptofree = NULL; + // ":let @r = expr": Set register contents. + } else if (*arg == '@') { + arg++; + if (op != NULL && (*op == '+' || *op == '-')) { + emsgf(_(e_letwrong), op); + } else if (endchars != NULL + && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { + emsgf(_(e_letunexp)); + } else { char_u *s; - char_u *p = get_tv_string_chk(tv); + char_u *ptofree = NULL; + const char *p = tv_get_string_chk(tv); if (p != NULL && op != NULL && *op == '.') { s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc); if (s != NULL) { - p = ptofree = concat_str(s, p); + ptofree = concat_str(s, (const char_u *)p); + p = (const char *)ptofree; xfree(s); } } if (p != NULL) { - write_reg_contents(*arg == '@' ? '"' : *arg, p, STRLEN(p), false); + write_reg_contents(*arg == '@' ? '"' : *arg, + (const char_u *)p, STRLEN(p), false); arg_end = arg + 1; } xfree(ptofree); @@ -2129,13 +2130,14 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { /* Get the index [expr] or the first index [expr: ]. */ p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ + if (*p == ':') { + empty1 = true; + } else { + empty1 = false; + if (eval1(&p, &var1, true) == FAIL) { // Recursive! return NULL; - if (get_tv_string_chk(&var1) == NULL) { + } + if (!tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return NULL; @@ -2174,7 +2176,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } return NULL; } - if (get_tv_string_chk(&var2) == NULL) { + if (!tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -3810,16 +3812,15 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) break; if ((op != '+' || rettv->v_type != VAR_LIST) - && (op == '.' || rettv->v_type != VAR_FLOAT) - ) { - /* For "list + ...", an illegal use of the first operand as - * a number cannot be determined before evaluating the 2nd - * operand: if this is also a list, all is ok. - * For "something . ...", "something - ..." or "non-list + ...", - * we know that the first operand needs to be a string or number - * without evaluating the 2nd operand. So check before to avoid - * side effects after an error. */ - if (evaluate && get_tv_string_chk(rettv) == NULL) { + && (op == '.' || rettv->v_type != VAR_FLOAT)) { + // For "list + ...", an illegal use of the first operand as + // a number cannot be determined before evaluating the 2nd + // operand: if this is also a list, all is ok. + // For "something . ...", "something - ..." or "non-list + ...", + // we know that the first operand needs to be a string or number + // without evaluating the 2nd operand. So check before to avoid + // side effects after an error. + if (evaluate && !tv_check_str(rettv)) { tv_clear(rettv); return FAIL; } @@ -3840,10 +3841,10 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) */ if (op == '.') { char buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf2[NUMBUFLEN]; // s1 already checked const char *const s1 = tv_get_string_buf(rettv, buf1); - const char *const s2 = (const char *)get_tv_string_buf_chk(&var2, buf2); + const char *const s2 = tv_get_string_buf_chk(&var2, buf2); if (s2 == NULL) { // Type error? tv_clear(rettv); tv_clear(&var2); @@ -4411,7 +4412,7 @@ eval_index ( empty1 = true; } else if (eval1(arg, &var1, evaluate) == FAIL) { // Recursive! return FAIL; - } else if (evaluate && get_tv_string_chk(&var1) == NULL) { + } else if (evaluate && !tv_check_str(&var1)) { // Not a number or string. tv_clear(&var1); return FAIL; @@ -4430,7 +4431,7 @@ eval_index ( tv_clear(&var1); } return FAIL; - } else if (evaluate && get_tv_string_chk(&var2) == NULL) { + } else if (evaluate && !tv_check_str(&var2)) { // Not a number or string. if (!empty1) { tv_clear(&var1); @@ -4566,7 +4567,7 @@ eval_index ( } if (len == -1) { - key = get_tv_string_chk(&var1); + key = (char_u *)tv_get_string_chk(&var1); if (key == NULL) { tv_clear(&var1); return FAIL; @@ -5570,7 +5571,7 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) char_u *key = NULL; dictitem_T *item; char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; /* * First check if it's not a curly-braces thing: {expr}. @@ -5602,9 +5603,9 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) goto failret; } if (evaluate) { - key = get_tv_string_buf_chk(&tvkey, buf); + key = (char_u *)tv_get_string_buf_chk(&tvkey, buf); if (key == NULL) { - // "key" is NULL when get_tv_string_buf_chk() gave an errmsg + // "key" is NULL when tv_get_string_buf_chk() gave an errmsg tv_clear(&tvkey); goto failret; } @@ -6598,7 +6599,6 @@ static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) { long lnum; - char_u *line; list_T *l = NULL; listitem_T *li = NULL; typval_T *tv; @@ -6622,21 +6622,23 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = l->lv_first; } for (;; ) { - if (l == NULL) - tv = &argvars[1]; /* append a string */ - else if (li == NULL) - break; /* end of list */ - else - tv = &li->li_tv; /* append item from list */ - line = get_tv_string_chk(tv); - if (line == NULL) { /* type error */ - rettv->vval.v_number = 1; /* Failed */ + if (l == NULL) { + tv = &argvars[1]; // Append a string. + } else if (li == NULL) { + break; // End of list. + } else { + tv = &li->li_tv; // Append item from list. + } + const char *const line = tv_get_string_chk(tv); + if (line == NULL) { // Type error. + rettv->vval.v_number = 1; // Failed. break; } - ml_append(lnum + added, line, (colnr_T)0, FALSE); - ++added; - if (l == NULL) + ml_append(lnum + added, (char_u *)line, (colnr_T)0, false); + added++; + if (l == NULL) { break; + } li = li->li_next; } @@ -6802,7 +6804,7 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) { garray_T ga; - char *error = (char *)get_tv_string_chk(&argvars[0]); + const char *const error = tv_get_string_chk(&argvars[0]); if (vimvars[VV_EXCEPTION].vv_str == NULL) { prepare_assert_error(&ga); ga_concat(&ga, (char_u *)"v:exception is not set"); @@ -6821,22 +6823,22 @@ static void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "assert_fails(cmd [, error])" function static void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *cmd = get_tv_string_chk(&argvars[0]); + const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; called_emsg = false; suppress_errthrow = true; emsg_silent = true; - do_cmdline_cmd((char *)cmd); + do_cmdline_cmd(cmd); if (!called_emsg) { prepare_assert_error(&ga); - ga_concat(&ga, (char_u *)"command did not fail: "); - ga_concat(&ga, cmd); + ga_concat(&ga, (const char_u *)"command did not fail: "); + ga_concat(&ga, (const char_u *)cmd); assert_error(&ga); ga_clear(&ga); } else if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char *error = (char *)get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + const char *const error = tv_get_string_buf_chk(&argvars[1], buf); if (error == NULL || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) { @@ -6911,14 +6913,15 @@ static void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void assert_match_common(typval_T *argvars, assert_type_T atype) { - char_u buf1[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - char_u *pat = get_tv_string_buf_chk(&argvars[0], buf1); - char_u *text = get_tv_string_buf_chk(&argvars[1], buf2); + char buf1[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); + const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); if (pat == NULL || text == NULL) { EMSG(_(e_invarg)); - } else if (pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { + } else if (pattern_match((char_u *)pat, (char_u *)text, false) + != (atype == ASSERT_MATCH)) { garray_T ga; prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); @@ -7107,7 +7110,6 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool error = false; - char_u *name; rettv->vval.v_number = -1; if (!tv_check_str_or_nr(&argvars[0])) { @@ -7119,13 +7121,14 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // If the buffer isn't found and the second argument is not zero create a // new buffer. + const char *name; if (buf == NULL && argvars[1].v_type != VAR_UNKNOWN && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = get_tv_string_chk(&argvars[0])) != NULL + && (name = tv_get_string_chk(&argvars[0])) != NULL && !error) { - buf = buflist_new(name, NULL, (linenr_T)1, 0); + buf = buflist_new((char_u *)name, NULL, 1, 0); } if (buf != NULL) { @@ -7190,24 +7193,23 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void byteidx(typval_T *argvars, typval_T *rettv, int comp) { - char_u *t; - char_u *str; - long idx; - - str = get_tv_string_chk(&argvars[0]); - idx = tv_get_number_chk(&argvars[1], NULL); + const char *const str = tv_get_string_chk(&argvars[0]); + varnumber_T idx = tv_get_number_chk(&argvars[1], NULL); rettv->vval.v_number = -1; - if (str == NULL || idx < 0) + if (str == NULL || idx < 0) { return; + } - t = str; + const char *t = str; for (; idx > 0; idx--) { - if (*t == NUL) /* EOL reached */ + if (*t == NUL) { // EOL reached. return; - if (enc_utf8 && comp) - t += utf_ptr2len(t); - else - t += (*mb_ptr2len)(t); + } + if (enc_utf8 && comp) { + t += utf_ptr2len((const char_u *)t); + } else { + t += (*mb_ptr2len)((const char_u *)t); + } } rettv->vval.v_number = (varnumber_T)(t - str); } @@ -7444,47 +7446,51 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *message; - char_u *buttons = NULL; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; + const char *message; + const char *buttons = NULL; int def = 1; int type = VIM_GENERIC; - char_u *typestr; + const char *typestr; bool error = false; - message = get_tv_string_chk(&argvars[0]); - if (message == NULL) - error = TRUE; + message = tv_get_string_chk(&argvars[0]); + if (message == NULL) { + error = true; + } if (argvars[1].v_type != VAR_UNKNOWN) { - buttons = get_tv_string_buf_chk(&argvars[1], buf); - if (buttons == NULL) - error = TRUE; + buttons = tv_get_string_buf_chk(&argvars[1], buf); + if (buttons == NULL) { + error = true; + } if (argvars[2].v_type != VAR_UNKNOWN) { def = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { - typestr = get_tv_string_buf_chk(&argvars[3], buf2); - if (typestr == NULL) - error = TRUE; - else { + typestr = tv_get_string_buf_chk(&argvars[3], buf2); + if (typestr == NULL) { + error = true; + } else { switch (TOUPPER_ASC(*typestr)) { - case 'E': type = VIM_ERROR; break; - case 'Q': type = VIM_QUESTION; break; - case 'I': type = VIM_INFO; break; - case 'W': type = VIM_WARNING; break; - case 'G': type = VIM_GENERIC; break; + case 'E': type = VIM_ERROR; break; + case 'Q': type = VIM_QUESTION; break; + case 'I': type = VIM_INFO; break; + case 'W': type = VIM_WARNING; break; + case 'G': type = VIM_GENERIC; break; } } } } } - if (buttons == NULL || *buttons == NUL) - buttons = (char_u *)_("&Ok"); + if (buttons == NULL || *buttons == NUL) { + buttons = _("&Ok"); + } - if (!error) - rettv->vval.v_number = do_dialog(type, NULL, message, buttons, - def, NULL, FALSE); + if (!error) { + rettv->vval.v_number = do_dialog( + type, NULL, (char_u *)message, (char_u *)buttons, def, NULL, false); + } } /* @@ -7708,32 +7714,29 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "key"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } + const size_t key_pattern_len = strlen(key_pattern); Callback callback; if (!callback_from_typval(&callback, &argvars[2])) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_len); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->callback = callback; watcher->busy = false; QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); @@ -7747,26 +7750,17 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[0].v_type != VAR_DICT) { - EMSG2(e_invarg2, "dict"); - return; - } - - if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { - EMSG2(e_invarg2, "key"); + emsgf(_(e_invarg2), "dict"); return; } if (argvars[2].v_type != VAR_FUNC && argvars[2].v_type != VAR_STRING) { - EMSG2(e_invarg2, "funcref"); + emsgf(_(e_invarg2), "funcref"); return; } - char *key_pattern = (char *)get_tv_string_chk(argvars + 1); - assert(key_pattern); - const size_t key_len = STRLEN(argvars[1].vval.v_string); - - if (key_len == 0) { - EMSG(_(e_emptykey)); + const char *const key_pattern = tv_get_string_chk(argvars + 1); + if (key_pattern == NULL) { return; } @@ -7924,16 +7918,15 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *s; - - s = get_tv_string_chk(&argvars[0]); - if (s != NULL) - s = skipwhite(s); + const char *s = tv_get_string_chk(&argvars[0]); + if (s != NULL) { + s = (const char *)skipwhite((const char_u *)s); + } - char_u *p = s; - if (s == NULL || eval1(&s, rettv, TRUE) == FAIL) { - if (p != NULL && !aborting()) { - EMSG2(_(e_invexpr2), p); + const char *const expr_start = s; + if (s == NULL || eval1((char_u **)&s, rettv, true) == FAIL) { + if (expr_start != NULL && !aborting()) { + EMSG2(_(e_invexpr2), expr_start); } need_clr_eos = FALSE; rettv->v_type = VAR_NUMBER; @@ -7965,75 +7958,74 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } -static char_u * get_list_line(int c, void *cookie, int indent) +static char_u *get_list_line(int c, void *cookie, int indent) { - listitem_T **p = (listitem_T **)cookie; - listitem_T *item = *p; - char_u buf[NUMBUFLEN]; - char_u *s; + const listitem_T **const p = (const listitem_T **)cookie; + const listitem_T *item = *p; if (item == NULL) { return NULL; } - s = get_tv_string_buf_chk(&item->li_tv, buf); + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); *p = item->li_next; - return s == NULL ? NULL : vim_strsave(s); + return (char_u *)(s == NULL ? NULL : xstrdup(s)); } // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int save_msg_silent = msg_silent; - int save_emsg_silent = emsg_silent; - bool save_emsg_noredir = emsg_noredir; - garray_T *save_capture_ga = capture_ga; + const int save_msg_silent = msg_silent; + const int save_emsg_silent = emsg_silent; + const bool save_emsg_noredir = emsg_noredir; + garray_T *const save_capture_ga = capture_ga; - if (check_secure()) { - return; - } + if (check_secure()) { + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + if (argvars[1].v_type != VAR_UNKNOWN) { + char buf[NUMBUFLEN]; + const char *const s = tv_get_string_buf_chk(&argvars[1], buf); - if (s == NULL) { - return; - } - if (STRNCMP(s, "silent", 6) == 0) { - msg_silent++; - } - if (STRCMP(s, "silent!") == 0) { - emsg_silent = true; - emsg_noredir = true; - } - } else { + if (s == NULL) { + return; + } + if (strncmp(s, S_LEN("silent")) == 0) { msg_silent++; } - - garray_T capture_local; - ga_init(&capture_local, (int)sizeof(char), 80); - capture_ga = &capture_local; - - if (argvars[0].v_type != VAR_LIST) { - do_cmdline_cmd(tv_get_string(&argvars[0])); - } else if (argvars[0].vval.v_list != NULL) { - list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, - DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + if (strcmp(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; } - msg_silent = save_msg_silent; - emsg_silent = save_emsg_silent; - emsg_noredir = save_emsg_noredir; + } else { + msg_silent++; + } - ga_append(capture_ga, NUL); - rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); + capture_ga = &capture_local; - capture_ga = save_capture_ga; + if (argvars[0].v_type != VAR_LIST) { + do_cmdline_cmd(tv_get_string(&argvars[0])); + } else if (argvars[0].vval.v_list != NULL) { + list_T *const list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *const item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; + } + msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; + + ga_append(capture_ga, NUL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); + + capture_ga = save_capture_ga; } /// "exepath()" function @@ -8231,7 +8223,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { const char *const av[] = { "keep", "force", "error" }; - action = (const char *)get_tv_string_chk(&argvars[2]); + action = tv_get_string_chk(&argvars[2]); if (action == NULL) { return; // Type error; error message already given. } @@ -8300,10 +8292,8 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) { - char_u *fresult = NULL; - char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - char_u *p; - char_u pathbuf[NUMBUFLEN]; + char_u *fresult = NULL; + char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; int count = 1; bool first = true; bool error = false; @@ -8313,13 +8303,15 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) const char *fname = tv_get_string(&argvars[0]); + char pathbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - p = get_tv_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) - error = TRUE; - else { - if (*p != NUL) - path = p; + const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf); + if (p == NULL) { + error = true; + } else { + if (*p != NUL) { + path = (char_u *)p; + } if (argvars[2].v_type != VAR_UNKNOWN) { count = tv_get_number_chk(&argvars[2], &error); @@ -8473,8 +8465,6 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) { typval_T rettv; typval_T argv[3]; - char_u buf[NUMBUFLEN]; - char_u *s; int retval = FAIL; int dummy; @@ -8482,7 +8472,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { - s = expr->vval.v_string; + const char_u *const s = expr->vval.v_string; if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { goto theend; @@ -8490,23 +8480,24 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) } else if (expr->v_type == VAR_PARTIAL) { partial_T *partial = expr->vval.v_partial; - s = partial_name(partial); + const char_u *const s = partial_name(partial); if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, partial, NULL) == FAIL) { goto theend; } } else { - s = get_tv_string_buf_chk(expr, buf); + char buf[NUMBUFLEN]; + const char *s = tv_get_string_buf_chk(expr, buf); if (s == NULL) { goto theend; } - s = skipwhite(s); - if (eval1(&s, &rettv, true) == FAIL) { + s = (const char *)skipwhite((const char_u *)s); + if (eval1((char_u **)&s, &rettv, true) == FAIL) { goto theend; } if (*s != NUL) { // check for trailing chars after expr - EMSG2(_(e_invexpr2), s); + emsgf(_(e_invexpr2), s); goto theend; } } @@ -8606,27 +8597,26 @@ static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *fname; - char_u *mods; - size_t usedlen = 0; + char_u *fbuf = NULL; size_t len; - char_u *fbuf = NULL; - char_u buf[NUMBUFLEN]; - - fname = get_tv_string_chk(&argvars[0]); - mods = get_tv_string_buf_chk(&argvars[1], buf); - if (fname == NULL || mods == NULL) + char buf[NUMBUFLEN]; + const char *fname = tv_get_string_chk(&argvars[0]); + const char *const mods = tv_get_string_buf_chk(&argvars[1], buf); + if (fname == NULL || mods == NULL) { fname = NULL; - else { - len = STRLEN(fname); - (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len); + } else { + len = strlen(fname); + size_t usedlen = 0; + (void)modify_fname((char_u *)mods, &usedlen, (char_u **)&fname, &fbuf, + &len); } rettv->v_type = VAR_STRING; - if (fname == NULL) + if (fname == NULL) { rettv->vval.v_string = NULL; - else - rettv->vval.v_string = vim_strnsave(fname, len); + } else { + rettv->vval.v_string = (char_u *)xmemdupz(fname, len); + } xfree(fbuf); } @@ -9257,7 +9247,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_getbufvar_end; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); emsg_off++; buf_T *const buf = get_buf_tv(&argvars[0], false); @@ -9970,14 +9960,13 @@ static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "getreg()" function static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; int arg2 = false; bool return_list = false; bool error = false; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); + strregname = tv_get_string_chk(&argvars[0]); error = strregname == NULL; if (argvars[1].v_type != VAR_UNKNOWN) { arg2 = tv_get_number_chk(&argvars[1], &error); @@ -9986,16 +9975,17 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } else { - strregname = vimvars[VV_REG].vv_str; + strregname = (const char *)vimvars[VV_REG].vv_str; } if (error) { return; } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } if (return_list) { rettv->v_type = VAR_LIST; @@ -10016,23 +10006,24 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *strregname; - int regname; + const char *strregname; if (argvars[0].v_type != VAR_UNKNOWN) { - strregname = get_tv_string_chk(&argvars[0]); - if (strregname == NULL) { /* type error; errmsg already given */ + strregname = tv_get_string_chk(&argvars[0]); + if (strregname == NULL) { // Type error; errmsg already given. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; return; } - } else - /* Default to v:register */ - strregname = vimvars[VV_REG].vv_str; + } else { + // Default to v:register. + strregname = (const char *)vimvars[VV_REG].vv_str; + } - regname = (strregname == NULL ? '"' : *strregname); - if (regname == 0) + int regname = (uint8_t)(strregname == NULL ? '"' : *strregname); + if (regname == 0) { regname = '"'; + } colnr_T reglen = 0; char buf[NUMBUFLEN + 2]; @@ -10108,7 +10099,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); if (tp != NULL && varname != NULL) { // Set tp to be our tabpage, temporarily. Also set the window to the @@ -10307,8 +10298,7 @@ getwinvar ( tp = curtab; } win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk( - &argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; @@ -10438,12 +10428,12 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u buf1[NUMBUFLEN]; - char_u *file = get_tv_string_buf_chk(&argvars[1], buf1); + char buf1[NUMBUFLEN]; + const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); if (file != NULL && !error) { garray_T ga; ga_init(&ga, (int)sizeof(char_u *), 10); - globpath((char_u *)tv_get_string(&argvars[0]), file, &ga, flags); + globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags); if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); @@ -10464,12 +10454,13 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) // "glob2regpat()" function static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) - ? NULL - : file_pat_to_reg_pat(pat, NULL, NULL, false); + rettv->vval.v_string = ((pat == NULL) + ? NULL + : file_pat_to_reg_pat((char_u *)pat, NULL, NULL, + false)); } /// "has()" function @@ -10797,14 +10788,14 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (check_restricted() || check_secure()) { return; } - char_u *str = get_tv_string_chk(&argvars[0]); // NULL on type error - histype = str != NULL ? get_histtype(str, STRLEN(str), false) : HIST_INVALID; + const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error + histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID; if (histype != HIST_INVALID) { char buf[NUMBUFLEN]; - str = (char_u *)tv_get_string_buf(&argvars[1], buf); + str = tv_get_string_buf(&argvars[1], buf); if (*str != NUL) { init_history(); - add_to_history(histype, str, false, NUL); + add_to_history(histype, (char_u *)str, false, NUL); rettv->vval.v_number = true; return; } @@ -10817,22 +10808,20 @@ static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int n; - char_u *str; - - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { n = 0; } else if (argvars[1].v_type == VAR_UNKNOWN) { // only one argument: clear entire history - n = clr_history(get_histtype(str, STRLEN(str), false)); + n = clr_history(get_histtype(str, strlen(str), false)); } else if (argvars[1].v_type == VAR_NUMBER) { // index given: remove that entry - n = del_history_idx(get_histtype(str, STRLEN(str), false), + n = del_history_idx(get_histtype(str, strlen(str), false), (int)tv_get_number(&argvars[1])); } else { // string given: remove all matching entries char buf[NUMBUFLEN]; - n = del_history_entry(get_histtype(str, STRLEN(str), false), + n = del_history_entry(get_histtype(str, strlen(str), false), (char_u *)tv_get_string_buf(&argvars[1], buf)); } rettv->vval.v_number = n; @@ -10845,13 +10834,12 @@ static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr) { HistoryType type; int idx; - char_u *str; - str = get_tv_string_chk(&argvars[0]); // NULL on type error + const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error if (str == NULL) { rettv->vval.v_string = NULL; } else { - type = get_histtype(str, STRLEN(str), false); + type = get_histtype(str, strlen(str), false); if (argvars[1].v_type == VAR_UNKNOWN) { idx = get_history_idx(type); } else { @@ -10870,9 +10858,9 @@ static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int i; - char_u *history = get_tv_string_chk(&argvars[0]); + const char *const history = tv_get_string_chk(&argvars[0]); - i = history == NULL ? HIST_CMD - 1 : get_histtype(history, STRLEN(history), + i = history == NULL ? HIST_CMD - 1 : get_histtype(history, strlen(history), false); if (i != HIST_INVALID) { i = get_history_idx(i); @@ -11009,11 +10997,8 @@ static int inputsecret_flag = 0; */ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) { - char_u *prompt = get_tv_string_chk(&argvars[0]); - char_u *p = NULL; - int c; + const char *prompt = tv_get_string_chk(&argvars[0]); int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; int xp_type = EXPAND_NOTHING; char_u *xp_arg = NULL; @@ -11022,49 +11007,45 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) cmd_silent = FALSE; /* Want to see the prompt. */ if (prompt != NULL) { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) + // Only the part of the message after the last NL is considered as + // prompt for the command line. + const char *p = strrchr(prompt, '\n'); + if (p == NULL) { p = prompt; - else { - ++p; - c = *p; - *p = NUL; + } else { + p++; msg_start(); msg_clr_eos(); - msg_puts_attr((const char *)prompt, echo_attr); + msg_puts_attr_len(prompt, p - prompt, echo_attr); msg_didout = false; msg_starthere(); - *p = c; } cmdline_row = msg_row; + const char *defstr = ""; if (argvars[1].v_type != VAR_UNKNOWN) { - char_u buf[NUMBUFLEN]; - defstr = get_tv_string_buf_chk(&argvars[1], buf); + char buf[NUMBUFLEN]; + defstr = tv_get_string_buf_chk(&argvars[1], buf); if (defstr != NULL) { stuffReadbuffSpec(defstr); } if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) { - char_u *xp_name; - int xp_namelen; - uint32_t argt; - - /* input() with a third argument: completion */ + // input() with a third argument: completion rettv->vval.v_string = NULL; - xp_name = get_tv_string_buf_chk(&argvars[2], buf); + const char *const xp_name = tv_get_string_buf_chk(&argvars[2], buf); if (xp_name == NULL) { return; } - xp_namelen = (int)STRLEN(xp_name); + const int xp_namelen = (int)strlen(xp_name); - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) + uint32_t argt; + if (parse_compl_arg((char_u *)xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) { return; + } } } @@ -11072,8 +11053,8 @@ static void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog) int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, - xp_type, xp_arg); + getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, + xp_type, xp_arg); ex_normal_busy = save_ex_normal_busy; } if (inputdialog && rettv->vval.v_string == NULL @@ -11544,8 +11525,8 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) assert(argl->lv_first); - const char_u *exe = get_tv_string_chk(&argl->lv_first->li_tv); - if (!exe || !os_can_exe(exe, NULL, true)) { + const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; } @@ -11553,14 +11534,14 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } if (cmd) { - *cmd = (char *)exe; + *cmd = exe; } // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - char *a = (char *)get_tv_string_chk(&arg->li_tv); + const char *a = tv_get_string_chk(&arg->li_tv); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); @@ -11803,50 +11784,49 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - garray_T ga; - char_u *sep; - if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) + if (argvars[0].vval.v_list == NULL) { return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = get_tv_string_chk(&argvars[1]); + } + const char *const sep = (argvars[1].v_type == VAR_UNKNOWN + ? " " + : tv_get_string_chk(&argvars[1])); rettv->v_type = VAR_STRING; if (sep != NULL) { + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); - tv_list_join(&ga, argvars[0].vval.v_list, (const char *)sep); + tv_list_join(&ga, argvars[0].vval.v_list, sep); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; - } else + } else { rettv->vval.v_string = NULL; + } } /// json_decode() function static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char numbuf[NUMBUFLEN]; - char *s = NULL; + const char *s = NULL; char *tofree = NULL; size_t len; if (argvars[0].v_type == VAR_LIST) { - if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &s)) { + if (!encode_vim_list_to_buf(argvars[0].vval.v_list, &len, &tofree)) { EMSG(_("E474: Failed to convert list to string")); return; } - tofree = s; + s = tofree; if (s == NULL) { assert(len == 0); s = ""; } } else { - s = (char *) get_tv_string_buf_chk(&argvars[0], (char_u *) numbuf); + s = tv_get_string_buf_chk(&argvars[0], numbuf); if (s) { len = strlen(s); } else { @@ -12043,10 +12023,8 @@ static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) { - char_u *which; - char_u buf[NUMBUFLEN]; - char_u *keys_buf = NULL; - char_u *rhs; + char_u *keys_buf = NULL; + char_u *rhs; int mode; int abbr = FALSE; int get_dict = FALSE; @@ -12062,20 +12040,24 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) return; } + char buf[NUMBUFLEN]; + const char *which; if (argvars[1].v_type != VAR_UNKNOWN) { - which = get_tv_string_buf_chk(&argvars[1], buf); + which = tv_get_string_buf_chk(&argvars[1], buf); if (argvars[2].v_type != VAR_UNKNOWN) { abbr = tv_get_number(&argvars[2]); if (argvars[3].v_type != VAR_UNKNOWN) { get_dict = tv_get_number(&argvars[3]); } } - } else - which = (char_u *)""; - if (which == NULL) + } else { + which = ""; + } + if (which == NULL) { return; + } - mode = get_map_mode(&which, 0); + mode = get_map_mode((char_u **)&which, 0); keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, CPO_TO_CPO_FLAGS); @@ -12141,9 +12123,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) char_u *str = NULL; long len = 0; char_u *expr = NULL; - char_u *pat; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; long start = 0; long nth = 1; @@ -12183,9 +12163,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) len = (long)STRLEN(str); } - pat = get_tv_string_buf_chk(&argvars[1], patbuf); - if (pat == NULL) + char patbuf[NUMBUFLEN]; + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { goto theend; + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -12224,7 +12206,7 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = p_ic; @@ -12336,18 +12318,20 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ - char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ - int prio = 10; /* default priority */ + char grpbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + const char *const grp = tv_get_string_buf_chk(&argvars[0], grpbuf); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + int prio = 10; int id = -1; bool error = false; const char *conceal_char = NULL; rettv->vval.v_number = -1; - if (grp == NULL || pat == NULL) + if (grp == NULL || pat == NULL) { return; + } if (argvars[2].v_type != VAR_UNKNOWN) { prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { @@ -12365,7 +12349,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } } - if (error == true) { + if (error) { return; } if (id >= 1 && id <= 3) { @@ -12373,17 +12357,16 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)grp, - (const char *)pat, prio, id, - NULL, conceal_char); + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, + conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - - char_u buf[NUMBUFLEN]; - const char_u *const group = get_tv_string_buf_chk(&argvars[0], buf); + + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); if (group == NULL) { return; } @@ -12431,8 +12414,8 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, (const char *)group, NULL, prio, id, - l, conceal_char); + rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, + conceal_char); } /* @@ -12833,11 +12816,11 @@ static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = get_tv_string_chk(&argvars[0]); - if (!rettv->vval.v_string) { + const char *const s = tv_get_string_chk(&argvars[0]); + if (!s) { return; } - rettv->vval.v_string = shorten_dir(vim_strsave(rettv->vval.v_string)); + rettv->vval.v_string = shorten_dir((char_u *)xstrdup(s)); } /* @@ -13240,7 +13223,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL && !tv_check_lock(d->dv_lock, arg_errmsg, arg_errmsg_len)) { - const char *key = (const char *)get_tv_string_chk(&argvars[1]); + const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); if (di == NULL) { @@ -13572,40 +13555,45 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int get_search_arg(typval_T *varp, int *flagsp) { int dir = FORWARD; - char_u *flags; - char_u nbuf[NUMBUFLEN]; int mask; if (varp->v_type != VAR_UNKNOWN) { - flags = get_tv_string_buf_chk(varp, nbuf); - if (flags == NULL) - return 0; /* type error; errmsg already given */ + char nbuf[NUMBUFLEN]; + const char *flags = tv_get_string_buf_chk(varp, nbuf); + if (flags == NULL) { + return 0; // Type error; errmsg already given. + } while (*flags != NUL) { switch (*flags) { - case 'b': dir = BACKWARD; break; - case 'w': p_ws = true; break; - case 'W': p_ws = false; break; - default: mask = 0; - if (flagsp != NULL) - switch (*flags) { - case 'c': mask = SP_START; break; - case 'e': mask = SP_END; break; - case 'm': mask = SP_RETCOUNT; break; - case 'n': mask = SP_NOMOVE; break; - case 'p': mask = SP_SUBPAT; break; - case 'r': mask = SP_REPEAT; break; - case 's': mask = SP_SETPCMARK; break; - case 'z': mask = SP_COLUMN; break; + case 'b': dir = BACKWARD; break; + case 'w': p_ws = true; break; + case 'W': p_ws = false; break; + default: { + mask = 0; + if (flagsp != NULL) { + switch (*flags) { + case 'c': mask = SP_START; break; + case 'e': mask = SP_END; break; + case 'm': mask = SP_RETCOUNT; break; + case 'n': mask = SP_NOMOVE; break; + case 'p': mask = SP_SUBPAT; break; + case 'r': mask = SP_REPEAT; break; + case 's': mask = SP_SETPCMARK; break; + case 'z': mask = SP_COLUMN; break; + } } - if (mask == 0) { - EMSG2(_(e_invarg2), flags); - dir = 0; - } else - *flagsp |= mask; + if (mask == 0) { + emsgf(_(e_invarg2), flags); + dir = 0; + } else { + *flagsp |= mask; + } + } } - if (dir == 0) + if (dir == 0) { break; - ++flags; + } + flags++; } } return dir; @@ -13989,16 +13977,17 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* default: FAIL */ - char_u *name = get_tv_string_chk(&argvars[0]); + const char *const name = tv_get_string_chk(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { locally = tv_get_number_chk(&argvars[1], &error) == 0; if (!error && argvars[2].v_type != VAR_UNKNOWN) { thisblock = tv_get_number_chk(&argvars[2], &error) != 0; } } - if (!error && name != NULL) - rettv->vval.v_number = find_decl(name, STRLEN(name), locally, + if (!error && name != NULL) { + rettv->vval.v_number = find_decl((char_u *)name, strlen(name), locally, thisblock, SEARCH_KEEP) == FAIL; + } } /* @@ -14006,49 +13995,53 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) { - char_u *spat, *mpat, *epat; - char_u *skip; bool save_p_ws = p_ws; int dir; int flags = 0; - char_u nbuf1[NUMBUFLEN]; - char_u nbuf2[NUMBUFLEN]; - char_u nbuf3[NUMBUFLEN]; - int retval = 0; /* default: FAIL */ + int retval = 0; // default: FAIL long lnum_stop = 0; long time_limit = 0; - /* Get the three pattern arguments: start, middle, end. */ - spat = get_tv_string_chk(&argvars[0]); - mpat = get_tv_string_buf_chk(&argvars[1], nbuf1); - epat = get_tv_string_buf_chk(&argvars[2], nbuf2); - if (spat == NULL || mpat == NULL || epat == NULL) - goto theend; /* type error */ + // Get the three pattern arguments: start, middle, end. + char nbuf1[NUMBUFLEN]; + char nbuf2[NUMBUFLEN]; + char nbuf3[NUMBUFLEN]; + const char *spat = tv_get_string_chk(&argvars[0]); + const char *mpat = tv_get_string_buf_chk(&argvars[1], nbuf1); + const char *epat = tv_get_string_buf_chk(&argvars[2], nbuf2); + if (spat == NULL || mpat == NULL || epat == NULL) { + goto theend; // Type error. + } - /* Handle the optional fourth argument: flags */ - dir = get_search_arg(&argvars[3], &flags); /* may set p_ws */ - if (dir == 0) + // Handle the optional fourth argument: flags. + dir = get_search_arg(&argvars[3], &flags); // may set p_ws. + if (dir == 0) { goto theend; + } - /* Don't accept SP_END or SP_SUBPAT. - * Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. - */ + // Don't accept SP_END or SP_SUBPAT. + // Only one of the SP_NOMOVE or SP_SETPCMARK flags can be set. if ((flags & (SP_END | SP_SUBPAT)) != 0 || ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) { EMSG2(_(e_invarg2), tv_get_string(&argvars[3])); goto theend; } - /* Using 'r' implies 'W', otherwise it doesn't work. */ - if (flags & SP_REPEAT) + // Using 'r' implies 'W', otherwise it doesn't work. + if (flags & SP_REPEAT) { p_ws = false; + } - /* Optional fifth argument: skip expression */ + // Optional fifth argument: skip expression. + const char *skip; if (argvars[3].v_type == VAR_UNKNOWN - || argvars[4].v_type == VAR_UNKNOWN) - skip = (char_u *)""; - else { - skip = get_tv_string_buf_chk(&argvars[4], nbuf3); + || argvars[4].v_type == VAR_UNKNOWN) { + skip = ""; + } else { + skip = tv_get_string_buf_chk(&argvars[4], nbuf3); + if (skip == NULL) { + goto theend; // Type error. + } if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[5], NULL); if (lnum_stop < 0) { @@ -14062,11 +14055,10 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } } } - if (skip == NULL) - goto theend; /* type error */ - retval = do_searchpair(spat, mpat, epat, dir, skip, flags, - match_pos, lnum_stop, time_limit); + retval = do_searchpair( + (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip, + flags, match_pos, lnum_stop, time_limit); theend: p_ws = save_p_ws; @@ -14341,14 +14333,12 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u nbuf[NUMBUFLEN]; - if (check_restricted() || check_secure() || !tv_check_str_or_nr(&argvars[0])) { return; } - const char *varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *varname = tv_get_string_chk(&argvars[1]); buf_T *const buf = get_buf_tv(&argvars[0], false); typval_T *varp = &argvars[2]; @@ -14363,7 +14353,8 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) varname++; numval = tv_get_number_chk(varp, &error); - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -14438,17 +14429,17 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = 0; - char_u *fname = get_tv_string_chk(&argvars[0]); + const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { return; } - char_u modebuf[NUMBUFLEN]; - char_u *mode_str = get_tv_string_buf_chk(&argvars[1], modebuf); + char modebuf[NUMBUFLEN]; + const char *const mode_str = tv_get_string_buf_chk(&argvars[1], modebuf); if (mode_str == NULL) { return; } - if (STRLEN(mode_str) != 9) { + if (strlen(mode_str) != 9) { EMSG2(_(e_invarg2), mode_str); return; } @@ -14469,26 +14460,28 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *line = NULL; list_T *l = NULL; listitem_T *li = NULL; long added = 0; linenr_T lcount = curbuf->b_ml.ml_line_count; linenr_T lnum = tv_get_lnum(&argvars[0]); + const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; li = l->lv_first; - } else - line = get_tv_string_chk(&argvars[1]); + } else { + line = tv_get_string_chk(&argvars[1]); + } /* default result is zero == OK */ for (;; ) { if (l != NULL) { - /* list argument, get next string */ - if (li == NULL) + // List argument, get next string. + if (li == NULL) { break; - line = get_tv_string_chk(&li->li_tv); + } + line = tv_get_string_chk(&li->li_tv); li = li->li_next; } @@ -14504,18 +14497,20 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (lnum <= curbuf->b_ml.ml_line_count) { - /* existing line, replace it */ - if (u_savesub(lnum) == OK && ml_replace(lnum, line, TRUE) == OK) { + // Existing line, replace it. + if (u_savesub(lnum) == OK + && ml_replace(lnum, (char_u *)line, true) == OK) { changed_bytes(lnum, 0); if (lnum == curwin->w_cursor.lnum) check_cursor_col(); rettv->vval.v_number = 0; /* OK */ } } else if (added > 0 || u_save(lnum - 1, lnum) == OK) { - /* lnum is one past the last line, append the line */ - ++added; - if (ml_append(lnum - 1, line, (colnr_T)0, FALSE) == OK) - rettv->vval.v_number = 0; /* OK */ + // lnum is one past the last line, append the line. + added++; + if (ml_append(lnum - 1, (char_u *)line, 0, false) == OK) { + rettv->vval.v_number = 0; // OK + } } if (l == NULL) /* only one string argument */ @@ -14544,7 +14539,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(2, 3) { static char *e_invact = N_("E927: Invalid action: '%s'"); - char_u *title = NULL; + const char *title = NULL; int action = ' '; rettv->vval.v_number = -1; dict_T *d = NULL; @@ -14563,7 +14558,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) EMSG(_(e_stringreq)); return; } - char_u *act = get_tv_string_chk(action_arg); + const char *const act = tv_get_string_chk(action_arg); if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { action = *act; } else { @@ -14576,7 +14571,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) // Option argument was not given. goto skip_args; } else if (title_arg->v_type == VAR_STRING) { - title = get_tv_string_chk(title_arg); + title = tv_get_string_chk(title_arg); if (!title) { // Type error. Error already printed by get_tv_string_chk(). return; @@ -14584,17 +14579,17 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_DICT) { d = title_arg->vval.v_dict; } else { - EMSG(_(e_dictreq)); + emsgf(_(e_dictreq)); return; } skip_args: if (!title) { - title = (char_u*)(wp ? "setloclist()" : "setqflist()"); + title = (wp ? "setloclist()" : "setqflist()"); } list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, title, d) == OK) { + if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14728,11 +14723,10 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - char_u *name; colnr_T curswant = -1; rettv->vval.v_number = -1; - name = get_tv_string_chk(argvars); + const char *const name = tv_get_string_chk(argvars); if (name != NULL) { if (list2fpos(&argvars[1], &pos, &fnum, &curswant) == OK) { if (--pos.col < 0) { @@ -14753,7 +14747,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark - if (setmark_pos(name[1], &pos, fnum) == OK) { + if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { rettv->vval.v_number = 0; } } else { @@ -14777,8 +14771,6 @@ static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int regname; - char_u *strregname; - char_u *stropt; bool append = false; MotionType yank_type; long block_len; @@ -14786,39 +14778,47 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) block_len = -1; yank_type = kMTUnknown; - strregname = get_tv_string_chk(argvars); - rettv->vval.v_number = 1; /* FAIL is default */ + rettv->vval.v_number = 1; // FAIL is default. - if (strregname == NULL) - return; /* type error; errmsg already given */ - regname = *strregname; - if (regname == 0 || regname == '@') + const char *const strregname = tv_get_string_chk(argvars); + if (strregname == NULL) { + return; // Type error; errmsg already given. + } + regname = (uint8_t)(*strregname); + if (regname == 0 || regname == '@') { regname = '"'; + } if (argvars[2].v_type != VAR_UNKNOWN) { - stropt = get_tv_string_chk(&argvars[2]); - if (stropt == NULL) - return; /* type error */ - for (; *stropt != NUL; ++stropt) + const char *stropt = tv_get_string_chk(&argvars[2]); + if (stropt == NULL) { + return; // Type error. + } + for (; *stropt != NUL; stropt++) { switch (*stropt) { - case 'a': case 'A': // append - append = true; - break; - case 'v': case 'c': // character-wise selection - yank_type = kMTCharWise; - break; - case 'V': case 'l': // line-wise selection - yank_type = kMTLineWise; - break; - case 'b': case Ctrl_V: // block-wise selection - yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - ++stropt; - block_len = getdigits_long(&stropt) - 1; - --stropt; + case 'a': case 'A': { // append + append = true; + break; + } + case 'v': case 'c': { // character-wise selection + yank_type = kMTCharWise; + break; + } + case 'V': case 'l': { // line-wise selection + yank_type = kMTLineWise; + break; + } + case 'b': case Ctrl_V: { // block-wise selection + yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + block_len = getdigits_long((char_u **)&stropt) - 1; + stropt--; + } + break; } - break; } + } } if (argvars[1].v_type == VAR_LIST) { @@ -14828,42 +14828,44 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. - char_u **lstval = xmalloc(sizeof(char_u *) * ((len + 1) * 2)); - char_u **curval = lstval; - char_u **allocval = lstval + len + 2; - char_u **curallocval = allocval; + char **lstval = xmalloc(sizeof(char *) * ((len + 1) * 2)); + const char **curval = (const char **)lstval; + char **allocval = lstval + len + 2; + char **curallocval = allocval; - char_u buf[NUMBUFLEN]; for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; li != NULL; li = li->li_next) { - char_u *strval = get_tv_string_buf_chk(&li->li_tv, buf); - if (strval == NULL) { + char buf[NUMBUFLEN]; + *curval = tv_get_string_buf_chk(&li->li_tv, buf); + if (*curval == NULL) { goto free_lstval; } - if (strval == buf) { + if (*curval == buf) { // Need to make a copy, - // next get_tv_string_buf_chk() will overwrite the string. - strval = vim_strsave(buf); - *curallocval++ = strval; + // next tv_get_string_buf_chk() will overwrite the string. + *curallocval = xstrdup(*curval); + *curval = *curallocval; + curallocval++; } - *curval++ = strval; + curval++; } *curval++ = NULL; - write_reg_contents_lst(regname, lstval, STRLEN(lstval), - append, yank_type, block_len); + write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, + block_len); free_lstval: - while (curallocval > allocval) - xfree(*--curallocval); + while (curallocval > allocval) { + xfree(*--curallocval); + } xfree(lstval); } else { - char_u *strval = get_tv_string_chk(&argvars[1]); + const char *strval = tv_get_string_chk(&argvars[1]); if (strval == NULL) { return; } - write_reg_contents_ex(regname, strval, STRLEN(strval), + write_reg_contents_ex(regname, (const char_u *)strval, STRLEN(strval), append, yank_type, block_len); } rettv->vval.v_number = 0; @@ -14881,7 +14883,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL)); - const char *const varname = (const char *)get_tv_string_chk(&argvars[1]); + const char *const varname = tv_get_string_chk(&argvars[1]); typval_T *const varp = &argvars[2]; if (varname != NULL && varp != NULL && tp != NULL) { @@ -14935,7 +14937,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) tp = curtab; } win_T *const win = find_win_by_nr(&argvars[off], tp); - const char *varname = (const char *)get_tv_string_chk(&argvars[off + 1]); + const char *varname = tv_get_string_chk(&argvars[off + 1]); typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { @@ -14950,8 +14952,8 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) varname++; numval = tv_get_number_chk(varp, &error); - char_u nbuf[NUMBUFLEN]; - char *const strval = (char *)get_tv_string_buf_chk(varp, nbuf); + char nbuf[NUMBUFLEN]; + const char *const strval = tv_get_string_buf_chk(varp, nbuf); if (!error && strval != NULL) { set_option_value(varname, numval, strval, OPT_LOCAL); } @@ -15415,25 +15417,26 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *word = (char_u *)""; + const char *word = ""; hlf_T attr = HLF_COUNT; size_t len = 0; tv_list_alloc_ret(rettv); if (argvars[0].v_type == VAR_UNKNOWN) { - /* Find the start and length of the badly spelled word. */ - len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr); - if (len != 0) - word = get_cursor_pos_ptr(); + // Find the start and length of the badly spelled word. + len = spell_move_to(curwin, FORWARD, true, true, &attr); + if (len != 0) { + word = (char *)get_cursor_pos_ptr(); + } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { - char_u *str = get_tv_string_chk(&argvars[0]); + const char *str = tv_get_string_chk(&argvars[0]); int capcol = -1; if (str != NULL) { - /* Check the argument for spelling. */ + // Check the argument for spelling. while (*str != NUL) { - len = spell_check(curwin, str, &attr, &capcol, false); + len = spell_check(curwin, (char_u *)str, &attr, &capcol, false); if (attr != HLF_COUNT) { word = str; break; @@ -15444,7 +15447,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); - tv_list_append_string(rettv->vval.v_list, (const char *)word, len); + tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" : attr == HLF_SPR ? "rare" @@ -15499,9 +15502,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *pat = NULL; regmatch_T regmatch; - char_u patbuf[NUMBUFLEN]; char_u *save_cpo; int match; colnr_T col = 0; @@ -15513,8 +15514,10 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) p_cpo = (char_u *)""; const char *str = tv_get_string(&argvars[0]); + const char *pat = NULL; + char patbuf[NUMBUFLEN]; if (argvars[1].v_type != VAR_UNKNOWN) { - pat = get_tv_string_buf_chk(&argvars[1], patbuf); + pat = tv_get_string_buf_chk(&argvars[1], patbuf); if (pat == NULL) { typeerr = true; } @@ -15522,15 +15525,16 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) keepempty = (bool)tv_get_number_chk(&argvars[2], &typeerr); } } - if (pat == NULL || *pat == NUL) - pat = (char_u *)"[\\x01- ]\\+"; + if (pat == NULL || *pat == NUL) { + pat = "[\\x01- ]\\+"; + } tv_list_alloc_ret(rettv); if (typeerr) return; - regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { regmatch.rm_ic = FALSE; while (*str != NUL || keepempty) { @@ -15682,7 +15686,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->vval.v_number = -1; - char_u *str = get_tv_string_chk(&argvars[0]); + const char *const str = tv_get_string_chk(&argvars[0]); if (str == NULL) { return; } @@ -15697,11 +15701,11 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) while (charidx >= 0 && byteidx < len) { if (charidx == 0) { - rettv->vval.v_number = mb_ptr2char(str + byteidx); + rettv->vval.v_number = mb_ptr2char((const char_u *)str + byteidx); break; } charidx--; - byteidx += MB_CPTR2LEN(str + byteidx); + byteidx += MB_CPTR2LEN((const char_u *)str + byteidx); } } @@ -15710,24 +15714,22 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *save_haystack; - char_u *pos; - int start_idx; - - needle = get_tv_string_chk(&argvars[1]); - save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *haystack = tv_get_string_buf_chk(&argvars[0], buf); + const char *const haystack_start = haystack; + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - start_idx = tv_get_number_chk(&argvars[2], &error); - if (error || start_idx >= (int)STRLEN(haystack)) { + const ptrdiff_t start_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], + &error); + if (error || start_idx >= (ptrdiff_t)strlen(haystack)) { return; } if (start_idx >= 0) { @@ -15735,9 +15737,10 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - pos = (char_u *)strstr((char *)haystack, (char *)needle); - if (pos != NULL) - rettv->vval.v_number = (varnumber_T)(pos - save_haystack); + const char *pos = strstr(haystack, needle); + if (pos != NULL) { + rettv->vval.v_number = (varnumber_T)(pos - haystack_start); + } } /* @@ -15765,7 +15768,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *s = tv_get_string(&argvars[0]); int skipcc = 0; varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); + int (*func_mb_ptr2char_adv)(const char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) { skipcc = tv_get_number_chk(&argvars[1], NULL); @@ -15775,7 +15778,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; while (*s != NUL) { - func_mb_ptr2char_adv((char_u **)&s); + func_mb_ptr2char_adv((const char_u **)&s); len++; } rettv->vval.v_number = len; @@ -15904,47 +15907,46 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u buf[NUMBUFLEN]; - char_u *needle; - char_u *haystack; - char_u *rest; - char_u *lastmatch = NULL; - int haystack_len, end_idx; - - needle = get_tv_string_chk(&argvars[1]); - haystack = get_tv_string_buf_chk(&argvars[0], buf); + char buf[NUMBUFLEN]; + const char *const needle = tv_get_string_chk(&argvars[1]); + const char *const haystack = tv_get_string_buf_chk(&argvars[0], buf); rettv->vval.v_number = -1; - if (needle == NULL || haystack == NULL) - return; /* type error; errmsg already given */ + if (needle == NULL || haystack == NULL) { + return; // Type error; errmsg already given. + } - haystack_len = (int)STRLEN(haystack); + const size_t haystack_len = STRLEN(haystack); + ptrdiff_t end_idx; if (argvars[2].v_type != VAR_UNKNOWN) { // Third argument: upper limit for index. - end_idx = tv_get_number_chk(&argvars[2], NULL); + end_idx = (ptrdiff_t)tv_get_number_chk(&argvars[2], NULL); if (end_idx < 0) { return; // Can never find a match. } } else { - end_idx = haystack_len; + end_idx = (ptrdiff_t)haystack_len; } + const char *lastmatch = NULL; if (*needle == NUL) { - /* Empty string matches past the end. */ + // Empty string matches past the end. lastmatch = haystack + end_idx; } else { - for (rest = haystack; *rest != NUL; ++rest) { - rest = (char_u *)strstr((char *)rest, (char *)needle); - if (rest == NULL || rest > haystack + end_idx) + for (const char *rest = haystack; *rest != NUL; rest++) { + rest = strstr(rest, needle); + if (rest == NULL || rest > haystack + end_idx) { break; + } lastmatch = rest; } } - if (lastmatch == NULL) + if (lastmatch == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = (varnumber_T)(lastmatch - haystack); + } } /* @@ -15994,20 +15996,20 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u patbuf[NUMBUFLEN]; - char_u subbuf[NUMBUFLEN]; - char_u flagsbuf[NUMBUFLEN]; + char patbuf[NUMBUFLEN]; + char subbuf[NUMBUFLEN]; + char flagsbuf[NUMBUFLEN]; - char_u *str = get_tv_string_chk(&argvars[0]); - char_u *pat = get_tv_string_buf_chk(&argvars[1], patbuf); - char_u *sub = NULL; - typval_T *expr = NULL; - char_u *flg = get_tv_string_buf_chk(&argvars[3], flagsbuf); + const char *const str = tv_get_string_chk(&argvars[0]); + const char *const pat = tv_get_string_buf_chk(&argvars[1], patbuf); + const char *sub = NULL; + const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); + typval_T *expr = NULL; if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { expr = &argvars[2]; } else { - sub = get_tv_string_buf_chk(&argvars[2], subbuf); + sub = tv_get_string_buf_chk(&argvars[2], subbuf); } rettv->v_type = VAR_STRING; @@ -16015,7 +16017,8 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) || flg == NULL) { rettv->vval.v_string = NULL; } else { - rettv->vval.v_string = do_string_sub(str, pat, sub, expr, flg); + rettv->vval.v_string = do_string_sub((char_u *)str, (char_u *)pat, + (char_u *)sub, expr, (char_u *)flg); } } @@ -16333,19 +16336,20 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int nr = 1; - char_u *arg; if (argvars[0].v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(&argvars[0]); + const char *const arg = tv_get_string_chk(&argvars[0]); nr = 0; if (arg != NULL) { - if (STRCMP(arg, "$") == 0) + if (strcmp(arg, "$") == 0) { nr = tabpage_index(NULL) - 1; - else + } else { EMSG2(_(e_invexpr2), arg); + } } - } else + } else { nr = tabpage_index(curtab); + } rettv->vval.v_number = nr; } @@ -16359,19 +16363,19 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) win_T *twin; int nr = 1; win_T *wp; - char_u *arg; twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { - arg = get_tv_string_chk(argvar); - if (arg == NULL) - nr = 0; /* type error; errmsg already given */ - else if (STRCMP(arg, "$") == 0) + const char *const arg = tv_get_string_chk(argvar); + if (arg == NULL) { + nr = 0; // Type error; errmsg already given. + } else if (strcmp(arg, "$") == 0) { twin = (tp == curtab) ? lastwin : tp->tp_lastwin; - else if (STRCMP(arg, "#") == 0) { + } else if (strcmp(arg, "#") == 0) { twin = (tp == curtab) ? prevwin : tp->tp_prevwin; - if (twin == NULL) + if (twin == NULL) { nr = 0; + } } else { EMSG2(_(e_invexpr2), arg); nr = 0; @@ -16853,28 +16857,25 @@ static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int fromlen; - int tolen; - int idx; - char_u buf[NUMBUFLEN]; - char_u buf2[NUMBUFLEN]; - garray_T ga; + char buf[NUMBUFLEN]; + char buf2[NUMBUFLEN]; const char *in_str = tv_get_string(&argvars[0]); - const char_u *fromstr = get_tv_string_buf_chk(&argvars[1], buf); - const char_u *tostr = get_tv_string_buf_chk(&argvars[2], buf2); + const char *fromstr = tv_get_string_buf_chk(&argvars[1], buf); + const char *tostr = tv_get_string_buf_chk(&argvars[2], buf2); // Default return value: empty string. rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; if (fromstr == NULL || tostr == NULL) { - return; // type error; errmsg already given + return; // Type error; errmsg already given. } + garray_T ga; ga_init(&ga, (int)sizeof(char), 80); if (!has_mbyte) { // Not multi-byte: fromstr and tostr must be the same length. - if (STRLEN(fromstr) != STRLEN(tostr)) { + if (strlen(fromstr) != strlen(tostr)) { goto error; } } @@ -16886,23 +16887,26 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *cpstr = in_str; const int inlen = (*mb_ptr2len)((const char_u *)in_str); int cplen = inlen; - idx = 0; - for (const char_u *p = fromstr; *p != NUL; p += fromlen) { - fromlen = (*mb_ptr2len)(p); + int idx = 0; + int fromlen; + for (const char *p = fromstr; *p != NUL; p += fromlen) { + fromlen = (*mb_ptr2len)((const char_u *)p); if (fromlen == inlen && STRNCMP(in_str, p, inlen) == 0) { + int tolen; for (p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + tolen = (*mb_ptr2len)((const char_u *)p); if (idx-- == 0) { cplen = tolen; cpstr = (char *)p; break; } } - if (*p == NUL) /* tostr is shorter than fromstr */ + if (*p == NUL) { // tostr is shorter than fromstr. goto error; + } break; } - ++idx; + idx++; } if (first && cpstr == in_str) { @@ -16910,8 +16914,9 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) // (multi-byte) characters. Done only once when a character // of in_str doesn't appear in fromstr. first = false; - for (const char_u *p = tostr; *p != NUL; p += tolen) { - tolen = (*mb_ptr2len)(p); + int tolen; + for (const char *p = tostr; *p != NUL; p += tolen) { + tolen = (*mb_ptr2len)((const char_u *)p); idx--; } if (idx != 0) { @@ -16926,7 +16931,7 @@ static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr) in_str += inlen; } else { // When not using multi-byte chars we can do it faster. - char_u *p = vim_strchr(fromstr, *in_str); + const char *const p = strchr(fromstr, *in_str); if (p != NULL) { ga_append(&ga, tostr[p - fromstr]); } else { @@ -17278,8 +17283,7 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, { int error = 0; for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = (const char *)get_tv_string_chk( - (typval_T *)&li->li_tv); + const char *const s = tv_get_string_chk(&li->li_tv); if (s == NULL) { return false; } @@ -17368,17 +17372,16 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return NULL; } - // For types other than list, let get_tv_string_buf_chk() get the value or + // For types other than list, let tv_get_string_buf_chk() get the value or // print an error. if (tv->v_type != VAR_LIST) { - char *ret = (char *)get_tv_string_chk(tv); + const char *ret = tv_get_string_chk(tv); if (ret && (*len = strlen(ret))) { - ret = xstrdup(ret); + return xmemdupz(ret, (size_t)(*len)); } else { - ret = NULL; *len = -1; + return NULL; } - return ret; } // Pre-calculate the resulting length. @@ -17448,7 +17451,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool binary = false; bool append = false; if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const flags = (const char *)get_tv_string_chk(&argvars[2]); + const char *const flags = tv_get_string_chk(&argvars[2]); if (flags == NULL) { return; } @@ -17460,9 +17463,8 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - const char buf[NUMBUFLEN]; - const char *const fname = (const char *)get_tv_string_buf_chk(&argvars[1], - (char_u *)buf); + char buf[NUMBUFLEN]; + const char *const fname = tv_get_string_buf_chk(&argvars[1], buf); if (fname == NULL) { return; } @@ -17510,7 +17512,6 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, int *const ret_fnum) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - char_u *name; static pos_T pos; pos_T *pp; @@ -17564,7 +17565,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &pos; } - name = get_tv_string_chk(tv); + const char *const name = tv_get_string_chk(tv); if (name == NULL) { return NULL; } @@ -17578,7 +17579,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, return &curwin->w_cursor; } if (name[0] == '\'') { // Mark. - pp = getmark_buf_fnum(curbuf, name[1], false, ret_fnum); + pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } @@ -18403,58 +18404,11 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } } -// TODO(ZyX-I): move to eval/typval - -/// Careful: This uses a single, static buffer. YOU CAN ONLY USE IT ONCE! -char_u *get_tv_string_chk(const typval_T *varp) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - static char_u mybuf[NUMBUFLEN]; - - return get_tv_string_buf_chk(varp, mybuf); -} - -char_u *get_tv_string_buf_chk(const typval_T *varp, char_u *buf) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - switch (varp->v_type) { - case VAR_NUMBER: - sprintf((char *)buf, "%" PRId64, (int64_t)varp->vval.v_number); - return buf; - case VAR_FUNC: - case VAR_PARTIAL: - EMSG(_("E729: using Funcref as a String")); - break; - case VAR_LIST: - EMSG(_("E730: using List as a String")); - break; - case VAR_DICT: - EMSG(_("E731: using Dictionary as a String")); - break; - case VAR_FLOAT: - EMSG(_(e_float_as_string)); - break; - case VAR_STRING: - if (varp->vval.v_string != NULL) - return varp->vval.v_string; - return (char_u *)""; - case VAR_SPECIAL: - STRCPY(buf, encode_special_var_names[varp->vval.v_special]); - return buf; - case VAR_UNKNOWN: - EMSG(_("E908: using an invalid value as a String")); - break; - } - return NULL; -} - -/* - * Find variable "name" in the list of variables. - * Return a pointer to it if found, NULL if not found. - * Careful: "a:0" variables don't have a name. - * When "htp" is not NULL we are writing to the variable, set "htp" to the - * hashtab_T used. - */ +// Find variable "name" in the list of variables. +// Return a pointer to it if found, NULL if not found. +// Careful: "a:0" variables don't have a name. +// When "htp" is not NULL we are writing to the variable, set "htp" to the +// hashtab_T used. static dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ca635dcae9..78eca15fec 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -796,7 +796,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // of the string means it should match everything up to the '*' instead of the // whole string. const size_t len = strlen(watcher->key_pattern); - if (watcher->key_pattern[len - 1] == '*') { + if (len && watcher->key_pattern[len - 1] == '*') { return strncmp(key, watcher->key_pattern, len - 1) == 0; } else { return strcmp(key, watcher->key_pattern) == 0; @@ -2020,7 +2020,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, /// /// @return true if everything is OK, false otherwise. bool tv_check_str_or_nr(const typval_T *const tv) - FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { switch (tv->v_type) { case VAR_NUMBER: @@ -2072,7 +2072,7 @@ static const char *const num_errors[] = { /// Check that given value is a number or can be converted to it /// -/// Error messages are compatible with tv_get_number() previously used for +/// Error messages are compatible with tv_get_number_chk() previously used for /// the same purpose. /// /// @param[in] tv Value to check. @@ -2101,6 +2101,50 @@ bool tv_check_num(const typval_T *const tv) return false; } +#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_UNKNOWN]=N_("E908: using an invalid value as a String"), +}; + +#undef FUNC_ERROR + +/// Check that given value is a string or can be converted to it +/// +/// Error messages are compatible with tv_get_string_chk() previously used for +/// the same purpose. +/// +/// @param[in] tv Value to check. +/// +/// @return true if everything is OK, false otherwise. +bool tv_check_str(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: + case VAR_SPECIAL: + case VAR_STRING: { + return true; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + assert(false); + return false; +} + //{{{2 Get /// Get the number value of a VimL object @@ -2245,6 +2289,67 @@ float_T tv_get_float(const typval_T *const tv) return 0; } +/// Get the string value of a VimL object +/// +/// @param[in] tv Object to get value of. +/// @param buf Buffer used to hold numbers and special variables converted to +/// string. When function encounters one of these stringified value +/// will be written to buf and buf will be returned. +/// +/// Buffer must have NUMBUFLEN size. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { + case VAR_NUMBER: { + snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); + return buf; + } + case VAR_STRING: { + if (tv->vval.v_string != NULL) { + return (const char *)tv->vval.v_string; + } + return ""; + } + case VAR_SPECIAL: { + STRCPY(buf, encode_special_var_names[tv->vval.v_special]); + return buf; + } + case VAR_PARTIAL: + case VAR_FUNC: + case VAR_LIST: + case VAR_DICT: + case VAR_FLOAT: + case VAR_UNKNOWN: { + EMSG(_(str_errors[tv->v_type])); + return false; + } + } + return NULL; +} + +/// Get the string value of a VimL object +/// +/// @warning For number and special values it uses a single, static buffer. It +/// may be used only once, next call to get_tv_string may reuse it. Use +/// tv_get_string_buf() if you need to use tv_get_string() output after +/// calling it again. +/// +/// @param[in] tv Object to get value of. +/// +/// @return Object value if it is VAR_STRING object, number converted to +/// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. +const char *tv_get_string_chk(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char mybuf[NUMBUFLEN]; + + return tv_get_string_buf_chk(tv, mybuf); +} + /// Get the string value of a VimL object /// /// @warning For number and special values it uses a single, static buffer. It @@ -2252,7 +2357,7 @@ float_T tv_get_float(const typval_T *const tv) /// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2269,7 +2374,7 @@ const char *tv_get_string(const typval_T *const tv) /// Get the string value of a VimL object /// -/// @note get_tv_string_chk() and get_tv_string_buf_chk() are similar, but +/// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. /// /// @param[in] tv Object to get value of. @@ -2285,8 +2390,7 @@ const char *tv_get_string(const typval_T *const tv) const char *tv_get_string_buf(const typval_T *const tv, char *const buf) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { - const char *const res = (const char *)get_tv_string_buf_chk( - (typval_T *)tv, (char_u *)buf); + const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); return res != NULL ? res : ""; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3c294b45b8..42581939e3 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -27,6 +27,7 @@ typedef double float_T; /// Mimimal possible value of varnumber_T variable #define VARNUMBER_MIN INT_MIN +#define PRIdVARNUMBER "d" /// %d printf format specifier for varnumber_T #define PRIdVARNUMBER "d" diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 9681527fee..9a847a4c0a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1008,8 +1008,8 @@ void do_bang(int addr_count, exarg_T *eap, int forceit, int do_in, int do_out) AppendToRedobuffLit(cmd, -1); xfree(cmd); - AppendToRedobuff((char_u *)"\n"); - bangredo = FALSE; + AppendToRedobuff("\n"); + bangredo = false; } /* * Add quotes around the command, for shells that need them. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6b661cff11..73b81ac2d9 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6794,7 +6794,7 @@ do_exedit ( int ms = msg_scroll; if (eap->nextcmd != NULL) { - stuffReadbuff(eap->nextcmd); + stuffReadbuff((const char *)eap->nextcmd); eap->nextcmd = NULL; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3bd8d580ab..a0981a42ce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -2554,19 +2554,22 @@ void cmdline_paste_str(char_u *s, int literally) else while (*s != NUL) { cv = *s; - if (cv == Ctrl_V && s[1]) - ++s; - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (cv == Ctrl_V && s[1]) { + s++; + } + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (cv == Ctrl_V || c == ESC || c == Ctrl_C || c == CAR || c == NL || c == Ctrl_L #ifdef UNIX || c == intr_char #endif - || (c == Ctrl_BSL && *s == Ctrl_N)) + || (c == Ctrl_BSL && *s == Ctrl_N)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -4636,7 +4639,7 @@ in_history ( /// /// @return Any value from HistoryType enum, including HIST_INVALID. May not /// return HIST_DEFAULT unless return_default is true. -HistoryType get_histtype(const char_u *const name, const size_t len, +HistoryType get_histtype(const char *const name, const size_t len, const bool return_default) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5029,7 +5032,7 @@ void ex_history(exarg_T *eap) while (ASCII_ISALPHA(*end) || vim_strchr((char_u *)":=@>/?", *end) != NULL) end++; - histype1 = get_histtype(arg, end - arg, false); + histype1 = get_histtype((const char *)arg, end - arg, false); if (histype1 == HIST_INVALID) { if (STRNICMP(arg, "all", end - arg) == 0) { histype1 = 0; @@ -5288,18 +5291,18 @@ static int ex_window(void) cmdwin_result = Ctrl_C; /* Set the new command line from the cmdline buffer. */ xfree(ccline.cmdbuff); - if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { /* :qa[!] typed */ - char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; + if (cmdwin_result == K_XF1 || cmdwin_result == K_XF2) { // :qa[!] typed + const char *p = (cmdwin_result == K_XF2) ? "qa" : "qa!"; if (histtype == HIST_CMD) { - /* Execute the command directly. */ - ccline.cmdbuff = vim_strsave((char_u *)p); + // Execute the command directly. + ccline.cmdbuff = (char_u *)xstrdup(p); cmdwin_result = CAR; } else { - /* First need to cancel what we were doing. */ + // First need to cancel what we were doing. ccline.cmdbuff = NULL; stuffcharReadbuff(':'); - stuffReadbuff((char_u *)p); + stuffReadbuff(p); stuffcharReadbuff(CAR); } } else if (cmdwin_result == K_XF2) { /* :qa typed */ diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 67ac9f9957..127efda65c 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -614,10 +614,12 @@ readfile ( return FAIL; } #ifdef UNIX - /* Set swap file protection bits after creating it. */ + // Set swap file protection bits after creating it. if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL - && curbuf->b_ml.ml_mfp->mf_fname != NULL) - (void)os_setperm(curbuf->b_ml.ml_mfp->mf_fname, (long)swap_mode); + && curbuf->b_ml.ml_mfp->mf_fname != NULL) { + (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname, + (long)swap_mode); + } #endif } @@ -2870,9 +2872,9 @@ buf_write ( xfree(backup); backup = NULL; } else { - /* set file protection same as original file, but - * strip s-bit */ - (void)os_setperm(backup, perm & 0777); + // set file protection same as original file, but + // strip s-bit. + (void)os_setperm((const char *)backup, perm & 0777); #ifdef UNIX /* @@ -2883,7 +2885,8 @@ buf_write ( */ if (file_info_new.stat.st_gid != file_info_old.stat.st_gid && os_fchown(bfd, -1, file_info_old.stat.st_gid) != 0) { - os_setperm(backup, (perm & 0707) | ((perm & 07) << 3)); + os_setperm((const char *)backup, + (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX mch_copy_sec(fname, backup); @@ -3037,8 +3040,8 @@ nobackup: && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { perm |= 0200; - (void)os_setperm(fname, perm); - made_writable = TRUE; + (void)os_setperm((const char *)fname, perm); + made_writable = true; } #endif @@ -3402,8 +3405,9 @@ restore_backup: || file_info.stat.st_uid != file_info_old.stat.st_uid || file_info.stat.st_gid != file_info_old.stat.st_gid) { os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); - if (perm >= 0) /* set permission again, may have changed */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set permission again, may have changed. + (void)os_setperm((const char *)wfname, perm); + } } buf_set_file_id(buf); } else if (!buf->file_id_valid) { @@ -3421,8 +3425,9 @@ restore_backup: if (made_writable) perm &= ~0200; /* reset 'w' bit for security reasons */ #endif - if (perm >= 0) /* set perm. of new file same as old file */ - (void)os_setperm(wfname, perm); + if (perm >= 0) { // Set perm. of new file same as old file. + (void)os_setperm((const char *)wfname, perm); + } #ifdef HAVE_ACL /* Probably need to set the ACL before changing the user (can't set the * ACL on a file the user doesn't own). */ @@ -3628,7 +3633,7 @@ restore_backup: close(empty_fd); } if (org != NULL) { - os_setperm((char_u *)org, os_getperm((const char *)fname) & 0777); + os_setperm(org, os_getperm((const char *)fname) & 0777); xfree(org); } } @@ -4690,8 +4695,8 @@ int vim_rename(const char_u *from, const char_u *to) errmsg = _("E210: Error reading \"%s\""); to = from; } -#ifndef UNIX /* for Unix os_open() already set the permission */ - os_setperm(to, perm); +#ifndef UNIX // For Unix os_open() already set the permission. + os_setperm((const char *)to, perm); #endif #ifdef HAVE_ACL mch_set_acl(to, acl); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b64f089b96..7143819e21 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -235,19 +235,18 @@ char_u *get_inserted(void) return get_buffcont(&redobuff, FALSE); } -/* - * Add string "s" after the current block of buffer "buf". - * K_SPECIAL and CSI should have been escaped already. - */ -static void -add_buff ( - buffheader_T *buf, - char_u *s, - ssize_t slen // length of "s" or -1 -) +/// Add string after the current block of the given buffer +/// +/// K_SPECIAL and CSI should have been escaped already. +/// +/// @param[out] buf Buffer to add to. +/// @param[in] s String to add. +/// @param[in] slen String length or -1 for NUL-terminated string. +static void add_buff(buffheader_T *const buf, const char *const s, + ptrdiff_t slen) { if (slen < 0) { - slen = (ssize_t)STRLEN(s); + slen = (ptrdiff_t)strlen(s); } if (slen == 0) { // don't add empty strings return; @@ -292,9 +291,8 @@ add_buff ( */ static void add_num_buff(buffheader_T *buf, long n) { - char_u number[32]; - - sprintf((char *)number, "%" PRId64, (int64_t)n); + char number[32]; + snprintf(number, sizeof(number), "%ld", n); add_buff(buf, number, -1L); } @@ -304,27 +302,29 @@ static void add_num_buff(buffheader_T *buf, long n) */ static void add_char_buff(buffheader_T *buf, int c) { - char_u bytes[MB_MAXBYTES + 1]; - int len; - int i; - char_u temp[4]; + char bytes[MB_MAXBYTES + 1]; - if (IS_SPECIAL(c)) + int len; + if (IS_SPECIAL(c)) { len = 1; - else - len = (*mb_char2bytes)(c, bytes); - for (i = 0; i < len; ++i) { - if (!IS_SPECIAL(c)) + } else { + len = (*mb_char2bytes)(c, (char_u *)bytes); + } + + for (int i = 0; i < len; i++) { + if (!IS_SPECIAL(c)) { c = bytes[i]; + } + char temp[4]; if (IS_SPECIAL(c) || c == K_SPECIAL || c == NUL) { - /* translate special key code into three byte sequence */ - temp[0] = K_SPECIAL; - temp[1] = (char_u)K_SECOND(c); - temp[2] = (char_u)K_THIRD(c); + // Translate special key code into three byte sequence. + temp[0] = (char)K_SPECIAL; + temp[1] = (char)K_SECOND(c); + temp[2] = (char)K_THIRD(c); temp[3] = NUL; } else { - temp[0] = (char_u)c; + temp[0] = (char)c; temp[1] = NUL; } add_buff(buf, temp, -1L); @@ -479,16 +479,14 @@ static int save_level = 0; void saveRedobuff(void) { - char_u *s; - if (save_level++ == 0) { save_redobuff = redobuff; redobuff.bh_first.b_next = NULL; save_old_redobuff = old_redobuff; old_redobuff.bh_first.b_next = NULL; - /* Make a copy, so that ":normal ." in a function works. */ - s = get_buffcont(&save_redobuff, FALSE); + // Make a copy, so that ":normal ." in a function works. + char *const s = (char *)get_buffcont(&save_redobuff, false); if (s != NULL) { add_buff(&redobuff, s, -1L); xfree(s); @@ -514,10 +512,11 @@ void restoreRedobuff(void) * Append "s" to the redo buffer. * K_SPECIAL and CSI should already have been escaped. */ -void AppendToRedobuff(char_u *s) +void AppendToRedobuff(const char *s) { - if (!block_redo) - add_buff(&redobuff, s, -1L); + if (!block_redo) { + add_buff(&redobuff, (const char *)s, -1L); + } } /* @@ -530,44 +529,47 @@ AppendToRedobuffLit ( int len /* length of "str" or -1 for up to the NUL */ ) { - char_u *s = str; - int c; - char_u *start; - - if (block_redo) + if (block_redo) { return; + } - while (len < 0 ? *s != NUL : s - str < len) { - /* Put a string of normal characters in the redo buffer (that's - * faster). */ - start = s; - while (*s >= ' ' && *s < DEL && (len < 0 || s - str < len)) - ++s; - - /* Don't put '0' or '^' as last character, just in case a CTRL-D is - * typed next. */ - if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) - --s; - if (s > start) + const char *s = (const char *)str; + while (len < 0 ? *s != NUL : s - (const char *)str < len) { + // Put a string of normal characters in the redo buffer (that's + // faster). + const char *start = s; + while (*s >= ' ' && *s < DEL && (len < 0 || s - (const char *)str < len)) { + s++; + } + + // Don't put '0' or '^' as last character, just in case a CTRL-D is + // typed next. + if (*s == NUL && (s[-1] == '0' || s[-1] == '^')) { + s--; + } + if (s > start) { add_buff(&redobuff, start, (long)(s - start)); + } - if (*s == NUL || (len >= 0 && s - str >= len)) + if (*s == NUL || (len >= 0 && s - (const char *)str >= len)) { break; + } - /* Handle a special or multibyte character. */ - if (has_mbyte) - /* Handle composing chars separately. */ - c = mb_cptr2char_adv(&s); - else - c = *s++; - if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) + // Handle a special or multibyte character. + // Composing chars separately are handled separately. + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&s) + : (uint8_t)(*s++)); + if (c < ' ' || c == DEL || (*s == NUL && (c == '0' || c == '^'))) { add_char_buff(&redobuff, Ctrl_V); + } - /* CTRL-V '0' must be inserted as CTRL-V 048 */ - if (*s == NUL && c == '0') - add_buff(&redobuff, (char_u *)"048", 3L); - else + // CTRL-V '0' must be inserted as CTRL-V 048. + if (*s == NUL && c == '0') { + add_buff(&redobuff, "048", 3L); + } else { add_char_buff(&redobuff, c); + } } } @@ -594,19 +596,19 @@ void AppendNumberToRedobuff(long n) * Append string "s" to the stuff buffer. * CSI and K_SPECIAL must already have been escaped. */ -void stuffReadbuff(char_u *s) +void stuffReadbuff(const char *s) { add_buff(&readbuf1, s, -1L); } /// Append string "s" to the redo stuff buffer. /// @remark CSI and K_SPECIAL must already have been escaped. -void stuffRedoReadbuff(char_u *s) +void stuffRedoReadbuff(const char *s) { add_buff(&readbuf2, s, -1L); } -void stuffReadbuffLen(char_u *s, long len) +void stuffReadbuffLen(const char *s, long len) { add_buff(&readbuf1, s, len); } @@ -616,19 +618,18 @@ void stuffReadbuffLen(char_u *s, long len) * escaping other K_SPECIAL and CSI bytes. * Change CR, LF and ESC into a space. */ -void stuffReadbuffSpec(char_u *s) +void stuffReadbuffSpec(const char *s) { - int c; - while (*s != NUL) { - if (*s == K_SPECIAL && s[1] != NUL && s[2] != NUL) { - /* Insert special key literally. */ - stuffReadbuffLen(s, 3L); + if ((uint8_t)(*s) == K_SPECIAL && s[1] != NUL && s[2] != NUL) { + // Insert special key literally. + stuffReadbuffLen(s, 3); s += 3; } else { - c = mb_ptr2char_adv(&s); - if (c == CAR || c == NL || c == ESC) + int c = mb_ptr2char_adv((const char_u **)&s); + if (c == CAR || c == NL || c == ESC) { c = ' '; + } stuffcharReadbuff(c); } } @@ -747,8 +748,8 @@ int start_redo(long count, int old_redo) /* copy the buffer name, if present */ if (c == '"') { - add_buff(&readbuf2, (char_u *)"\"", 1L); - c = read_redo(FALSE, old_redo); + add_buff(&readbuf2, "\"", 1L); + c = read_redo(false, old_redo); /* if a numbered buffer is used, increment the number */ if (c >= '1' && c < '9') @@ -1091,21 +1092,19 @@ static void gotchars(char_u *chars, size_t len) { char_u *s = chars; int c; - char_u buf[2]; // remember how many chars were last recorded if (Recording) { last_recorded_len += len; } - buf[1] = NUL; while (len--) { // Handle one byte at a time; no translation to be done. c = *s++; updatescript(c); if (Recording) { - buf[0] = (char_u)c; + char buf[2] = { (char)c, NUL }; add_buff(&recordbuff, buf, 1L); } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index d87407f099..c15287aa38 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1199,6 +1199,7 @@ EXTERN char_u e_dirnotf[] INIT(= N_( "E919: Directory not found in '%s': \"%s\"")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); +EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index 621fa6fefc..d96848754c 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -625,7 +625,7 @@ static int utf_safe_read_char_adv(const char_u **s, size_t *n) * Get character at **pp and advance *pp to the next character. * Note: composing characters are skipped! */ -int mb_ptr2char_adv(char_u **pp) +int mb_ptr2char_adv(const char_u **const pp) { int c; @@ -638,7 +638,7 @@ int mb_ptr2char_adv(char_u **pp) * Get character at **pp and advance *pp to the next character. * Note: composing characters are returned as separate characters. */ -int mb_cptr2char_adv(char_u **pp) +int mb_cptr2char_adv(const char_u **pp) { int c; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 6ef929120e..d4919dc3b6 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1155,7 +1155,7 @@ static void normal_check_stuff_buffer(NormalState *s) if (need_start_insertmode && goto_im() && !VIsual_active) { need_start_insertmode = false; - stuffReadbuff((uint8_t *)"i"); // start insert mode next + stuffReadbuff("i"); // start insert mode next // skip the fileinfo message now, because it would be shown // after insert mode finishes! need_fileinfo = false; @@ -1469,8 +1469,9 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * If 'cpoptions' does not contain 'r', insert the search * pattern to really repeat the same command. */ - if (vim_strchr(p_cpo, CPO_REDO) == NULL) + if (vim_strchr(p_cpo, CPO_REDO) == NULL) { AppendToRedobuffLit(cap->searchbuf, -1); + } AppendToRedobuff(NL_STR); } else if (cap->cmdchar == ':') { /* do_cmdline() has stored the first typed line in @@ -1853,10 +1854,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) break; case OP_FILTER: - if (vim_strchr(p_cpo, CPO_FILTER) != NULL) - AppendToRedobuff((char_u *)"!\r"); /* use any last used !cmd */ - else - bangredo = true; /* do_bang() will put cmd in redo buffer */ + if (vim_strchr(p_cpo, CPO_FILTER) != NULL) { + AppendToRedobuff("!\r"); // Use any last used !cmd. + } else { + bangredo = true; // do_bang() will put cmd in redo buffer. + } case OP_INDENT: case OP_COLON: @@ -2026,43 +2028,44 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) static void op_colon(oparg_T *oap) { stuffcharReadbuff(':'); - if (oap->is_VIsual) - stuffReadbuff((char_u *)"'<,'>"); - else { - /* - * Make the range look nice, so it can be repeated. - */ - if (oap->start.lnum == curwin->w_cursor.lnum) + if (oap->is_VIsual) { + stuffReadbuff("'<,'>"); + } else { + // Make the range look nice, so it can be repeated. + if (oap->start.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else + } else { stuffnumReadbuff((long)oap->start.lnum); + } if (oap->end.lnum != oap->start.lnum) { stuffcharReadbuff(','); - if (oap->end.lnum == curwin->w_cursor.lnum) + if (oap->end.lnum == curwin->w_cursor.lnum) { stuffcharReadbuff('.'); - else if (oap->end.lnum == curbuf->b_ml.ml_line_count) + } else if (oap->end.lnum == curbuf->b_ml.ml_line_count) { stuffcharReadbuff('$'); - else if (oap->start.lnum == curwin->w_cursor.lnum) { - stuffReadbuff((char_u *)".+"); + } else if (oap->start.lnum == curwin->w_cursor.lnum) { + stuffReadbuff(".+"); stuffnumReadbuff(oap->line_count - 1); - } else + } else { stuffnumReadbuff((long)oap->end.lnum); + } } } - if (oap->op_type != OP_COLON) - stuffReadbuff((char_u *)"!"); + if (oap->op_type != OP_COLON) { + stuffReadbuff("!"); + } if (oap->op_type == OP_INDENT) { - stuffReadbuff(get_equalprg()); - stuffReadbuff((char_u *)"\n"); + stuffReadbuff((const char *)get_equalprg()); + stuffReadbuff("\n"); } else if (oap->op_type == OP_FORMAT) { if (*curbuf->b_p_fp != NUL) { - stuffReadbuff(curbuf->b_p_fp); + stuffReadbuff((const char *)curbuf->b_p_fp); } else if (*p_fp != NUL) { - stuffReadbuff(p_fp); + stuffReadbuff((const char *)p_fp); } else { - stuffReadbuff((char_u *)"fmt"); + stuffReadbuff("fmt"); } - stuffReadbuff((char_u *)"\n']"); + stuffReadbuff("\n']"); } /* @@ -2304,7 +2307,7 @@ do_mouse ( if (VIsual_active) { if (VIsual_select) { stuffcharReadbuff(Ctrl_G); - stuffReadbuff((char_u *)"\"+p"); + stuffReadbuff("\"+p"); } else { stuffcharReadbuff('y'); stuffcharReadbuff(K_MIDDLEMOUSE); @@ -4476,7 +4479,7 @@ static void nv_colon(cmdarg_T *cap) /* translate "count:" into ":.,.+(count - 1)" */ stuffcharReadbuff('.'); if (cap->count0 > 1) { - stuffReadbuff((char_u *)",.+"); + stuffReadbuff(",.+"); stuffnumReadbuff(cap->count0 - 1L); } } @@ -6156,17 +6159,15 @@ static void nv_abbrev(cmdarg_T *cap) */ static void nv_optrans(cmdarg_T *cap) { - static char_u *(ar[8]) = {(char_u *)"dl", (char_u *)"dh", - (char_u *)"d$", (char_u *)"c$", - (char_u *)"cl", (char_u *)"cc", - (char_u *)"yy", (char_u *)":s\r"}; - static char_u *str = (char_u *)"xXDCsSY&"; + static const char *(ar[]) = { "dl", "dh", "d$", "c$", "cl", "cc", "yy", + ":s\r" }; + static const char *str = "xXDCsSY&"; if (!checkclearopq(cap->oap)) { if (cap->count0) { stuffnumReadbuff(cap->count0); } - stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); + stuffReadbuff(ar[strchr(str, (char)cap->cmdchar) - str]); } cap->opcount = 0; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index fd1e2d20f2..68ef27222c 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -1109,7 +1109,6 @@ int insert_reg( ) { int retval = OK; - char_u *arg; int allocated; /* @@ -1125,21 +1124,24 @@ int insert_reg( if (regname != NUL && !valid_yank_reg(regname, false)) return FAIL; - if (regname == '.') /* insert last inserted text */ - retval = stuff_inserted(NUL, 1L, TRUE); - else if (get_spec_reg(regname, &arg, &allocated, TRUE)) { - if (arg == NULL) + char_u *arg; + if (regname == '.') { // Insert last inserted text. + retval = stuff_inserted(NUL, 1L, true); + } else if (get_spec_reg(regname, &arg, &allocated, true)) { + if (arg == NULL) { return FAIL; - stuffescaped(arg, literally); - if (allocated) + } + stuffescaped((const char *)arg, literally); + if (allocated) { xfree(arg); - } else { /* name or number register */ + } + } else { // Name or number register. yankreg_T *reg = get_yank_register(regname, YREG_PASTE); if (reg->y_array == NULL) { retval = FAIL; } else { for (size_t i = 0; i < reg->y_size; i++) { - stuffescaped(reg->y_array[i], literally); + stuffescaped((const char *)reg->y_array[i], literally); // Insert a newline between lines and after last line if // y_type is kMTLineWise. if (reg->y_type == kMTLineWise || i < reg->y_size - 1) { @@ -1156,29 +1158,29 @@ int insert_reg( * Stuff a string into the typeahead buffer, such that edit() will insert it * literally ("literally" TRUE) or interpret is as typed characters. */ -static void stuffescaped(char_u *arg, int literally) +static void stuffescaped(const char *arg, int literally) { - int c; - char_u *start; - while (*arg != NUL) { - /* Stuff a sequence of normal ASCII characters, that's fast. Also - * stuff K_SPECIAL to get the effect of a special key when "literally" - * is TRUE. */ - start = arg; - while ((*arg >= ' ' && *arg < DEL) || (*arg == K_SPECIAL && !literally)) - ++arg; - if (arg > start) + // Stuff a sequence of normal ASCII characters, that's fast. Also + // stuff K_SPECIAL to get the effect of a special key when "literally" + // is TRUE. + const char *const start = arg; + while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL + && !literally)) { + arg++; + } + if (arg > start) { stuffReadbuffLen(start, (long)(arg - start)); + } /* stuff a single special character */ if (*arg != NUL) { - if (has_mbyte) - c = mb_cptr2char_adv(&arg); - else - c = *arg++; - if (literally && ((c < ' ' && c != TAB) || c == DEL)) + const int c = (has_mbyte + ? mb_cptr2char_adv((const char_u **)&arg) + : (uint8_t)(*arg++)); + if (literally && ((c < ' ' && c != TAB) || c == DEL)) { stuffcharReadbuff(Ctrl_V); + } stuffcharReadbuff(c); } } @@ -2663,7 +2665,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // back to the previous line in the case of 'noautoindent' and // 'backspace' includes "eol". So we insert a dummy space for Ctrl_U // to consume. - stuffReadbuff((char_u *)"\n "); + stuffReadbuff("\n "); stuffcharReadbuff(Ctrl_U); } } @@ -2675,7 +2677,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // character. Simulate it with motion commands after the insert. if (flags & PUT_CURSEND) { if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"j0"); + stuffReadbuff("j0"); } else { // Avoid ringing the bell from attempting to move into the space after // the current line. We can stuff the readbuffer with "l" if: @@ -2705,7 +2707,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } } else if (flags & PUT_LINE) { - stuffReadbuff((char_u *)"g'["); + stuffReadbuff("g'["); } // So the 'u' command restores cursor position after ".p, save the cursor @@ -4981,7 +4983,7 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L); } -void write_reg_contents_lst(int name, char_u **strings, int maxlen, +void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type, colnr_T block_len) { diff --git a/src/nvim/option.c b/src/nvim/option.c index 1881b329ec..8a80c46cf2 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -3419,15 +3419,18 @@ static char_u *set_chars_option(char_u **varp) && p[len] == ':' && p[len + 1] != NUL) { s = p + len + 1; - c1 = mb_ptr2char_adv(&s); - if (mb_char2cells(c1) > 1) + c1 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c1) > 1) { continue; + } if (tab[i].cp == &lcs_tab2) { - if (*s == NUL) + if (*s == NUL) { continue; - c2 = mb_ptr2char_adv(&s); - if (mb_char2cells(c2) > 1) + } + c2 = mb_ptr2char_adv((const char_u **)&s); + if (mb_char2cells(c2) > 1) { continue; + } } if (*s == ',' || *s == NUL) { if (round) { @@ -6887,8 +6890,8 @@ void set_fileformat(int eol_style, int opt_flags) need_maketitle = true; // Set window title later. } -/// Skip to next part of an option argument: Skip space and comma. -char_u *skip_to_option_part(char_u *p) +/// Skip to next part of an option argument: skip space and comma +char_u *skip_to_option_part(const char_u *p) { if (*p == ',') { p++; @@ -6896,7 +6899,7 @@ char_u *skip_to_option_part(char_u *p) while (*p == ' ') { p++; } - return p; + return (char_u *)p; } /// Isolate one part of a string option separated by `sep_chars`. diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 0372bc8a8c..3833a43f5f 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -622,11 +622,11 @@ int32_t os_getperm(const char *name) /// Set the permission of a file. /// /// @return `OK` for success, `FAIL` for failure. -int os_setperm(const char_u *name, int perm) +int os_setperm(const char *const name, int perm) FUNC_ATTR_NONNULL_ALL { int r; - RUN_UV_FS_FUNC(r, uv_fs_chmod, (const char *)name, perm, NULL); + RUN_UV_FS_FUNC(r, uv_fs_chmod, name, perm, NULL); return (r == kLibuvSuccess ? OK : FAIL); } diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 9c6f02f778..9baa53d2a2 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -2221,10 +2221,11 @@ collection: if (*regparse == '[') endc = get_coll_element(®parse); if (endc == 0) { - if (has_mbyte) - endc = mb_ptr2char_adv(®parse); - else + if (has_mbyte) { + endc = mb_ptr2char_adv((const char_u **)®parse); + } else { endc = *regparse++; + } } /* Handle \o40, \x20 and \u20AC style sequences */ @@ -6271,8 +6272,8 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) str2 = s2; c1 = c2 = 0; while ((int)(str1 - s1) < *n) { - c1 = mb_ptr2char_adv(&str1); - c2 = mb_ptr2char_adv(&str2); + c1 = mb_ptr2char_adv((const char_u **)&str1); + c2 = mb_ptr2char_adv((const char_u **)&str2); /* decompose the character if necessary, into 'base' characters * because I don't care about Arabic, I will hard-code the Hebrew @@ -6586,7 +6587,6 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, if (expr != NULL) { typval_T argv[2]; int dummy; - char_u buf[NUMBUFLEN]; typval_T rettv; staticList10_T matchList; @@ -6616,7 +6616,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, clear_submatch_list(&matchList); } } - eval_result = get_tv_string_buf_chk(&rettv, buf); + char buf[NUMBUFLEN]; + eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); if (eval_result != NULL) { eval_result = vim_strsave(eval_result); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 505d04ed56..cf460adb82 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3141,7 +3141,7 @@ win_line ( p_extra = extra; c = *p_extra; - mb_c = mb_ptr2char_adv(&p_extra); + mb_c = mb_ptr2char_adv((const char_u **)&p_extra); mb_utf8 = (c >= 0x80); n_extra = (int)STRLEN(p_extra); c_extra = NUL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 84bee9b97f..d4f49bffb2 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2309,10 +2309,11 @@ int captype(char_u *word, char_u *end) for (p = word; !spell_iswordp_nmw(p, curwin); mb_ptr_adv(p)) if (end == NULL ? *p == NUL : p >= end) return 0; // only non-word characters, illegal word - if (has_mbyte) - c = mb_ptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_ptr2char_adv((const char_u **)&p); + } else { c = *p++; + } firstcap = allcap = SPELL_ISUPPER(c); // Need to check all letters to find a word with mixed upper/lower. @@ -2607,14 +2608,15 @@ static bool spell_iswordp(char_u *p, win_T *wp) // Returns true if "p" points to a word character. // Unlike spell_iswordp() this doesn't check for "midword" characters. -bool spell_iswordp_nmw(char_u *p, win_T *wp) +bool spell_iswordp_nmw(const char_u *p, win_T *wp) { int c; if (has_mbyte) { c = mb_ptr2char(p); - if (c > 255) + if (c > 255) { return spell_mb_isword_class(mb_get_class(p), wp); + } return spelltab.st_isw[c]; } return spelltab.st_isw[*p]; @@ -2675,7 +2677,7 @@ int spell_casefold(char_u *str, int len, char_u *buf, int buflen) buf[outi] = NUL; return FAIL; } - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); } buf[outi] = NUL; @@ -2937,7 +2939,7 @@ void spell_suggest(int count) // For redo we use a change-word command. ResetRedobuff(); - AppendToRedobuff((char_u *)"ciw"); + AppendToRedobuff("ciw"); AppendToRedobuffLit(p + c, stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); @@ -3406,17 +3408,19 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper) int l; p = word; - if (has_mbyte) - c = mb_cptr2char_adv(&p); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&p); + } else { c = *p++; - if (upper) + } + if (upper) { c = SPELL_TOUPPER(c); - else + } else { c = SPELL_TOFOLD(c); - if (has_mbyte) + } + if (has_mbyte) { l = mb_char2bytes(c, wcopy); - else { + } else { l = 1; wcopy[0] = c; } @@ -3433,10 +3437,11 @@ static void allcap_copy(char_u *word, char_u *wcopy) d = wcopy; for (s = word; *s != NUL; ) { - if (has_mbyte) - c = mb_cptr2char_adv(&s); - else + if (has_mbyte) { + c = mb_cptr2char_adv((const char_u **)&s); + } else { c = *s++; + } if (c == 0xdf) { c = 'S'; @@ -5938,12 +5943,12 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // The sl_sal_first[] table contains the translation for chars up to // 255, sl_sal the rest. for (s = inword; *s != NUL; ) { - c = mb_cptr2char_adv(&s); - if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) + c = mb_cptr2char_adv((const char_u **)&s); + if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { c = ' '; - else if (c < 256) + } else if (c < 256) { c = slang->sl_sal_first[c]; - else { + } else { ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; if (ip == NULL) // empty list, can't match c = NUL; @@ -6228,9 +6233,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) int word[MAXWLEN]; int wres[MAXWLEN]; int l; - char_u *s; int *ws; - char_u *t; int *pf; int i, j, z; int reslen; @@ -6250,9 +6253,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // Remove accents, if wanted. We actually remove all non-word characters. // But keep white space. wordlen = 0; - for (s = inword; *s != NUL; ) { - t = s; - c = mb_cptr2char_adv(&s); + for (const char_u *s = inword; *s != NUL; ) { + const char_u *t = s; + c = mb_cptr2char_adv((const char_u **)&s); if (slang->sl_rem_accents) { if (enc_utf8 ? utf_class(c) == 0 : ascii_iswhite(c)) { if (did_white) @@ -6261,8 +6264,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) did_white = true; } else { did_white = false; - if (!spell_iswordp_nmw(t, curwin)) + if (!spell_iswordp_nmw(t, curwin)) { continue; + } } } word[wordlen++] = c; @@ -6309,7 +6313,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) continue; ++k; } - s = smp[n].sm_rules; + char_u *s = smp[n].sm_rules; pri = 5; // default priority p0 = *s; @@ -6708,25 +6712,30 @@ soundalike_score ( // support multi-byte characters. static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) { - int *cnt; - int badlen, goodlen; // lengths including NUL + int *cnt; int j, i; int t; int bc, gc; int pbc, pgc; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; const bool l_has_mbyte = has_mbyte; + // Lengths with NUL. + int badlen; + int goodlen; if (l_has_mbyte) { // Get the characters from the multi-byte strings and put them in an // int array for easy access. - for (p = badword, badlen = 0; *p != NUL; ) + badlen = 0; + for (const char_u *p = badword; *p != NUL; ) { wbadword[badlen++] = mb_cptr2char_adv(&p); + } wbadword[badlen++] = 0; - for (p = goodword, goodlen = 0; *p != NUL; ) + goodlen = 0; + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[goodlen++] = mb_cptr2char_adv(&p); + } wgoodword[goodlen++] = 0; } else { badlen = (int)STRLEN(badword) + 1; @@ -6960,19 +6969,20 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo int score_off; int minscore; int round; - char_u *p; int wbadword[MAXWLEN]; int wgoodword[MAXWLEN]; // Get the characters from the multi-byte strings and put them in an // int array for easy access. bi = 0; - for (p = badword; *p != NUL; ) + for (const char_u *p = badword; *p != NUL; ) { wbadword[bi++] = mb_cptr2char_adv(&p); + } wbadword[bi++] = 0; gi = 0; - for (p = goodword; *p != NUL; ) + for (const char_u *p = goodword; *p != NUL; ) { wgoodword[gi++] = mb_cptr2char_adv(&p); + } wgoodword[gi++] = 0; // The idea is to go from start to end over the words. So long as diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 6ba2801203..4d7ff558ad 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -1435,7 +1435,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // First count the number of items for each list. Temporarily use // sl_sal_first[] for this. for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); + c = mb_cptr2char_adv((const char_u **)&p); mb_cptr_adv(s); if (c >= 256) ++lp->sl_sal_first[c & 0xff]; @@ -1455,8 +1455,8 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // list. memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); for (p = from, s = to; *p != NUL && *s != NUL; ) { - c = mb_cptr2char_adv(&p); - i = mb_cptr2char_adv(&s); + c = mb_cptr2char_adv((const char_u **)&p); + i = mb_cptr2char_adv((const char_u **)&s); if (c >= 256) { // Append the from-to chars at the end of the list with // the low byte. @@ -1542,8 +1542,9 @@ static int *mb_str2wide(char_u *s) int i = 0; int *res = xmalloc((mb_charlen(s) + 1) * sizeof(int)); - for (char_u *p = s; *p != NUL; ) - res[i++] = mb_ptr2char_adv(&p); + for (char_u *p = s; *p != NUL; ) { + res[i++] = mb_ptr2char_adv((const char_u **)&p); + } res[i] = NUL; return res; @@ -2486,13 +2487,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Check that every character appears only once. for (p = items[1]; *p != NUL; ) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); if ((!GA_EMPTY(&spin->si_map) && vim_strchr(spin->si_map.ga_data, c) != NULL) - || vim_strchr(p, c) != NULL) + || vim_strchr(p, c) != NULL) { smsg(_("Duplicate character in MAP in %s line %d"), fname, lnum); + } } // We simply concatenate all the MAP strings, separated by @@ -2717,12 +2719,12 @@ static unsigned get_affitem(int flagtype, char_u **pp) } res = getdigits_int(pp); } else { - res = mb_ptr2char_adv(pp); + res = mb_ptr2char_adv((const char_u **)pp); if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG && res >= 'A' && res <= 'Z')) { if (**pp == NUL) return 0; - res = mb_ptr2char_adv(pp) + (res << 16); + res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } return res; @@ -2823,12 +2825,14 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) case AFT_CAPLONG: case AFT_LONG: for (p = afflist; *p != NUL; ) { - n = mb_ptr2char_adv(&p); + n = mb_ptr2char_adv((const char_u **)&p); if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) - && *p != NUL) - n = mb_ptr2char_adv(&p) + (n << 16); - if (n == flag) + && *p != NUL) { + n = mb_ptr2char_adv((const char_u **)&p) + (n << 16); + } + if (n == flag) { return true; + } } break; @@ -5436,9 +5440,10 @@ static void init_spellfile(void) fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) ->lp_slang->sl_fname; vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", - fname != NULL - && strstr((char *)path_tail(fname), ".ascii.") != NULL - ? (char_u *)"ascii" : spell_enc()); + ((fname != NULL + && strstr((char *)path_tail(fname), ".ascii.") != NULL) + ? "ascii" + : (const char *)spell_enc())); set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL); break; } @@ -5465,9 +5470,9 @@ static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp) EMSG(_(e_affform)); return FAIL; } - f = mb_ptr2char_adv(&pf); - l = mb_ptr2char_adv(&pl); - u = mb_ptr2char_adv(&pu); + f = mb_ptr2char_adv((const char_u **)&pf); + l = mb_ptr2char_adv((const char_u **)&pl); + u = mb_ptr2char_adv((const char_u **)&pu); // Every character that appears is a word character. if (f < 256) new_st.st_isw[f] = true; @@ -5532,7 +5537,7 @@ set_spell_charflags ( } if (*p != NUL) { - c = mb_ptr2char_adv(&p); + c = mb_ptr2char_adv((const char_u **)&p); new_st.st_fold[i + 128] = c; if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) new_st.st_upper[c] = i + 128; @@ -5619,12 +5624,13 @@ static void set_map_str(slang_T *lp, char_u *map) // "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and // before the same slash. For characters above 255 sl_map_hash is used. for (p = map; *p != NUL; ) { - c = mb_cptr2char_adv(&p); - if (c == '/') + c = mb_cptr2char_adv((const char_u **)&p); + if (c == '/') { headc = 0; - else { - if (headc == 0) + } else { + if (headc == 0) { headc = c; + } // Characters above 255 don't fit in sl_map_array[], put them in // the hash table. Each entry is the char, a NUL the headchar and diff --git a/src/nvim/strings.c b/src/nvim/strings.c index e20979c307..5dcffe00e0 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -598,22 +598,21 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// free "*tofree". /// /// @return String value or NULL in case of error. -static char *tv_str(typval_T *tvs, int *idxp, char ** const tofree) +static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int idx = *idxp - 1; - char *s = NULL; + const char *s = NULL; if (tvs[idx].v_type == VAR_UNKNOWN) { EMSG(_(e_printf)); } else { (*idxp)++; if (tvs[idx].v_type == VAR_STRING || tvs[idx].v_type == VAR_NUMBER) { - s = (char *)get_tv_string_chk(&tvs[idx]); + s = tv_get_string_chk(&tvs[idx]); *tofree = NULL; } else { - s = encode_tv2echo(&tvs[idx], NULL); - *tofree = s; + s = *tofree = encode_tv2echo(&tvs[idx], NULL); } } return s; @@ -953,7 +952,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, case 's': case 'S': str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, char *); + : va_arg(ap, const char *); if (!str_arg) { str_arg = "[NULL]"; str_arg_l = 6; diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 010ef2c8fa..4d4e8d9bb9 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1140,7 +1140,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, EMSG2(_(e_not_open), file_name); goto theend; } - (void)os_setperm((char_u *)file_name, perm); + (void)os_setperm(file_name, perm); if (p_verbose > 0) { verbose_enter(); smsg(_("Writing undo file: %s"), file_name); @@ -1165,7 +1165,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, && os_fileinfo(file_name, &file_info_new) && file_info_old.stat.st_gid != file_info_new.stat.st_gid && os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) { - os_setperm((char_u *)file_name, (perm & 0707) | ((perm & 07) << 3)); + os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); } # ifdef HAVE_SELINUX if (buf->b_ffname != NULL) diff --git a/test/functional/eval/input_spec.lua b/test/functional/eval/input_spec.lua new file mode 100644 index 0000000000..393fc10175 --- /dev/null +++ b/test/functional/eval/input_spec.lua @@ -0,0 +1,38 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') + +local feed = helpers.feed +local clear = helpers.clear +local command = helpers.command + +local screen + +before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() +end) + +describe('input()', function() + it('works correctly with multiline prompts', function() + feed([[:call input("Test\nFoo")]]) + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + Test | + Foo^ | + ]], {{bold=true, foreground=Screen.colors.Blue}}) + end) + it('works correctly with multiline prompts and :echohl', function() + command('hi Test ctermfg=Red guifg=Red term=bold') + feed([[:echohl Test | call input("Test\nFoo")]]) + screen:expect([[ + {1:~ }| + {1:~ }| + {1:~ }| + {2:Test} | + {2:Foo}^ | + ]], {{bold=true, foreground=Screen.colors.Blue}, {foreground=Screen.colors.Red}}) + end) +end) diff --git a/test/functional/eval/match_functions_spec.lua b/test/functional/eval/match_functions_spec.lua index f6bad59c66..3150d89f62 100644 --- a/test/functional/eval/match_functions_spec.lua +++ b/test/functional/eval/match_functions_spec.lua @@ -44,3 +44,18 @@ describe('setmatches()', function() eq({}, funcs.getmatches()) end) end) + +describe('matchadd()', function() + it('correctly works when first two arguments and conceal are numbers at once', + function() + command('hi def link 1 PreProc') + eq(4, funcs.matchadd(1, 2, 3, 4, {conceal=5})) + eq({{ + group='1', + pattern='2', + priority=3, + id=4, + conceal='5', + }}, funcs.getmatches()) + end) +end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 057245db1c..8cc717483e 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -9,7 +9,7 @@ local eval = helpers.eval describe('dictionary change notifications', function() local channel - setup(function() + before_each(function() clear() channel = nvim('get_api_info')[1] nvim('set_var', 'channel', channel) @@ -18,19 +18,15 @@ describe('dictionary change notifications', function() -- the same set of tests are applied to top-level dictionaries(g:, b:, w: and -- t:) and a dictionary variable, so we generate them in the following -- function. - local function gentests(dict_expr, dict_expr_suffix, dict_init) - if not dict_expr_suffix then - dict_expr_suffix = '' - end - + local function gentests(dict_expr, dict_init) local function update(opval, key) if not key then key = 'watched' end if opval == '' then - nvim('command', "unlet "..dict_expr..dict_expr_suffix..key) + command(('unlet %s[\'%s\']'):format(dict_expr, key)) else - nvim('command', "let "..dict_expr..dict_expr_suffix..key.." "..opval) + command(('let %s[\'%s\'] %s'):format(dict_expr, key, opval)) end end @@ -50,7 +46,7 @@ describe('dictionary change notifications', function() describe(dict_expr .. ' watcher', function() if dict_init then - setup(function() + before_each(function() source(dict_init) end) end @@ -143,6 +139,32 @@ describe('dictionary change notifications', function() ]]) end) + it('is triggered for empty keys', function() + command([[ + call dictwatcheradd(]]..dict_expr..[[, "", "g:Changed") + ]]) + update('= 1', '') + verify_value({new = 1}, '') + update('= 2', '') + verify_value({old = 1, new = 2}, '') + command([[ + call dictwatcherdel(]]..dict_expr..[[, "", "g:Changed") + ]]) + end) + + it('is triggered for empty keys when using catch-all *', function() + command([[ + call dictwatcheradd(]]..dict_expr..[[, "*", "g:Changed") + ]]) + update('= 1', '') + verify_value({new = 1}, '') + update('= 2', '') + verify_value({old = 1, new = 2}, '') + command([[ + call dictwatcherdel(]]..dict_expr..[[, "*", "g:Changed") + ]]) + end) + -- test a sequence of updates of different types to ensure proper memory -- management(with ASAN) local function test_updates(tests) @@ -190,10 +212,10 @@ describe('dictionary change notifications', function() gentests('b:') gentests('w:') gentests('t:') - gentests('g:dict_var', '.', 'let g:dict_var = {}') + gentests('g:dict_var', 'let g:dict_var = {}') describe('multiple watchers on the same dict/key', function() - setup(function() + before_each(function() source([[ function! g:Watcher1(dict, key, value) call rpcnotify(g:channel, '1', a:key, a:value) @@ -213,6 +235,9 @@ describe('dictionary change notifications', function() end) it('only removes watchers that fully match dict, key and callback', function() + nvim('command', 'let g:key = "value"') + eq({'notification', '1', {'key', {new = 'value'}}}, next_msg()) + eq({'notification', '2', {'key', {new = 'value'}}}, next_msg()) nvim('command', 'call dictwatcherdel(g:, "key", "g:Watcher1")') nvim('command', 'let g:key = "v2"') eq({'notification', '2', {'key', {old = 'value', new = 'v2'}}}, next_msg()) @@ -220,6 +245,17 @@ describe('dictionary change notifications', function() end) describe('errors', function() + before_each(function() + source([[ + function! g:Watcher1(dict, key, value) + call rpcnotify(g:channel, '1', a:key, a:value) + endfunction + function! g:Watcher2(dict, key, value) + call rpcnotify(g:channel, '2', a:key, a:value) + endfunction + ]]) + end) + -- WARNING: This suite depends on the above tests it('fails to remove if no watcher with matching callback is found', function() eq("Vim(call):Couldn't find a watcher matching key and callback", @@ -236,15 +272,10 @@ describe('dictionary change notifications', function() command('call dictwatcherdel(g:, "key", "g:InvalidCb")') end) - it('fails with empty keys', function() - eq("Vim(call):E713: Cannot use empty key for Dictionary", - exc_exec('call dictwatcheradd(g:, "", "g:Watcher1")')) - eq("Vim(call):E713: Cannot use empty key for Dictionary", - exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")')) - end) - it('does not fail to replace a watcher function', function() source([[ + let g:key = 'v2' + call dictwatcheradd(g:, "key", "g:Watcher2") function! g:ReplaceWatcher2() function! g:Watcher2(dict, key, value) call rpcnotify(g:channel, '2b', a:key, a:value) -- cgit From 5df35297f832b3247c18253c916be6066c603739 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 02:50:50 +0300 Subject: eval: Remove eval_expr() completely --- runtime/doc/if_pyth.txt | 5 ----- src/nvim/eval.c | 17 ----------------- test/unit/eval/tricks_spec.lua | 22 +++++++++++++--------- 3 files changed, 13 insertions(+), 31 deletions(-) diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index f2a7d91bb7..6321175420 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -181,11 +181,6 @@ vim.eval(str) *python-eval* # string.atoi() to convert to # a number. - :py tagList = vim.eval('taglist("eval_expr")') -< The latter will return a python list of python dicts, for instance: - [{'cmd': '/^eval_expr(arg, nextcmd)$/', 'static': 0, 'name': - 'eval_expr', 'kind': 'f', 'filename': './src/eval.c'}] - vim.bindeval(str) *python-bindeval* Like |python-eval|, but returns special objects described in |python-bindeval-objects|. These python objects let you modify (|List| diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3a0075e48a..51ffa8887d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1178,23 +1178,6 @@ int get_spellword(list_T *list, const char **pp) return tv_get_number(&li->li_tv); } -/* - * Top level evaluation function. - * Returns an allocated typval_T with the result. - * Returns NULL when there is an error. - */ -typval_T *eval_expr(char_u *arg, char_u **nextcmd) -{ - typval_T *tv = xmalloc(sizeof(typval_T)); - - if (eval0(arg, tv, nextcmd, TRUE) == FAIL) { - xfree(tv); - return NULL; - } - - return tv; -} - // Call some vimL function and return the result in "*rettv". // Uses argv[argc] for the function arguments. Only Number and String diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index 7f0a445f2c..54029734fb 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -6,13 +6,17 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq -local eval = cimport('./src/nvim/eval.h', './src/nvim/memory.h') +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', + './src/nvim/memory.h') -local eval_expr = function(expr) - return ffi.gc(eval.eval_expr(to_cstr(expr), nil), function(tv) - eval.tv_clear(tv) - eval.xfree(tv) - end) +local eval0 = function(expr) + local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), + eval.tv_clear) + if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then + return nil + else + return tv + end end describe('NULL typval_T', function() @@ -25,19 +29,19 @@ describe('NULL typval_T', function() while os.getenv(unexistent_env) ~= nil do unexistent_env = unexistent_env .. '_XXX' end - local rettv = eval_expr('$' .. unexistent_env) + local rettv = eval0('$' .. unexistent_env) eq(eval.VAR_STRING, rettv.v_type) eq(nil, rettv.vval.v_string) end) itp('is produced by v:_null_list', function() - local rettv = eval_expr('v:_null_list') + local rettv = eval0('v:_null_list') eq(eval.VAR_LIST, rettv.v_type) eq(nil, rettv.vval.v_list) end) itp('is produced by v:_null_dict', function() - local rettv = eval_expr('v:_null_dict') + local rettv = eval0('v:_null_dict') eq(eval.VAR_DICT, rettv.v_type) eq(nil, rettv.vval.v_dict) end) -- cgit From 31a3158d0b574385186ab55c074edf85356d1d6c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 03:46:49 +0300 Subject: eval: Make sort always stable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Should fix test failures on QB: 20:00:51,837 INFO - not ok 420 - sort() sorts “wrong” values between -0.0001 and 0.0001, preserving order 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua @ 21 20:00:51,837 INFO - # Failure message: test/functional/eval/sort_spec.lua:39: Expected objects to be the same. 20:00:51,837 INFO - # Passed in: 20:00:51,837 INFO - # (string) '[-1.0e-4, v:true, v:false, v:null, function('tr'), {'a': 42}, 'check', [], 1.0e-4]' 20:00:51,837 INFO - # Expected: 20:00:51,837 INFO - # (string) '[-1.0e-4, function('tr'), v:true, v:false, v:null, [], {'a': 42}, 'check', 1.0e-4]' 20:00:51,837 INFO - # stack traceback: 20:00:51,837 INFO - # test/functional/eval/sort_spec.lua:39: in function 20:00:51,837 INFO - # --- src/nvim/eval.c | 58 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 51ffa8887d..2dc1cb50af 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15022,58 +15022,61 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; - char_u *p1; - char_u *p2; - char_u *tofree1 = NULL; - char_u *tofree2 = NULL; - int res; + sortItem_T *const si1 = (sortItem_T *)s1; + sortItem_T *const si2 = (sortItem_T *)s2; - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - typval_T *tv1 = &si1->item->li_tv; - typval_T *tv2 = &si2->item->li_tv; + typval_T *const tv1 = &si1->item->li_tv; + typval_T *const tv2 = &si2->item->li_tv; + + int res; if (sortinfo->item_compare_numbers) { - const varnumber_T v1 = tv_get_number(tv1); - const varnumber_T v2 = tv_get_number(tv2); + const long v1 = tv_get_number(tv1); + const long v2 = tv_get_number(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } if (sortinfo->item_compare_float) { const float_T v1 = tv_get_float(tv1); const float_T v2 = tv_get_float(tv2); - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + res = v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + goto item_compare_end; } + char *tofree1 = NULL; + char *tofree2 = NULL; + char *p1; + char *p2; + // encode_tv2string() puts quotes around a string and allocates memory. Don't // do that for string variables. Use a single quote when comparing with // a non-string to do what the docs promise. if (tv1->v_type == VAR_STRING) { if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p1 = (char_u *)"'"; + p1 = "'"; } else { - p1 = tv1->vval.v_string; + p1 = (char *)tv1->vval.v_string; } } else { - tofree1 = p1 = (char_u *) encode_tv2string(tv1, NULL); + tofree1 = p1 = encode_tv2string(tv1, NULL); } if (tv2->v_type == VAR_STRING) { if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) { - p2 = (char_u *)"'"; + p2 = "'"; } else { - p2 = tv2->vval.v_string; + p2 = (char *)tv2->vval.v_string; } } else { - tofree2 = p2 = (char_u *) encode_tv2string(tv2, NULL); + tofree2 = p2 = encode_tv2string(tv2, NULL); } if (p1 == NULL) { - p1 = (char_u *)""; + p1 = ""; } if (p2 == NULL) { - p2 = (char_u *)""; + p2 = ""; } if (!sortinfo->item_compare_numeric) { if (sortinfo->item_compare_ic) { @@ -15083,19 +15086,20 @@ static int item_compare(const void *s1, const void *s2, bool keep_zero) } } else { double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); + n1 = strtod(p1, &p1); + n2 = strtod(p2, &p2); res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; } + xfree(tofree1); + xfree(tofree2); + +item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { res = si1->idx > si2->idx ? 1 : -1; } - - xfree(tofree1); - xfree(tofree2); return res; } -- cgit From 6cc3d59ec8e4d6f32c8c3d9755c625e32512b8e2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 04:29:56 +0300 Subject: misc1: Refactor ask_yesno() --- src/nvim/misc1.c | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index d751f13644..0b74b4437e 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2211,39 +2211,44 @@ change_warning ( } } -/* - * Ask for a reply from the user, a 'y' or a 'n'. - * No other characters are accepted, the message is repeated until a valid - * reply is entered or CTRL-C is hit. - * If direct is TRUE, don't use vgetc() but ui_inchar(), don't get characters - * from any buffers but directly from the user. - * - * return the 'y' or 'n' - */ -int ask_yesno(const char *str, bool direct) +/// Ask for a reply from the user, 'y' or 'n' +/// +/// No other characters are accepted, the message is repeated until a valid +/// reply is entered or is hit. +/// +/// @param[in] str Prompt: question to ask user. Is always followed by +/// " (y/n)?". +/// @param[in] direct Determines what function to use to get user input. If +/// true then ui_inchar() will be used, otherwise vgetc(). +/// I.e. when direct is true then characters are obtained +/// directly from the user without buffers involved. +/// +/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt. +int ask_yesno(const char *const str, const bool direct) { - int r = ' '; - int save_State = State; + const int save_State = State; no_wait_return++; - State = CONFIRM; // mouse behaves like with :confirm - setmouse(); // disables mouse for xterm + State = CONFIRM; // Mouse behaves like with :confirm. + setmouse(); // Disable mouse in xterm. no_mapping++; + int r = ' '; while (r != 'y' && r != 'n') { - /* same highlighting as for wait_return */ - smsg_attr(hl_attr(HLF_R), - "%s (y/n)?", str); - if (direct) + // Same highlighting as for wait_return. + smsg_attr(hl_attr(HLF_R), "%s (y/n)?", str); + if (direct) { r = get_keystroke(); - else + } else { r = plain_vgetc(); - if (r == Ctrl_C || r == ESC) + } + if (r == Ctrl_C || r == ESC) { r = 'n'; - msg_putchar(r); /* show what you typed */ + } + msg_putchar(r); // Show what you typed. ui_flush(); } - --no_wait_return; + no_wait_return--; State = save_State; setmouse(); no_mapping--; -- cgit From c4fe656fef5ade20da4861a2f1e5d0f776b0df8f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 17:56:24 +0300 Subject: typval.h: Allow non-var expressions in TV_DICT_ITER first argument --- src/nvim/eval.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2dc1cb50af..a5b0a65497 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -11248,26 +11248,26 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ -static void dict_list(typval_T *argvars, typval_T *rettv, int what) -{ - if (argvars[0].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); +/// Turn a dictionary into a list +/// +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. +static void dict_list(typval_T *const tv, typval_T *const rettv, + const DictListType what) +{ + if (tv->v_type != VAR_DICT) { + emsgf(_(e_dictreq)); return; } - dict_T *const d = argvars[0].vval.v_dict; - if (d == NULL) { + if (tv->vval.v_dict == NULL) { return; } tv_list_alloc_ret(rettv); - TV_DICT_ITER(d, di, { + TV_DICT_ITER(tv->vval.v_dict, di, { listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); -- cgit From 86fc4580b83f20211d7f85566e84a0e1dad0a136 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 18:40:19 +0300 Subject: eval: Fix max_min functions Found two bugs: 1. Multiple unneeded error messages, vim/vim#1039. 2. Unformatted error string, vim/vim#1040. --- src/nvim/eval.c | 76 ++++++++++++-------------- src/nvim/eval/typval.h | 4 +- test/functional/eval/minmax_functions_spec.lua | 51 +++++++++++++++++ 3 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 test/functional/eval/minmax_functions_spec.lua diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a5b0a65497..c96abfa149 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12463,53 +12463,49 @@ static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, 4); } -static void max_min(typval_T *argvars, typval_T *rettv, int domax) +/// Get maximal/minimal number value in a list or dictionary +/// +/// @param[in] tv List or dictionary to work with. If it contains something +/// that is not an integer number (or cannot be coerced to +/// it) error is given. +/// @param[out] rettv Location where result will be saved. Only assigns +/// vval.v_number, type is not touched. Returns zero for +/// empty lists/dictionaries. +/// @param[in] domax Determines whether maximal or minimal value is desired. +static void max_min(const typval_T *const tv, typval_T *const rettv, + const bool domax) + FUNC_ATTR_NONNULL_ALL { - long n = 0; - long i; + varnumber_T n = 0; bool error = false; - if (argvars[0].v_type == VAR_LIST) { - list_T *l; - listitem_T *li; - - l = argvars[0].vval.v_list; - if (l != NULL) { - li = l->lv_first; - if (li != NULL) { - n = tv_get_number_chk(&li->li_tv, &error); - for (;; ) { - li = li->li_next; - if (li == NULL) { - break; - } - i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } + if (tv->v_type == VAR_LIST) { + const list_T *const l = tv->vval.v_list; + if (tv_list_len(l) != 0) { + n = tv_get_number_chk(&l->lv_first->li_tv, &error); + for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; + li = li->li_next) { + const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); + if (domax ? i > n : i < n) { + n = i; } } } - } else if (argvars[0].v_type == VAR_DICT) { - dict_T *d; - int first = TRUE; - hashitem_T *hi; - int todo; - - d = argvars[0].vval.v_dict; - if (d != NULL) { - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { - if (!HASHITEM_EMPTY(hi)) { - todo--; - i = tv_get_number_chk(&TV_DICT_HI2DI(hi)->di_tv, &error); - if (first) { - n = i; - first = FALSE; - } else if (domax ? i > n : i < n) - n = i; + } else if (tv->v_type == VAR_DICT) { + if (tv->vval.v_dict != NULL) { + bool first = true; + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + break; } - } + if (first) { + n = i; + first = true; + } else if (domax ? i > n : i < n) { + n = i; + } + }); } } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 42581939e3..3b41314b46 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -279,13 +279,13 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(list_T *const l) +static inline long tv_list_len(const list_T *const l) { if (l == NULL) { return 0; diff --git a/test/functional/eval/minmax_functions_spec.lua b/test/functional/eval/minmax_functions_spec.lua new file mode 100644 index 0000000000..c6eb754f91 --- /dev/null +++ b/test/functional/eval/minmax_functions_spec.lua @@ -0,0 +1,51 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local clear = helpers.clear +local funcs = helpers.funcs +local redir_exec = helpers.redir_exec + +before_each(clear) +for _, func in ipairs({'min', 'max'}) do + describe(func .. '()', function() + it('gives a single error message when multiple values failed conversions', + function() + eq('\nE745: Using a List as a Number\n0', + redir_exec('echo ' .. func .. '([-5, [], [], [], 5])')) + eq('\nE745: Using a List as a Number\n0', + redir_exec('echo ' .. func .. '({1:-5, 2:[], 3:[], 4:[], 5:5})')) + for errmsg, errinput in pairs({ + ['E745: Using a List as a Number'] = '[]', + ['E805: Using a Float as a Number'] = '0.0', + ['E703: Using a Funcref as a Number'] = 'function("tr")', + ['E728: Using a Dictionary as a Number'] = '{}', + }) do + eq('\n' .. errmsg .. '\n0', + redir_exec('echo ' .. func .. '([' .. errinput .. '])')) + eq('\n' .. errmsg .. '\n0', + redir_exec('echo ' .. func .. '({1:' .. errinput .. '})')) + end + end) + it('works with arrays/dictionaries with zero items', function() + eq(0, funcs[func]({})) + eq(0, eval(func .. '({})')) + end) + it('works with arrays/dictionaries with one item', function() + eq(5, funcs[func]({5})) + eq(5, funcs[func]({test=5})) + end) + it('works with NULL arrays/dictionaries', function() + eq(0, eval(func .. '(v:_null_list)')) + eq(0, eval(func .. '(v:_null_dict)')) + end) + it('errors out for invalid types', function() + for _, errinput in ipairs({'1', 'v:true', 'v:false', 'v:null', + 'function("tr")', '""'}) do + eq(('\nE712: Argument of %s() must be a List or Dictionary\n0'):format( + func), + redir_exec('echo ' .. func .. '(' .. errinput .. ')')) + end + end) + end) +end -- cgit From 3a3816c990362e1ad8321e6b4748a33c94d1d797 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Sep 2016 21:42:24 +0300 Subject: cmake: Use CMAKE_CURRENT_LIST_DIR and remove vars used only once --- src/nvim/CMakeLists.txt | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index e752f5d4df..1c2932ed50 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -22,7 +22,6 @@ set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.genera set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generated.h) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) -set(GENERATED_FUNCS_HASH_INPUT ${GENERATED_DIR}/funcs.generated.h.gperf) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) @@ -31,10 +30,6 @@ set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) set(FUNCS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/geneval.lua) set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) -set(EVENTS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua) -set(EX_CMDS_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua) -set(EVAL_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/eval.lua) -set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) @@ -112,7 +107,7 @@ set(CONV_SOURCES window.c) foreach(sfile ${CONV_SOURCES}) - if(NOT EXISTS "${PROJECT_SOURCE_DIR}/src/nvim/${sfile}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() @@ -167,11 +162,11 @@ endfunction() # NVIM_GENERATED_SOURCES: generated source files # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} - "${PROJECT_SOURCE_DIR}/src/nvim/regexp_nfa.c" + "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" ${GENERATED_API_DISPATCH}) get_filename_component(full_d ${sfile} PATH) - file(RELATIVE_PATH d "${PROJECT_SOURCE_DIR}/src/nvim" "${full_d}") - if(${d} MATCHES "^([.][.]|auto/)") + file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") + if(${d} MATCHES "^[.][.]|auto/") file(RELATIVE_PATH d "${GENERATED_DIR}" "${full_d}") endif() get_filename_component(f ${sfile} NAME) @@ -239,8 +234,8 @@ list(APPEND NVIM_GENERATED_SOURCES add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} COMMAND ${LUA_PRG} ${EX_CMDS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} - DEPENDS ${EX_CMDS_GENERATOR} ${EX_CMDS_DEFS_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_INCLUDES_DIR} ${GENERATED_DIR} + DEPENDS ${EX_CMDS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/ex_cmds.lua ) if(NOT GPERF_PRG) @@ -248,24 +243,24 @@ if(NOT GPERF_PRG) endif() add_custom_command(OUTPUT ${GENERATED_FUNCS} ${FUNCS_DATA} COMMAND ${LUA_PRG} ${FUNCS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_DIR} ${API_METADATA} ${FUNCS_DATA} COMMAND ${GPERF_PRG} - ${GENERATED_FUNCS_HASH_INPUT} --output-file=${GENERATED_FUNCS} - DEPENDS ${FUNCS_GENERATOR} ${EVAL_DEFS_FILE} ${API_METADATA} + ${GENERATED_DIR}/funcs.generated.h.gperf --output-file=${GENERATED_FUNCS} + DEPENDS ${FUNCS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/eval.lua ${API_METADATA} ) list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_FUNCS}") add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} COMMAND ${LUA_PRG} ${EVENTS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} - DEPENDS ${EVENTS_GENERATOR} ${EVENTS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) add_custom_command(OUTPUT ${GENERATED_OPTIONS} COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} - ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_OPTIONS} - DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} + ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} + DEPENDS ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua ) # NVIM_GENERATED_FOR_SOURCES and NVIM_GENERATED_FOR_HEADERS must be mutually exclusive. -- cgit From 40feac6efcc75395bb20da2b028b67a2b6e95bd9 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:32:14 +0300 Subject: message: Revise maxlen argument in msg_puts_attr_len `attr` argument is enough to forbid putting message in history. Also forbid strings with NUL before `len` just in case (it appears that this does not ever happen). --- src/nvim/message.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 423f5a27e7..1eab9a403b 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1562,13 +1562,17 @@ void msg_puts_attr(const char *const s, const int attr) msg_puts_attr_len(s, -1, attr); } -/// Like msg_puts_attr(), but with a maximum length "maxlen" (in bytes). -/// When "maxlen" is -1 there is no maximum length. -/// When "maxlen" is >= 0 the message is not put in the history. -void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) +/// Write a message with highlight attributes +/// +/// @param[in] str NUL-terminated message string. +/// @param[in] len Length of the string or -1. +/// @param[in] attr Highlight attribute. +void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) + FUNC_ATTR_NONNULL_ALL { + assert(len < 0 || memchr(str, 0, len) == NULL); // If redirection is on, also write to the redirection file. - redir_write(str, maxlen); + redir_write(str, len); // Don't print anything when using ":silent cmd". if (msg_silent != 0) { @@ -1576,8 +1580,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) } // if MSG_HIST flag set, add message to history - if ((attr & MSG_HIST) && maxlen < 0) { - add_msg_hist(str, -1, attr); + if (attr & MSG_HIST) { + assert(len < 0); + add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } @@ -1596,9 +1601,9 @@ void msg_puts_attr_len(const char *str, const ptrdiff_t maxlen, int attr) // different, e.g. for Win32 console) or we just don't know where the // cursor is. if (msg_use_printf()) { - msg_puts_printf(str, maxlen); + msg_puts_printf(str, len); } else { - msg_puts_display((const char_u *)str, maxlen, attr, false); + msg_puts_display((const char_u *)str, len, attr, false); } } -- cgit From 8b0fa64ed3dbd4376cbfa195526ec97d8c944a97 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 10 Sep 2016 22:35:47 +0300 Subject: message: Remove incorrect assertion It was there only to prove that *now* `len` argument is not used to forbid putting message into history (i.e. that Neovim behaviour did not change). After this commit it should be possible to use msg_puts_attr_len with len>=0 to put message into history should new code need this. --- src/nvim/message.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nvim/message.c b/src/nvim/message.c index 1eab9a403b..83f2735b50 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1581,7 +1581,6 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) // if MSG_HIST flag set, add message to history if (attr & MSG_HIST) { - assert(len < 0); add_msg_hist(str, (int)len, attr); attr &= ~MSG_HIST; } -- cgit From 2ad4fba46db26d0724106b194f7cb628fb9b02e8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:37:03 +0300 Subject: eval: Move copy_tv to eval/typval --- src/nvim/api/private/helpers.c | 2 +- src/nvim/eval.c | 215 +++++++++++++++++------------------------ src/nvim/eval/typval.c | 86 ++++++++++++++--- src/nvim/move.PVS-Studio.cfg | 10 ++ src/nvim/shada.c | 2 +- test/unit/eval/helpers.lua | 4 +- 6 files changed, 178 insertions(+), 141 deletions(-) create mode 100644 src/nvim/move.PVS-Studio.cfg diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2c8a56cc1a..b245132ba7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -179,7 +179,7 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del, } // Update the value - copy_tv(&tv, &di->di_tv); + tv_copy(&tv, &di->di_tv); // Clear the temporary variable tv_clear(&tv); } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c96abfa149..a2b6d12288 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -494,6 +494,13 @@ typedef enum ASSERT_OTHER, } assert_type_T; +/// Type for dict_list function +typedef enum { + kDictListKeys, ///< List dictionary keys. + kDictListValues, ///< List dictionary values. + kDictListItems, ///< List dictionary contents: [keys, values]. +} DictListType; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.c.generated.h" #endif @@ -2412,7 +2419,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); } else { tv_clear(&lp->ll_li->li_tv); - copy_tv(&ri->li_tv, &lp->ll_li->li_tv); + tv_copy(&ri->li_tv, &lp->ll_li->li_tv); } ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) @@ -2452,7 +2459,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, lp->ll_tv = &di->di_tv; } else { if (watched) { - copy_tv(lp->ll_tv, &oldtv); + tv_copy(lp->ll_tv, &oldtv); } if (op != NULL && *op != '=') { @@ -2465,7 +2472,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, // Assign the value to the variable or list item. if (copy) { - copy_tv(rettv, lp->ll_tv); + tv_copy(rettv, lp->ll_tv); } else { *lp->ll_tv = *rettv; lp->ll_tv->v_lock = 0; @@ -2926,7 +2933,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) typval_T oldtv; if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); // need to save key because dictitem_remove will free it key = xstrdup((char *)di->di_key); } @@ -2998,7 +3005,7 @@ int do_unlet(char_u *name, int forceit) bool watched = tv_dict_is_watched(dict); if (watched) { - copy_tv(&di->di_tv, &oldtv); + tv_copy(&di->di_tv, &oldtv); } delete_var(ht, hi); @@ -4532,7 +4539,7 @@ eval_index ( rettv->vval.v_list = l; l->lv_refcount++; } else { - copy_tv(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); tv_clear(rettv); *rettv = var1; } @@ -4570,7 +4577,7 @@ eval_index ( return FAIL; } - copy_tv(&item->di_tv, &var1); + tv_copy(&item->di_tv, &var1); tv_clear(rettv); *rettv = var1; break; @@ -6298,7 +6305,7 @@ call_func( } if (error == ERROR_NONE && partial->pt_argc > 0) { for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) { - copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]); + tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]); } for (int i = 0; i < argcount_in; i++) { argv[i + argv_clear] = argvars_in[i]; @@ -6552,7 +6559,7 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, arg_errmsg, arg_errmsg_len)) { tv_list_append_tv(l, &argvars[1]); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else EMSG(_(e_listreq)); @@ -7231,7 +7238,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, /* Make a copy of each argument. This is needed to be able to set * v_lock to VAR_FIXED in the copy without changing the original list. */ - copy_tv(&item->li_tv, &argv[argc++]); + tv_copy(&item->li_tv, &argv[argc++]); } if (item == NULL) { @@ -8190,7 +8197,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) item = NULL; tv_list_extend(l1, l2, item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { @@ -8224,7 +8231,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_extend(d1, d2, action); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } else { EMSG2(_(e_listdictarg), "extend()"); @@ -8441,7 +8448,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8451,7 +8458,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) int retval = FAIL; int dummy; - copy_tv(tv, &vimvars[VV_VAL].vv_tv); + tv_copy(tv, &vimvars[VV_VAL].vv_tv); argv[0] = vimvars[VV_KEY].vv_tv; argv[1] = vimvars[VV_VAL].vv_tv; if (expr->v_type == VAR_FUNC) { @@ -8859,13 +8866,13 @@ static void common_function(typval_T *argvars, typval_T *rettv, } int i = 0; for (; i < arg_len; i++) { - copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &pt->pt_argv[i++]); + tv_copy(&li->li_tv, &pt->pt_argv[i++]); } } } @@ -9012,10 +9019,12 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (tv == NULL) { - if (argvars[2].v_type != VAR_UNKNOWN) - copy_tv(&argvars[2], rettv); - } else - copy_tv(tv, rettv); + if (argvars[2].v_type != VAR_UNKNOWN) { + tv_copy(&argvars[2], rettv); + } + } else { + tv_copy(tv, rettv); + } } /// Returns information about signs placed in a buffer as list of dicts. @@ -9260,7 +9269,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *const v = find_var_in_ht(&curbuf->b_vars->dv_hashtab, 'b', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -9273,7 +9282,7 @@ static void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) f_getbufvar_end: if (!done && argvars[2].v_type != VAR_UNKNOWN) { // use the default value - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10094,7 +10103,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) v = find_var_in_ht(&tp->tp_vars->dv_hashtab, 't', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10104,7 +10113,7 @@ static void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (!done && argvars[2].v_type != VAR_UNKNOWN) { - copy_tv(&argvars[2], rettv); + tv_copy(&argvars[2], rettv); } } @@ -10315,7 +10324,7 @@ getwinvar ( v = find_var_in_ht(&win->w_vars->dv_hashtab, 'w', varname, strlen(varname), false); if (v != NULL) { - copy_tv(&v->di_tv, rettv); + tv_copy(&v->di_tv, rettv); done = true; } } @@ -10330,7 +10339,7 @@ getwinvar ( if (!done && argvars[off + 2].v_type != VAR_UNKNOWN) { // use the default return value - copy_tv(&argvars[off + 2], rettv); + tv_copy(&argvars[off + 2], rettv); } } @@ -11181,7 +11190,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (l != NULL) { tv_list_insert_tv(l, &argvars[1], item); - copy_tv(&argvars[0], rettv); + tv_copy(&argvars[0], rettv); } } } @@ -11271,31 +11280,36 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, listitem_T *const li = tv_list_item_alloc(); tv_list_append(rettv->vval.v_list, li); - if (what == 0) { - // keys() - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } else if (what == 1) { - // values() - copy_tv(&di->di_tv, &li->li_tv); - } else { - // items() - list_T *const l2 = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - l2->lv_refcount++; - - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = 0; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - - sub_li = tv_list_item_alloc(); - tv_list_append(l2, sub_li); - copy_tv(&di->di_tv, &sub_li->li_tv); + switch (what) { + case kDictListKeys: { + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + break; + } + case kDictListValues: { + tv_copy(&di->di_tv, &li->li_tv); + break; + } + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = VAR_UNLOCKED; + li->li_tv.vval.v_list = sub_l; + sub_l->lv_refcount++; + + listitem_T *sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + sub_li->li_tv.v_type = VAR_STRING; + sub_li->li_tv.v_lock = VAR_UNLOCKED; + sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); + + sub_li = tv_list_item_alloc(); + tv_list_append(sub_l, sub_li); + tv_copy(&di->di_tv, &sub_li->li_tv); + break; + } } }); } @@ -12256,21 +12270,24 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } } } else if (type == 2) { - /* return matched string */ - if (l != NULL) - copy_tv(&li->li_tv, rettv); - else - rettv->vval.v_string = vim_strnsave(regmatch.startp[0], - (int)(regmatch.endp[0] - regmatch.startp[0])); - } else if (l != NULL) + // Return matched string. + if (l != NULL) { + tv_copy(&li->li_tv, rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + } else if (l != NULL) { rettv->vval.v_number = idx; - else { - if (type != 0) + } else { + if (type != 0) { rettv->vval.v_number = (varnumber_T)(regmatch.startp[0] - str); - else + } else { rettv->vval.v_number = (varnumber_T)(regmatch.endp[0] - str); + } rettv->vval.v_number += (varnumber_T)(str - expr); } } @@ -15135,8 +15152,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); + tv_copy(&si1->item->li_tv, &argv[0]); + tv_copy(&si2->item->li_tv, &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -18203,7 +18220,7 @@ static int get_var_tv( } ret = FAIL; } else if (rettv != NULL) { - copy_tv(tv, rettv); + tv_copy(tv, rettv); } return ret; @@ -18375,7 +18392,7 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) } else { pt->pt_argc = ret_pt->pt_argc; for (i = 0; i < pt->pt_argc; i++) { - copy_tv(&ret_pt->pt_argv[i], &pt->pt_argv[i]); + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } } } @@ -18852,7 +18869,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (watched) { - copy_tv(&v->di_tv, &oldtv); + tv_copy(&v->di_tv, &oldtv); } tv_clear(&v->di_tv); } else { // Add a new variable. @@ -18877,7 +18894,7 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) { - copy_tv(tv, &v->di_tv); + tv_copy(tv, &v->di_tv); } else { v->di_tv = *tv; v->di_tv.v_lock = 0; @@ -18992,56 +19009,6 @@ bool valid_varname(const char *varname) return true; } -/* - * Copy the values from typval_T "from" to typval_T "to". - * When needed allocates string or increases reference count. - * Does not make a copy of a list or dict but copies the reference! - * It is OK for "from" and "to" to point to the same item. This is used to - * make a copy later. - */ -void copy_tv(typval_T *from, typval_T *to) -{ - to->v_type = from->v_type; - to->v_lock = 0; - memmove(&to->vval, &from->vval, sizeof(to->vval)); - switch (from->v_type) { - case VAR_NUMBER: - case VAR_FLOAT: - case VAR_SPECIAL: - break; - case VAR_STRING: - case VAR_FUNC: - if (from->vval.v_string != NULL) { - to->vval.v_string = vim_strsave(from->vval.v_string); - if (from->v_type == VAR_FUNC) { - func_ref(to->vval.v_string); - } - } - break; - case VAR_PARTIAL: - if (from->vval.v_partial == NULL) { - to->vval.v_partial = NULL; - } else { - to->vval.v_partial = from->vval.v_partial; - (to->vval.v_partial->pt_refcount)++; - } - break; - case VAR_LIST: - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } - break; - case VAR_DICT: - if (from->vval.v_dict != NULL) { - to->vval.v_dict->dv_refcount++; - } - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "copy_tv(UNKNOWN)"); - break; - } -} - /// Make a copy of an item /// /// Lists and Dictionaries are also copied. @@ -19080,11 +19047,11 @@ int var_item_copy(const vimconv_T *const conv, case VAR_FUNC: case VAR_PARTIAL: case VAR_SPECIAL: - copy_tv(from, to); + tv_copy(from, to); break; case VAR_STRING: if (conv == NULL || conv->vc_type == CONV_NONE) { - copy_tv(from, to); + tv_copy(from, to); } else { to->v_type = VAR_STRING; to->v_lock = 0; @@ -20981,7 +20948,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (addlocal) { // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. - copy_tv(&v->di_tv, &v->di_tv); + tv_copy(&v->di_tv, &v->di_tv); tv_dict_add(&fc->l_vars, v); } else { tv_dict_add(&fc->l_avars, v); @@ -21188,13 +21155,13 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // Make a copy of the a: variables, since we didn't do that above. TV_DICT_ITER(&fc->l_avars, di, { - copy_tv(&di->di_tv, &di->di_tv); + tv_copy(&di->di_tv, &di->di_tv); }); // Make a copy of the a:000 items, since we didn't do that above. for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - copy_tv(&li->li_tv, &li->li_tv); + tv_copy(&li->li_tv, &li->li_tv); } } @@ -21700,7 +21667,7 @@ const void *var_shada_iter(const void *const iter, const char **const name, hi = (const hashitem_T *) iter; } *name = (char *)TV_DICT_HI2DI(hi)->di_key; - copy_tv(&TV_DICT_HI2DI(hi)->di_tv, rettv); + tv_copy(&TV_DICT_HI2DI(hi)->di_tv, rettv); while ((size_t)(++hi - hifirst) < hinum) { if (!HASHITEM_EMPTY(hi) && var_flavour(hi->hi_key) == VAR_FLAVOUR_SHADA) { return hi; diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 78eca15fec..8a407a4513 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1,6 +1,8 @@ +#include #include -#include +#include #include +#include #include "nvim/lib/queue.h" #include "nvim/eval/typval.h" @@ -276,7 +278,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, /// Insert VimL value into a list /// /// @param[out] l List to insert to. -/// @param[in,out] tv Value to insert. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an /// allocated listitem_T and inserted. /// @param[in] item Item to insert before. If NULL, inserts at the end of the /// list. @@ -285,7 +287,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, { listitem_T *const ni = tv_list_item_alloc(); - copy_tv(tv, &ni->li_tv); + tv_copy(tv, &ni->li_tv); tv_list_insert(l, ni, item); } @@ -313,13 +315,13 @@ void tv_list_append(list_T *const l, listitem_T *const item) /// Append VimL value to the end of list /// /// @param[out] l List to append to. -/// @param[in,out] tv Value to append. Is copied (@see copy_tv()) to an +/// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an /// allocated listitem_T. void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - copy_tv(tv, &li->li_tv); + tv_copy(tv, &li->li_tv); tv_list_append(l, li); } @@ -447,7 +449,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, break; } } else { - copy_tv(&item->li_tv, &ni->li_tv); + tv_copy(&item->li_tv, &ni->li_tv); } tv_list_append(copy, ni); } @@ -828,13 +830,13 @@ void tv_dict_watcher_notify(dict_T *const dict, const char *const key, if (newtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new")); - copy_tv(newtv, &v->di_tv); + tv_copy(newtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } if (oldtv) { dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old")); - copy_tv(oldtv, &v->di_tv); + tv_copy(oldtv, &v->di_tv); tv_dict_add(argv[2].vval.v_dict, v); } @@ -926,7 +928,7 @@ static dictitem_T *tv_dict_item_copy(dictitem_T *const di) FUNC_ATTR_MALLOC { dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); return new_di; } @@ -1161,7 +1163,7 @@ bool tv_dict_get_callback(dict_T *const d, } typval_T tv; - copy_tv(&di->di_tv, &tv); + tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); bool res = callback_from_typval(result, &tv); tv_clear(&tv); @@ -1338,11 +1340,11 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, } if (watched) { - copy_tv(&di1->di_tv, &oldtv); + tv_copy(&di1->di_tv, &oldtv); } tv_clear(&di1->di_tv); - copy_tv(&di2->di_tv, &di1->di_tv); + tv_copy(&di2->di_tv, &di1->di_tv); if (watched) { tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, @@ -1433,7 +1435,7 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, break; } } else { - copy_tv(&di->di_tv, &new_di->di_tv); + tv_copy(&di->di_tv, &new_di->di_tv); } if (tv_dict_add(copy, new_di) == FAIL) { tv_dict_item_free(new_di); @@ -1782,6 +1784,64 @@ void tv_free(typval_T *tv) } } +//{{{3 Copy + +/// Copy typval from one location to another +/// +/// When needed allocates string or increases reference count. Does not make +/// a copy of a container, but copies its reference! +/// +/// It is OK for `from` and `to` to point to the same location; this is used to +/// make a copy later. +/// +/// @param[in] from Location to copy from. +/// @param[out] to Location to copy to. +void tv_copy(typval_T *const from, typval_T *const to) +{ + to->v_type = from->v_type; + to->v_lock = VAR_UNLOCKED; + memmove(&to->vval, &from->vval, sizeof(to->vval)); + switch (from->v_type) { + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: { + break; + } + case VAR_STRING: + case VAR_FUNC: { + if (from->vval.v_string != NULL) { + to->vval.v_string = vim_strsave(from->vval.v_string); + if (from->v_type == VAR_FUNC) { + func_ref(to->vval.v_string); + } + } + break; + } + case VAR_PARTIAL: { + if (to->vval.v_partial != NULL) { + to->vval.v_partial->pt_refcount++; + } + break; + } + case VAR_LIST: { + if (from->vval.v_list != NULL) { + to->vval.v_list->lv_refcount++; + } + break; + } + case VAR_DICT: { + if (from->vval.v_dict != NULL) { + to->vval.v_dict->dv_refcount++; + } + break; + } + case VAR_UNKNOWN: { + EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + break; + } + } +} + //{{{2 Locks /// Lock or unlock an item diff --git a/src/nvim/move.PVS-Studio.cfg b/src/nvim/move.PVS-Studio.cfg new file mode 100644 index 0000000000..cb6da32f12 --- /dev/null +++ b/src/nvim/move.PVS-Studio.cfg @@ -0,0 +1,10 @@ + +source-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.c +i-file=/home/zyx/a.a/Proj/c/neovim/src/nvim/move.i +language=C +skip-cl-exe=yes +preprocessor=gcc +platform=linux64 +lic-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.lic +output-file=/home/zyx/a.a/Proj/c/neovim/build/../PVS-Studio.log.x +analysis-mode=4 diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 0f04a5e9cf..f65fdaf1c0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -2559,7 +2559,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (sd_writer->sd_conv.vc_type != CONV_NONE) { var_item_copy(&sd_writer->sd_conv, &vartv, &tgttv, true, 0); } else { - copy_tv(&vartv, &tgttv); + tv_copy(&vartv, &tgttv); } ShaDaWriteResult spe_ret; if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) { diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index be77ef4c83..c603953a05 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -280,7 +280,7 @@ local lua2typvalt_type_tab = { if type(k) == 'string' then local di = eval.tv_dict_item_alloc(to_cstr(k)) local val_tv = ffi.gc(lua2typvalt(v, processed), nil) - eval.copy_tv(val_tv, di.di_tv) + eval.tv_copy(val_tv, di.di_tv) eval.tv_clear(val_tv) eval.tv_dict_add(dct, di) end @@ -300,7 +300,7 @@ local lua2typvalt_type_tab = { argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil) for i, arg in ipairs(l.args) do local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) - eval.copy_tv(arg_tv, argv[i - 1]) + eval.tv_copy(arg_tv, argv[i - 1]) eval.tv_clear(arg_tv) end end -- cgit From a32db8ed19c35847373d4a7fd56d7797a5a26897 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 11 Sep 2016 03:54:13 +0300 Subject: eval/typval: Add missing includes, also add a script to find them Contains unfinished attempt to integrate IWYU (ref #549). To finish it different job should be done, specifically: - Instead of feeding IWYU with modified file a mirror source tree should be created with the help of CMake which will contain modified sources. This solves the problem with IWYU thinking that `*.generated.h` headers should be included in place of `*` headers. - Build IWYU as all other third-party utilities. - Make modified sources avoid problems with `nvim/func_attr.h` includes and various related tricks. Current script may only be used for manual checks like this: ./scripts/check-includes.py \ --generated-includes-dir build/include \ --generated-includes-dir build/src/nvim/auto \ --file src/nvim/eval/typval.c \ -- -Isrc -Ibuild/include -Ibuild/src/nvim/auto \ -DINCLUDE_GENERATED_DECLARATIONS (it is also somewhat fine with `--file src/nvim/eval/typval.h`). I have no idea why (I mean, why developer think that these lines are needed, why they are suggested is pretty obvious: because there is typedef which mentions them before structs are defined), but for typval.h it reports, among other things, that it should add lines struct dictvar_S; struct listitem_S; struct listvar_S; struct listwatch_S; --- scripts/check-includes.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval/typval.c | 5 ++++ src/nvim/eval/typval.h | 4 ++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100755 scripts/check-includes.py diff --git a/scripts/check-includes.py b/scripts/check-includes.py new file mode 100755 index 0000000000..21308a21aa --- /dev/null +++ b/scripts/check-includes.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import sys +import re +import os + +from subprocess import Popen, PIPE +from argparse import ArgumentParser + + +GENERATED_INCLUDE_RE = re.compile( + r'^\s*#\s*include\s*"([/a-z_0-9.]+\.generated\.h)"(\s+//.*)?$') + + +def main(argv): + argparser = ArgumentParser() + argparser.add_argument('--generated-includes-dir', action='append', + help='Directory where generated includes are located.') + argparser.add_argument('--file', type=open, help='File to check.') + argparser.add_argument('iwyu_args', nargs='*', + help='IWYU arguments, must go after --.') + args = argparser.parse_args(argv) + + with args.file: + include_dirs = [] + + iwyu = Popen(['include-what-you-use', '-xc'] + args.iwyu_args + ['/dev/stdin'], + stdin=PIPE, stdout=PIPE, stderr=PIPE) + + for line in args.file: + match = GENERATED_INCLUDE_RE.match(line) + if match: + for d in args.generated_includes_dir: + try: + f = open(os.path.join(d, match.group(1))) + except IOError: + continue + else: + with f: + for generated_line in f: + iwyu.stdin.write(generated_line) + break + else: + raise IOError('Failed to find {0}'.format(match.group(1))) + else: + iwyu.stdin.write(line) + + iwyu.stdin.close() + + out = iwyu.stdout.read() + err = iwyu.stderr.read() + + ret = iwyu.wait() + + if ret != 2: + print('IWYU failed with exit code {0}:'.format(ret)) + print('{0} stdout {0}'.format('=' * ((80 - len(' stdout ')) // 2))) + print(out) + print('{0} stderr {0}'.format('=' * ((80 - len(' stderr ')) // 2))) + print(err) + return 1 + return 0 + + +if __name__ == '__main__': + raise SystemExit(main(sys.argv[1:])) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8a407a4513..4777e00a76 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -20,6 +20,11 @@ #include "nvim/ascii.h" #include "nvim/pos.h" #include "nvim/charset.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/message.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3b41314b46..791b191009 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -3,9 +3,11 @@ #include #include -#include +#include #include +#include +#include "nvim/types.h" #include "nvim/hashtab.h" #include "nvim/garray.h" #include "nvim/mbyte.h" -- cgit From a394167177390a66c275d32e3862c82c546970ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 13:41:11 +0300 Subject: unittests: Test tv_list_item_\* functions To check that memory is free()d correctly. --- src/nvim/eval/typval.c | 1 + test/unit/eval/helpers.lua | 11 ++- test/unit/eval/typval_spec.lua | 210 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 test/unit/eval/typval_spec.lua diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4777e00a76..a26afb20c6 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -228,6 +228,7 @@ void tv_list_unref(list_T *const l) /// @param[in] item2 Last item to remove. void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL { // notify watchers for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index c603953a05..8bf06e61f1 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -12,6 +12,7 @@ local null_string = {[true]='NULL string'} local null_list = {[true]='NULL list'} local null_dict = {[true]='NULL dict'} local type_key = {[true]='type key'} +local locks_key = {[true]='locks key'} local list_type = {[true]='list type'} local dict_type = {[true]='dict type'} local func_type = {[true]='func type'} @@ -23,9 +24,9 @@ local nil_value = {[true]='nil'} local lua2typvalt local function li_alloc(nogc) - local gcfunc = eval.listitem_free + local gcfunc = eval.tv_list_item_free if nogc then gcfunc = nil end - local li = ffi.gc(eval.listitem_alloc(), gcfunc) + local li = ffi.gc(eval.tv_list_item_alloc(), gcfunc) li.li_next = nil li.li_prev = nil li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} @@ -373,8 +374,9 @@ lua2typvalt = function(l, processed) end end +local void_ptr = ffi.typeof('void *') local function void(ptr) - return ffi.cast('void*', ptr) + return ffi.cast(void_ptr, ptr) end local alloc_logging_helpers = { @@ -386,7 +388,7 @@ local alloc_logging_helpers = { end, str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end, - freed = function(p) return {func='free', args={p and void(p)}} end, + freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } return { @@ -402,6 +404,7 @@ return { nil_value=nil_value, type_key=type_key, + locks_key=locks_key, list=list, lst2tbl=lst2tbl, diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua new file mode 100644 index 0000000000..f2cb593cf5 --- /dev/null +++ b/test/unit/eval/typval_spec.lua @@ -0,0 +1,210 @@ +local helpers = require('test.unit.helpers')(after_each) +local eval_helpers = require('test.unit.eval.helpers') + +local itp = helpers.gen_itp(it) + +local eq = helpers.eq +local neq = helpers.neq +local ffi = helpers.ffi +local NULL = helpers.NULL +local cimport = helpers.cimport +local alloc_log_new = helpers.alloc_log_new + +local a = eval_helpers.alloc_logging_helpers +local list = eval_helpers.list +local lst2tbl = eval_helpers.lst2tbl +local type_key = eval_helpers.type_key +local dict_type = eval_helpers.dict_type +local lua2typvalt = eval_helpers.lua2typvalt + +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') + +local function li_alloc(nogc) + local gcfunc = lib.tv_list_item_free + if nogc then gcfunc = nil end + local li = ffi.gc(lib.tv_list_item_alloc(), gcfunc) + li.li_next = nil + li.li_prev = nil + li.li_tv = {v_type=lib.VAR_UNKNOWN, v_lock=lib.VAR_UNLOCKED} + return li +end + +local function list_index(l, idx) + return tv_list_find(l, idx) +end + +local function list_items(l) + local lis = {} + local li = l.lv_first + for i = 1, l.lv_len do + lis[i] = li + li = li.li_next + end + return lis +end + +local function list_watch(li) + return ffi.new('listwatch_T', {lw_item=li}) +end + +local alloc_log +local restore_allocators + +local to_cstr_nofree = function(v) return lib.xstrdup(v) end + +local alloc_log = alloc_log_new() + +before_each(function() + alloc_log:before_each() +end) + +after_each(function() + alloc_log:after_each() +end) + +describe('typval.c', function() + describe('list', function() + describe('item', function() + describe('alloc()/free()', function() + itp('works', function() + local li = li_alloc(true) + neq(nil, li) + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + end) + itp('also frees the value', function() + local li + local s + local l + local tv + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_NUMBER + li.li_tv.vval.v_number = 10 + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_FLOAT + li.li_tv.vval.v_float = 10.5 + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_STRING + li.li_tv.vval.v_string = nil + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.freed(alloc_log.null), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_STRING + s = to_cstr_nofree('test') + li.li_tv.vval.v_string = s + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.str(s, #('test')), + a.freed(s), + a.freed(li), + }) + + li = li_alloc(true) + li.li_tv.v_type = lib.VAR_LIST + l = list() + l.lv_refcount = 2 + li.li_tv.vval.v_list = l + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.list(l), + a.freed(li), + }) + eq(1, l.lv_refcount) + + li = li_alloc(true) + tv = lua2typvalt({[type_key]=dict_type}) + tv.vval.v_dict.dv_refcount = 2 + li.li_tv = tv + lib.tv_list_item_free(li) + alloc_log:check({ + a.li(li), + a.dict(tv.vval.v_dict), + a.freed(li), + }) + eq(1, tv.vval.v_dict.dv_refcount) + end) + end) + describe('remove()', function() + itp('works', function() + local l = list(1, 2, 3, 4, 5, 6, 7) + neq(nil, l) + local lis = list_items(l) + alloc_log:check({ + a.list(l), + a.li(lis[1]), + a.li(lis[2]), + a.li(lis[3]), + a.li(lis[4]), + a.li(lis[5]), + a.li(lis[6]), + a.li(lis[7]), + }) + + lib.tv_list_item_remove(l, lis[1]) + alloc_log:check({ + a.freed(table.remove(lis, 1)), + }) + eq(lis, list_items(l)) + + lib.tv_list_item_remove(l, lis[6]) + alloc_log:check({ + a.freed(table.remove(lis)), + }) + eq(lis, list_items(l)) + + lib.tv_list_item_remove(l, lis[3]) + alloc_log:check({ + a.freed(table.remove(lis, 3)), + }) + eq(lis, list_items(l)) + end) + itp('works and adjusts watchers correctly', function() + local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) + neq(nil, l) + local lis = list_items(l) + -- Three watchers: pointing to first, middle and last elements. + local lws = {list_watch(lis[1]), list_watch(lis[4]), list_watch(lis[7])} + lib.tv_list_watch_add(l, lws[1]) + lib.tv_list_watch_add(l, lws[2]) + lib.tv_list_watch_add(l, lws[3]) + + lib.tv_list_item_remove(l, lis[4]) + eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_item_remove(l, lis[2]) + eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_item_remove(l, lis[7]) + eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_item_remove(l, lis[1]) + eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_free(l) + end) + end) + end) + end) +end) -- cgit From d2639e1e1d041af583dadd08e34405321fc24eea Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 14 Sep 2016 19:39:19 +0300 Subject: unittests: Add tests for list watchers and list alloc/free/unref --- test/unit/eval/typval_spec.lua | 180 +++++++++++++++++++++++++++++++++++------ 1 file changed, 154 insertions(+), 26 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f2cb593cf5..0967e38eef 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -6,49 +6,48 @@ local itp = helpers.gen_itp(it) local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi -local NULL = helpers.NULL local cimport = helpers.cimport local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list -local lst2tbl = eval_helpers.lst2tbl local type_key = eval_helpers.type_key +local li_alloc = eval_helpers.li_alloc local dict_type = eval_helpers.dict_type +local list_type = eval_helpers.list_type local lua2typvalt = eval_helpers.lua2typvalt local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') -local function li_alloc(nogc) - local gcfunc = lib.tv_list_item_free - if nogc then gcfunc = nil end - local li = ffi.gc(lib.tv_list_item_alloc(), gcfunc) - li.li_next = nil - li.li_prev = nil - li.li_tv = {v_type=lib.VAR_UNKNOWN, v_lock=lib.VAR_UNLOCKED} - return li -end - -local function list_index(l, idx) - return tv_list_find(l, idx) -end - local function list_items(l) local lis = {} local li = l.lv_first for i = 1, l.lv_len do - lis[i] = li + lis[i] = ffi.gc(li, nil) li = li.li_next end return lis end -local function list_watch(li) - return ffi.new('listwatch_T', {lw_item=li}) +local function list_watch_alloc(li) + return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) end -local alloc_log -local restore_allocators +local function list_watch(l, li) + local lw = list_watch_alloc(li or l.lv_first) + lib.tv_list_watch_add(l, lw) + return lw +end + +local function get_alloc_rets(exp_log, res) + for i = 1,#exp_log do + if ({malloc=true, calloc=true})[exp_log[i].func] then + res[#res + 1] = exp_log[i].ret + end + end + res.freed = function(r, n) return {func='free', args={r[n]}} end + return exp_log +end local to_cstr_nofree = function(v) return lib.xstrdup(v) end @@ -122,7 +121,7 @@ describe('typval.c', function() li = li_alloc(true) li.li_tv.v_type = lib.VAR_LIST - l = list() + l = ffi.gc(list(), nil) l.lv_refcount = 2 li.li_tv.vval.v_list = l lib.tv_list_item_free(li) @@ -185,26 +184,155 @@ describe('typval.c', function() neq(nil, l) local lis = list_items(l) -- Three watchers: pointing to first, middle and last elements. - local lws = {list_watch(lis[1]), list_watch(lis[4]), list_watch(lis[7])} - lib.tv_list_watch_add(l, lws[1]) - lib.tv_list_watch_add(l, lws[2]) - lib.tv_list_watch_add(l, lws[3]) + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[4]), + list_watch(l, lis[7]), + } lib.tv_list_item_remove(l, lis[4]) + ffi.gc(lis[4], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[2]) + ffi.gc(lis[2], lib.tv_list_item_free) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) lib.tv_list_item_remove(l, lis[7]) + ffi.gc(lis[7], lib.tv_list_item_free) eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) lib.tv_list_item_remove(l, lis[1]) + ffi.gc(lis[1], lib.tv_list_item_free) eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + alloc_log:clear() lib.tv_list_free(l) + alloc_log:check({ + a.freed(lis[3]), + a.freed(lis[5]), + a.freed(lis[6]), + a.freed(l), + }) end) end) end) + describe('watch', function() + describe('remove()', function() + itp('works', function() + local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) + eq(nil, l.lv_watch) + local lw = list_watch(l) + neq(nil, l.lv_watch) + alloc_log:clear() + lib.tv_list_watch_remove(l, lw) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + local lws = { list_watch(l), list_watch(l), list_watch(l) } + alloc_log:clear() + lib.tv_list_watch_remove(l, lws[2]) + eq(lws[3], l.lv_watch) + eq(lws[1], l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[1]) + eq(lws[3], l.lv_watch) + eq(nil, l.lv_watch.lw_next) + lib.tv_list_watch_remove(l, lws[3]) + eq(nil, l.lv_watch) + alloc_log:check({ + -- Does not free anything. + }) + end) + itp('ignores not found watchers', function() + local l = list(1, 2, 3, 4, 5, 6, 7) + local lw = list_watch_alloc() + lib.tv_list_watch_remove(l, lw) + end) + end) + end) + -- add() and fix() were tested when testing tv_list_item_remove() + describe('alloc()/free()', function() + itp('recursively frees list with', function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({[type_key]=dict_type}), nil) + local l3 = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.li(l1.lv_first), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(1), + }) + lib.tv_list_free(l2) + alloc_log:check({ + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(5), + }) + lib.tv_list_free(l3) + alloc_log:check({ + alloc_rets:freed(9), + alloc_rets:freed(10), + alloc_rets:freed(8), + }) + end) + itp('frees all containers inside a list', function() + local l1 = ffi.gc(list('abc', {[type_key]=dict_type}, {[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.str(l1.lv_first.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_first), + a.dict(l1.lv_first.li_next.li_tv.vval.v_dict), + a.li(l1.lv_first.li_next), + a.list(l1.lv_last.li_tv.vval.v_list), + a.li(l1.lv_last), + }, alloc_rets)) + lib.tv_list_free(l1) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(4), + alloc_rets:freed(5), + alloc_rets:freed(6), + alloc_rets:freed(7), + alloc_rets:freed(1), + }) + end) + end) + describe('unref()', function() + itp('recursively frees list when reference count goes to 0', function() + local l = ffi.gc(list({[type_key]=list_type}), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l), + a.list(l.lv_first.li_tv.vval.v_list), + a.li(l.lv_first), + }, alloc_rets)) + l.lv_refcount = 2 + lib.tv_list_unref(l) + alloc_log:check({}) + lib.tv_list_unref(l) + alloc_log:check({ + alloc_rets:freed(2), + alloc_rets:freed(3), + alloc_rets:freed(1), + }) + end) + end) end) end) -- cgit From be360d88414fa3b874262f3ed3a2bba5926a751e Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 14 Sep 2016 20:32:07 +0300 Subject: unittests: Add tests for tv_list_insert() --- test/unit/eval/typval_spec.lua | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0967e38eef..fa15686fb2 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -16,6 +16,7 @@ local li_alloc = eval_helpers.li_alloc local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type local lua2typvalt = eval_helpers.lua2typvalt +local typvalt2lua = eval_helpers.typvalt2lua local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') @@ -334,5 +335,76 @@ describe('typval.c', function() }) end) end) + describe('remove_items()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}) + local l = l_tv.vval.v_list + local lis = list_items(l) + -- Three watchers: pointing to first, middle and last elements. + local lws = { + list_watch(l, lis[1]), + list_watch(l, lis[7]), + list_watch(l, lis[13]), + } + alloc_log:clear() + + lib.tv_list_remove_items(l, lis[1], lis[3]) + eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) + + lib.tv_list_remove_items(l, lis[11], lis[13]) + eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv)) + eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_remove_items(l, lis[6], lis[8]) + eq({4, 5, 9, 10}, typvalt2lua(l_tv)) + eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) + + lib.tv_list_remove_items(l, lis[4], lis[10]) + eq({[type_key]=list_type}, typvalt2lua(l_tv)) + eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) + + alloc_log:check({}) + end) + end) + describe('insert()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) + local l = l_tv.vval.v_list + local lis = list_items(l) + local li + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} + lib.tv_list_insert(l, li, lis[1]) + eq(l.lv_first, li) + eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} + lib.tv_list_insert(l, li, lis[5]) + eq(list_items(l)[6], li) + eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + end) + itp('works with an empty list', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + eq(nil, l.lv_first) + eq(nil, l.lv_last) + + local li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({100500}, typvalt2lua(l_tv)) + end) + end) end) end) -- cgit From 9b8beaff021fd83770fb5510904910de2e696263 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Sep 2016 04:25:49 +0300 Subject: unittests: Add tests for tv_list_insert*()/…append*() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/eval/helpers.lua | 18 +++- test/unit/eval/typval_spec.lua | 228 +++++++++++++++++++++++++++++++++++------ 2 files changed, 210 insertions(+), 36 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 8bf06e61f1..f4ea6799f5 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -369,7 +369,7 @@ lua2typvalt = function(l, processed) return typvalt(eval.VAR_STRING, {v_string=eval.xmemdupz(to_cstr(l), #l)}) elseif type(l) == 'cdata' then local tv = typvalt(eval.VAR_UNKNOWN) - eval.copy_tv(l, tv) + eval.tv_copy(l, tv) return tv end end @@ -379,14 +379,28 @@ local function void(ptr) return ffi.cast(void_ptr, ptr) end +local function alloc_len(len, get_ptr) + if type(len) == 'string' or type(len) == 'table' then + return #len + elseif len == nil then + return eval.strlen(get_ptr()) + else + return len + end +end + local alloc_logging_helpers = { list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end, li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end, dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end, di = function(di, size) + size = alloc_len(size, function() return di.di_key end) return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)} end, - str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end, + str = function(s, size) + size = alloc_len(size, function() return s end) + return {func='malloc', args={size + 1}, ret=void(s)} + end, freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index fa15686fb2..680d5fa3b4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -13,10 +13,14 @@ local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc +local int_type = eval_helpers.int_type local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type +local null_list = eval_helpers.null_list +local null_dict = eval_helpers.null_dict local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua +local null_string = eval_helpers.null_string local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') @@ -367,43 +371,199 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('insert()', function() - itp('works', function() - local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) - local l = l_tv.vval.v_list - local lis = list_items(l) - local li - - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} - lib.tv_list_insert(l, li, nil) - eq(l.lv_last, li) - eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) - - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} - lib.tv_list_insert(l, li, lis[1]) - eq(l.lv_first, li) - eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) - - li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} - lib.tv_list_insert(l, li, lis[5]) - eq(list_items(l)[6], li) - eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + describe('insert', function() + describe('()', function() + itp('works', function() + local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7}) + local l = l_tv.vval.v_list + local lis = list_items(l) + local li + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=0}} + lib.tv_list_insert(l, li, lis[1]) + eq(l.lv_first, li) + eq({0, 1, 2, 3, 4, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + + li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=4.5}} + lib.tv_list_insert(l, li, lis[5]) + eq(list_items(l)[6], li) + eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) + end) + itp('works with an empty list', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + eq(nil, l.lv_first) + eq(nil, l.lv_last) + + local li = li_alloc(true) + li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} + lib.tv_list_insert(l, li, nil) + eq(l.lv_last, li) + eq({100500}, typvalt2lua(l_tv)) + end) end) - itp('works with an empty list', function() - local l_tv = lua2typvalt({[type_key]=list_type}) - local l = l_tv.vval.v_list + describe('tv()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local l_l_tv = lua2typvalt({[type_key]=list_type}) + alloc_log:clear() + local l_l = l_l_tv.vval.v_list + eq(1, l_l.lv_refcount) + lib.tv_list_insert_tv(l, l_l_tv, nil) + eq(2, l_l.lv_refcount) + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_first), + }) - eq(nil, l.lv_first) - eq(nil, l.lv_last) + local l_s_tv = lua2typvalt('test') + alloc_log:check({ + a.str(l_s_tv.vval.v_string, 'test'), + }) + lib.tv_list_insert_tv(l, l_s_tv, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.str(l.lv_first.li_tv.vval.v_string, 'test'), + }) + + eq({'test', {[type_key]=list_type}}, typvalt2lua(l_tv)) + end) + end) + end) + describe('append', function() + describe('list()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local l_l = list(1) + alloc_log:clear() + eq(1, l_l.lv_refcount) + lib.tv_list_append_list(l, l_l) + eq(2, l_l.lv_refcount) + eq(l_l, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_list(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{1}, null_list}, typvalt2lua(l_tv)) + end) + end) + describe('dict()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local l_d_tv = lua2typvalt({test=1}) + local l_d = l_d_tv.vval.v_dict + alloc_log:clear() + eq(1, l_d.dv_refcount) + lib.tv_list_append_dict(l, l_d) + eq(2, l_d.dv_refcount) + eq(l_d, l.lv_first.li_tv.vval.v_list) + alloc_log:check({ + a.li(l.lv_last), + }) - local li = li_alloc(true) - li.li_tv = {v_type=lib.VAR_FLOAT, vval={v_float=100500}} - lib.tv_list_insert(l, li, nil) - eq(l.lv_last, li) - eq({100500}, typvalt2lua(l_tv)) + lib.tv_list_append_dict(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{test=1}, null_dict}, typvalt2lua(l_tv)) + end) + end) + describe('string()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + alloc_log:clear() + lib.tv_list_append_string(l, 'test', 3) + alloc_log:check({ + a.str(l.lv_last.li_tv.vval.v_string, 'tes'), + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, nil, 0) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, nil, -1) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_string(l, 'test', -1) + alloc_log:check({ + a.str(l.lv_last.li_tv.vval.v_string, 'test'), + a.li(l.lv_last), + }) + + eq({'tes', null_string, null_string, 'test'}, typvalt2lua(l_tv)) + end) + end) + describe('allocated string()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + local s = lib.xstrdup('test') + alloc_log:clear() + lib.tv_list_append_allocated_string(l, s) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_allocated_string(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_allocated_string(l, nil) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({'test', null_string, null_string}, typvalt2lua(l_tv)) + end) + end) + describe('number()', function() + itp('works', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + + alloc_log:clear() + lib.tv_list_append_number(l, -100500) + alloc_log:check({ + a.li(l.lv_last), + }) + + lib.tv_list_append_number(l, 100500) + alloc_log:check({ + a.li(l.lv_last), + }) + + eq({{[type_key]=int_type, value=-100500}, + {[type_key]=int_type, value=100500}}, typvalt2lua(l_tv)) + end) end) end) end) -- cgit From 9898f36aa306d2a7f53fbe08c64048260992d3bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 17 Sep 2016 22:06:11 +0300 Subject: unittests: Test tv_list_copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also found some bugs: 1. var_item_copy() always fails to copy v:_null_list and v:_null_dict. Fixing this should mean fixing `deepcopy(v:_null_list)` which should’ve been, but was not listed in #4615. This also fixes `deepcopy(v:_null_dict)`. 2. var_item_copy() crashes when trying to copy NULL string with `conv != NULL`. 3. `conv` argument is ignored when copying list unless `deep` is true, but it was not reflected in documentation. 4. `tv_dict_item_alloc_len()` allocated more memory then needed. 5. typvalt2lua was not able to handle self-referencing containers. --- src/nvim/eval.c | 11 ++- src/nvim/eval/typval.c | 1 + test/unit/eval/typval_spec.lua | 183 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 190 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a2b6d12288..7c3754607e 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -19024,7 +19024,7 @@ bool valid_varname(const char *varname) /// list[1]`) var_item_copy with zero copyID will emit /// a copy with (`copy[0] isnot copy[1]`), with non-zero it /// will emit a copy with (`copy[0] is copy[1]`) like in the -/// original list. Not use when deep is false. +/// original list. Not used when deep is false. int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *const to, @@ -19050,7 +19050,8 @@ int var_item_copy(const vimconv_T *const conv, tv_copy(from, to); break; case VAR_STRING: - if (conv == NULL || conv->vc_type == CONV_NONE) { + if (conv == NULL || conv->vc_type == CONV_NONE + || from->vval.v_string == NULL) { tv_copy(from, to); } else { to->v_type = VAR_STRING; @@ -19075,8 +19076,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } - if (to->vval.v_list == NULL) + if (to->vval.v_list == NULL && from->vval.v_list != NULL) { ret = FAIL; + } break; case VAR_DICT: to->v_type = VAR_DICT; @@ -19090,8 +19092,9 @@ int var_item_copy(const vimconv_T *const conv, } else { to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID); } - if (to->vval.v_dict == NULL) + if (to->vval.v_dict == NULL && from->vval.v_dict != NULL) { ret = FAIL; + } break; case VAR_UNKNOWN: EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index a26afb20c6..ae4199697f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -424,6 +424,7 @@ void tv_list_append_number(list_T *const l, const varnumber_T n) /// Make a copy of list /// /// @param[in] conv If non-NULL, then all internal strings will be converted. +/// Only used when `deep` is true. /// @param[in] orig Original list to copy. /// @param[in] deep If false, then shallow copy will be done. /// @param[in] copyID See var_item_copy(). diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 680d5fa3b4..7a6b4579f9 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -3,17 +3,21 @@ local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) +local OK = helpers.OK local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi local cimport = helpers.cimport +local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list +local lst2tbl = eval_helpers.lst2tbl local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type +local first_di = eval_helpers.first_di local dict_type = eval_helpers.dict_type local list_type = eval_helpers.list_type local null_list = eval_helpers.null_list @@ -22,7 +26,8 @@ local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string -local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h') +local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', + './src/nvim/mbyte.h') local function list_items(l) local lis = {} @@ -62,6 +67,30 @@ before_each(function() alloc_log:before_each() end) +local function clear_tmp_allocs() + local toremove = {} + local allocs = {} + for i, v in ipairs(alloc_log.log) do + if v.func == 'malloc' or v.func == 'calloc' then + allocs[tostring(v.ret)] = i + elseif v.func == 'realloc' or v.func == 'free' then + if allocs[tostring(v.args[1])] then + toremove[#toremove + 1] = allocs[tostring(v.args[1])] + if v.func == 'free' then + toremove[#toremove + 1] = i + end + end + if v.func == 'realloc' then + allocs[tostring(v.ret)] = i + end + end + end + table.sort(toremove) + for i = #toremove,1,-1 do + table.remove(alloc_log.log, toremove[i]) + end +end + after_each(function() alloc_log:after_each() end) @@ -566,5 +595,157 @@ describe('typval.c', function() end) end) end) + describe('copy()', function() + local function tv_list_copy(...) + return ffi.gc(lib.tv_list_copy(...), lib.tv_list_unref) + end + itp('copies NULL correctly', function() + eq(nil, lib.tv_list_copy(nil, nil, true, 0)) + eq(nil, lib.tv_list_copy(nil, nil, false, 0)) + eq(nil, lib.tv_list_copy(nil, nil, true, 1)) + eq(nil, lib.tv_list_copy(nil, nil, false, 1)) + end) + itp('copies list correctly without converting items', function() + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_copy1 = tv_list_copy(nil, l, false, 0) + eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(2, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_copy1 = list_items(l_copy1) + eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) + eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_copy1)) + alloc_log:check({ + a.list(l_copy1), + a.li(lis_copy1[1]), + a.li(lis_copy1[2]), + a.li(lis_copy1[3]), + a.li(lis_copy1[4]), + a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_copy1[5]), + a.li(lis_copy1[6]), + a.li(lis_copy1[7]), + }) + lib.tv_list_free(ffi.gc(l_copy1, nil)) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(nil, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, #('«')), + a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end) + itp('copies list correctly and converts items', function() + local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) + lib.convert_setup(vc, nil, nil) + end) + -- UTF-8 ↔ latin1 conversions need no iconv + eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) + + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() + + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(vc, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq({{['\171']='\187'}, {'\191'}, 1, '\191', null_string, null_list, null_dict}, + lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + clear_tmp_allocs() + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, 1), + a.str(di_deepcopy1.di_tv.vval.v_string, 2), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end) + itp('returns different/same containers with(out) copyID', function() + local l_inner_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt({l_inner_tv, l_inner_tv}) + eq(3, l_inner_tv.vval.v_list.lv_refcount) + local l = l_tv.vval.v_list + eq(l.lv_first.li_tv.vval.v_list, l.lv_last.li_tv.vval.v_list) + + local l_copy1 = tv_list_copy(nil, l, true, 0) + neq(l_copy1.lv_first.li_tv.vval.v_list, l_copy1.lv_last.li_tv.vval.v_list) + eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy1)) + + local l_copy2 = tv_list_copy(nil, l, true, 2) + eq(l_copy2.lv_first.li_tv.vval.v_list, l_copy2.lv_last.li_tv.vval.v_list) + eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy2)) + + eq(3, l_inner_tv.vval.v_list.lv_refcount) + end) + itp('works with self-referencing list with copyID', function() + local l_tv = lua2typvalt({[type_key]=list_type}) + local l = l_tv.vval.v_list + eq(1, l.lv_refcount) + lib.tv_list_append_list(l, l) + eq(2, l.lv_refcount) + + local l_copy1 = tv_list_copy(nil, l, true, 2) + eq(2, l_copy1.lv_refcount) + local v = {} + v[1] = v + eq(v, lst2tbl(l_copy1)) + + local lis = list_items(l) + lib.tv_list_item_remove(l, lis[1]) + eq(1, l.lv_refcount) + + local lis_copy1 = list_items(l_copy1) + lib.tv_list_item_remove(l_copy1, lis_copy1[1]) + eq(1, l_copy1.lv_refcount) + end) + end) end) end) -- cgit From 82e6cac5f9d437da5aeb8c4cf51945cfeb5922ff Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 18 Sep 2016 01:54:23 +0300 Subject: functests: Add null_spec.lua from #4615 For now it is full of FIXMEs and tests for incorrect behaviour. Sorted out to have FIXMEs in one place, commented crashing tests in other and correctly working tests in the third one. --- test/functional/eval/null_spec.lua | 141 +++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 test/functional/eval/null_spec.lua diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua new file mode 100644 index 0000000000..9a35905be0 --- /dev/null +++ b/test/functional/eval/null_spec.lua @@ -0,0 +1,141 @@ +local helpers = require('test.functional.helpers')(after_each) + +local curbufmeths = helpers.curbufmeths +local redir_exec = helpers.redir_exec +local exc_exec = helpers.exc_exec +local command = helpers.command +local clear = helpers.clear +local meths = helpers.meths +local funcs = helpers.funcs +local eq = helpers.eq + +describe('NULL', function() + before_each(function() + clear() + command('let L = v:_null_list') + command('let D = v:_null_dict') + command('let S = $XXX_NONEXISTENT_VAR_XXX') + end) + local tmpfname = 'Xtest-functional-viml-null' + after_each(function() + os.remove(tmpfname) + end) + describe('list', function() + local null_list_test = function(name, cmd, err) + it(name, function() + eq(err, exc_exec(cmd)) + end) + end + local null_list_expr_test = function(name, expr, err, val, after) + it(name, function() + eq((err == 0) and ('') or ('\n' .. err), + redir_exec('let g:_var = ' .. expr)) + if val == nil then + eq(0, funcs.exists('g:_var')) + else + eq(val, meths.get_var('_var')) + end + if after ~= nil then + after() + end + end) + end + + -- Incorrect behaviour + + -- FIXME map() should not return 0 without error + null_list_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) + -- FIXME map() should not return 0 without error + null_list_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) + -- FIXME map() should at least return L + null_list_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) + -- FIXME filter() should at least return L + null_list_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) + -- FIXME add() should not return 1 at all + null_list_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + -- FIXME extend() should not return 0 without error + null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) + -- FIXME extend() should not return 0 at all + null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is equal to itself', 'L == L', 0, 0) + -- FIXME Should return 0 + null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) + -- FIXME Should return empty list + null_list_expr_test('can be added to itself', '(L + L)', 'E15: Invalid expression: (L + L)', nil) + -- FIXME Should return [1] + null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', + 'E15: Invalid expression: (L + [1])', nil) + -- FIXME Should return [1] + null_list_expr_test('can be added to non-empty list', '([1] + L)', + 'E15: Invalid expression: ([1] + L)', nil) + -- FIXME Should return 1 + null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) + -- FIXME should be accepted by inputlist() + null_list_expr_test('is accepted as an empty list by inputlist()', + '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) + -- FIXME should be accepted by writefile(), return {0, {}} + null_list_expr_test('is accepted as an empty list by writefile()', + ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), + 'E484: Can\'t open file ' .. tmpfname, {0, {}}) + -- FIXME should give error message + null_list_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) + -- FIXME should return 0 + null_list_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) + -- FIXME should return 0 + null_list_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) + -- FIXME should return 0 + null_list_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) + -- FIXME should return empty list or error out + null_list_expr_test('is accepted by sort()', 'sort(L)', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) + -- FIXME should not error out + null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') + -- FIXME should not error out + null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') + -- FIXME should not error out + null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 'Vim(for):E714: List required') + + -- Subjectable behaviour + + -- FIXME Should return 1 + null_list_expr_test('is equal to empty list', 'L == []', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) + -- FIXME Should return 1 + null_list_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) + + -- Crashes + + -- null_list_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + -- null_list_expr_test('does not crash setline', 'setline(1, L)', 0, 0) + -- null_list_expr_test('does not crash system()', 'system("cat", L)', 0, '') + -- null_list_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + + -- Correct behaviour + null_list_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() + eq({''}, curbufmeths.get_lines(0, -1, false)) + end) + null_list_expr_test('is identical to itself', 'L is L', 0, 1) + null_list_expr_test('can be sliced', 'L[:]', 0, {}) + null_list_expr_test('can be copied', 'copy(L)', 0, {}) + null_list_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) + null_list_expr_test('does not crash when indexed', 'L[1]', + 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) + null_list_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) + null_list_expr_test('does not crash col()', 'col(L)', 0, 0) + null_list_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) + null_list_expr_test('does not crash line()', 'line(L)', 0, 0) + null_list_expr_test('does not crash count()', 'count(L, 1)', 0, 0) + null_list_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_list_expr_test('is empty', 'empty(L)', 0, 1) + null_list_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) + null_list_expr_test('has zero length', 'len(L)', 0, 0) + null_list_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) + null_list_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) + null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') + null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') + null_list_test('does not crash lockvar', 'lockvar! L', 0) + end) +end) -- cgit From f80a00469fdba8a3dec0edae30c911d050485055 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 18 Sep 2016 01:59:07 +0300 Subject: eval/typval: Make tv_list_concat handle NULL lists correctly Fixes some FIXMEs in eval/null_spec.lua. --- src/nvim/eval/typval.c | 25 +++++++++++++++---------- test/functional/eval/null_spec.lua | 12 ++++-------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ae4199697f..9dd1b62d7d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -498,20 +498,25 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - return FAIL; - } + list_T *l; - // make a copy of the first list. - list_T *const l = tv_list_copy(NULL, l1, false, 0); - if (l == NULL) { + tv->v_type = VAR_LIST; + + if (l1 == NULL && l2 == NULL) { + l = NULL; + } else if (l1 == NULL) { + l = tv_list_copy(NULL, l2, false, 0); + } else { + l = tv_list_copy(NULL, l1, false, 0); + if (l != NULL && l2 != NULL) { + tv_list_extend(l, l2, NULL); + } + } + if (l == NULL && !(l1 == NULL && l2 == NULL)) { return FAIL; } - tv->v_type = VAR_LIST; - tv->vval.v_list = l; - // append all items from the second list - tv_list_extend(l, l2, NULL); + tv->vval.v_list = l; return OK; } diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 9a35905be0..ccdd1ce34c 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -61,14 +61,6 @@ describe('NULL', function() null_list_expr_test('is equal to itself', 'L == L', 0, 0) -- FIXME Should return 0 null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) - -- FIXME Should return empty list - null_list_expr_test('can be added to itself', '(L + L)', 'E15: Invalid expression: (L + L)', nil) - -- FIXME Should return [1] - null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', - 'E15: Invalid expression: (L + [1])', nil) - -- FIXME Should return [1] - null_list_expr_test('can be added to non-empty list', '([1] + L)', - 'E15: Invalid expression: ([1] + L)', nil) -- FIXME Should return 1 null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) -- FIXME should be accepted by inputlist() @@ -137,5 +129,9 @@ describe('NULL', function() null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') null_list_test('does not crash lockvar', 'lockvar! L', 0) + null_list_expr_test('can be added to itself', '(L + L)', 0, {}) + null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) + null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) + null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) end) end) -- cgit From 56e4c2f67e54b88f637d30e565313bc6b2c22f29 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 19 Sep 2016 00:50:07 +0300 Subject: unittests: Test tv_list_concat() --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 142 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 9dd1b62d7d..1cc195ddc4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -481,7 +481,7 @@ void tv_list_extend(list_T *const l1, list_T *const l2, int todo = l2->lv_len; // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first + for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 ; item = item->li_next) { tv_list_insert_tv(l1, &item->li_tv, bef); diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 7a6b4579f9..2cc22053f3 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -14,6 +14,7 @@ local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local list = eval_helpers.list local lst2tbl = eval_helpers.lst2tbl +local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type @@ -747,5 +748,146 @@ describe('typval.c', function() eq(1, l_copy1.lv_refcount) end) end) + describe('concat()', function() + itp('works with NULL lists', function() + local l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv1 = typvalt() + eq(OK, lib.tv_list_concat(nil, l, rettv1)) + eq(1, l.lv_refcount) + eq(tonumber(lib.VAR_LIST), tonumber(rettv1.v_type)) + eq({1, {}}, typvalt2lua(rettv1)) + eq(1, rettv1.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv1.vval.v_list), + a.li(rettv1.vval.v_list.lv_first), + a.li(rettv1.vval.v_list.lv_last), + }) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv2 = typvalt() + eq(OK, lib.tv_list_concat(l, nil, rettv2)) + eq(1, l.lv_refcount) + eq(tonumber(lib.VAR_LIST), tonumber(rettv2.v_type)) + eq({1, {}}, typvalt2lua(rettv2)) + eq(1, rettv2.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv2.vval.v_list), + a.li(rettv2.vval.v_list.lv_first), + a.li(rettv2.vval.v_list.lv_last), + }) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + local rettv3 = typvalt() + eq(OK, lib.tv_list_concat(nil, nil, rettv3)) + eq(tonumber(lib.VAR_LIST), tonumber(rettv3.v_type)) + eq(null_list, typvalt2lua(rettv3)) + alloc_log:check({}) + end) + itp('works with two different lists', function() + local l1 = list(1, {}) + local l2 = list(3, {[type_key]=list_type}) + eq(1, l1.lv_refcount) + eq(1, l1.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, l2.lv_refcount) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + alloc_log:clear() + + local rettv = typvalt() + eq(OK, lib.tv_list_concat(l1, l2, rettv)) + eq(1, l1.lv_refcount) + eq(2, l1.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + alloc_log:check({ + a.list(rettv.vval.v_list), + a.li(rettv.vval.v_list.lv_first), + a.li(rettv.vval.v_list.lv_first.li_next), + a.li(rettv.vval.v_list.lv_last.li_prev), + a.li(rettv.vval.v_list.lv_last), + }) + eq({1, {}, 3, {[type_key]=list_type}}, typvalt2lua(rettv)) + end) + itp('can concatenate list with itself', function() + local l = list(1, {}) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + alloc_log:clear() + + local rettv = typvalt() + eq(OK, lib.tv_list_concat(l, l, rettv)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + alloc_log:check({ + a.list(rettv.vval.v_list), + a.li(rettv.vval.v_list.lv_first), + a.li(rettv.vval.v_list.lv_first.li_next), + a.li(rettv.vval.v_list.lv_last.li_prev), + a.li(rettv.vval.v_list.lv_last), + }) + eq({1, {}, 1, {}}, typvalt2lua(rettv)) + end) + itp('can concatenate empty non-NULL lists', function() + local l = list(1, {}) + local le = list() + local le2 = list() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:clear() + + local rettv1 = typvalt() + eq(OK, lib.tv_list_concat(l, le, rettv1)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv1.vval.v_list), + a.li(rettv1.vval.v_list.lv_first), + a.li(rettv1.vval.v_list.lv_last), + }) + eq({1, {}}, typvalt2lua(rettv1)) + + local rettv2 = typvalt() + eq(OK, lib.tv_list_concat(le, l, rettv2)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv2.vval.v_list), + a.li(rettv2.vval.v_list.lv_first), + a.li(rettv2.vval.v_list.lv_last), + }) + eq({1, {}}, typvalt2lua(rettv2)) + + local rettv3 = typvalt() + eq(OK, lib.tv_list_concat(le, le, rettv3)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv3.vval.v_list), + }) + eq({[type_key]=list_type}, typvalt2lua(rettv3)) + + local rettv4 = typvalt() + eq(OK, lib.tv_list_concat(le, le2, rettv4)) + eq(1, l.lv_refcount) + eq(3, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, le.lv_refcount) + eq(1, le2.lv_refcount) + alloc_log:check({ + a.list(rettv4.vval.v_list), + }) + eq({[type_key]=list_type}, typvalt2lua(rettv4)) + end) + end) end) end) -- cgit From 7ceebacb3fad49ba8321397cf839948caa55b3f5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 24 Sep 2016 00:51:34 +0300 Subject: eval/typval,tests: Fix extending list with itself, add tests Adds unit test for tv_list_extend and regression test for extend() VimL function. --- src/nvim/eval/typval.c | 4 +- test/functional/eval/container_functions_spec.lua | 24 +++ test/unit/eval/helpers.lua | 2 + test/unit/eval/typval_spec.lua | 184 +++++++++++++++++++--- 4 files changed, 187 insertions(+), 27 deletions(-) create mode 100644 test/functional/eval/container_functions_spec.lua diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 1cc195ddc4..746cbbfe1c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -479,11 +479,13 @@ void tv_list_extend(list_T *const l1, list_T *const l2, FUNC_ATTR_NONNULL_ARG(1, 2) { int todo = l2->lv_len; + listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); + listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. for (listitem_T *item = l2->lv_first ; item != NULL && --todo >= 0 - ; item = item->li_next) { + ; item = (item == befbef ? saved_next : item->li_next)) { tv_list_insert_tv(l1, &item->li_tv, bef); } } diff --git a/test/functional/eval/container_functions_spec.lua b/test/functional/eval/container_functions_spec.lua new file mode 100644 index 0000000000..04a3248c49 --- /dev/null +++ b/test/functional/eval/container_functions_spec.lua @@ -0,0 +1,24 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local meths = helpers.meths +local clear = helpers.clear + +before_each(clear) + +describe('extend()', function() + it('suceeds to extend list with itself', function() + meths.set_var('l', {1, {}}) + eq({1, {}, 1, {}}, eval('extend(l, l)')) + eq({1, {}, 1, {}}, meths.get_var('l')) + + meths.set_var('l', {1, {}}) + eq({1, {}, 1, {}}, eval('extend(l, l, 0)')) + eq({1, {}, 1, {}}, meths.get_var('l')) + + meths.set_var('l', {1, {}}) + eq({1, 1, {}, {}}, eval('extend(l, l, 1)')) + eq({1, 1, {}, {}}, meths.get_var('l')) + end) +end) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index f4ea6799f5..eea5cc8880 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -439,4 +439,6 @@ return { list_items=list_items, dict_items=dict_items, + + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2cc22053f3..926bf1c478 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -19,10 +19,9 @@ local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type local first_di = eval_helpers.first_di -local dict_type = eval_helpers.dict_type -local list_type = eval_helpers.list_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict +local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string @@ -168,7 +167,7 @@ describe('typval.c', function() eq(1, l.lv_refcount) li = li_alloc(true) - tv = lua2typvalt({[type_key]=dict_type}) + tv = lua2typvalt({}) tv.vval.v_dict.dv_refcount = 2 li.li_tv = tv lib.tv_list_item_free(li) @@ -290,8 +289,8 @@ describe('typval.c', function() describe('alloc()/free()', function() itp('recursively frees list with', function() local l1 = ffi.gc(list(1, 'abc'), nil) - local l2 = ffi.gc(list({[type_key]=dict_type}), nil) - local l3 = ffi.gc(list({[type_key]=list_type}), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), @@ -325,8 +324,8 @@ describe('typval.c', function() alloc_rets:freed(8), }) end) - itp('frees all containers inside a list', function() - local l1 = ffi.gc(list('abc', {[type_key]=dict_type}, {[type_key]=list_type}), nil) + itp('does not free container items with recurse=false', function() + local l1 = ffi.gc(list('abc', {}, empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), @@ -351,7 +350,7 @@ describe('typval.c', function() end) describe('unref()', function() itp('recursively frees list when reference count goes to 0', function() - local l = ffi.gc(list({[type_key]=list_type}), nil) + local l = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l), @@ -395,7 +394,7 @@ describe('typval.c', function() eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) lib.tv_list_remove_items(l, lis[4], lis[10]) - eq({[type_key]=list_type}, typvalt2lua(l_tv)) + eq(empty_list, typvalt2lua(l_tv)) eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) alloc_log:check({}) @@ -428,7 +427,7 @@ describe('typval.c', function() eq({0, 1, 2, 3, 4, 4.5, 5, 6, 7, 100500}, typvalt2lua(l_tv)) end) itp('works with an empty list', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list eq(nil, l.lv_first) @@ -443,10 +442,10 @@ describe('typval.c', function() end) describe('tv()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list - local l_l_tv = lua2typvalt({[type_key]=list_type}) + local l_l_tv = lua2typvalt(empty_list) alloc_log:clear() local l_l = l_l_tv.vval.v_list eq(1, l_l.lv_refcount) @@ -467,14 +466,14 @@ describe('typval.c', function() a.str(l.lv_first.li_tv.vval.v_string, 'test'), }) - eq({'test', {[type_key]=list_type}}, typvalt2lua(l_tv)) + eq({'test', empty_list}, typvalt2lua(l_tv)) end) end) end) describe('append', function() describe('list()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local l_l = list(1) @@ -497,7 +496,7 @@ describe('typval.c', function() end) describe('dict()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local l_d_tv = lua2typvalt({test=1}) @@ -521,7 +520,7 @@ describe('typval.c', function() end) describe('string()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list alloc_log:clear() @@ -552,7 +551,7 @@ describe('typval.c', function() end) describe('allocated string()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list local s = lib.xstrdup('test') @@ -577,7 +576,7 @@ describe('typval.c', function() end) describe('number()', function() itp('works', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list alloc_log:clear() @@ -710,7 +709,7 @@ describe('typval.c', function() }) end) itp('returns different/same containers with(out) copyID', function() - local l_inner_tv = lua2typvalt({[type_key]=list_type}) + local l_inner_tv = lua2typvalt(empty_list) local l_tv = lua2typvalt({l_inner_tv, l_inner_tv}) eq(3, l_inner_tv.vval.v_list.lv_refcount) local l = l_tv.vval.v_list @@ -718,16 +717,16 @@ describe('typval.c', function() local l_copy1 = tv_list_copy(nil, l, true, 0) neq(l_copy1.lv_first.li_tv.vval.v_list, l_copy1.lv_last.li_tv.vval.v_list) - eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy1)) + eq({empty_list, empty_list}, lst2tbl(l_copy1)) local l_copy2 = tv_list_copy(nil, l, true, 2) eq(l_copy2.lv_first.li_tv.vval.v_list, l_copy2.lv_last.li_tv.vval.v_list) - eq({{[type_key]=list_type}, {[type_key]=list_type}}, lst2tbl(l_copy2)) + eq({empty_list, empty_list}, lst2tbl(l_copy2)) eq(3, l_inner_tv.vval.v_list.lv_refcount) end) itp('works with self-referencing list with copyID', function() - local l_tv = lua2typvalt({[type_key]=list_type}) + local l_tv = lua2typvalt(empty_list) local l = l_tv.vval.v_list eq(1, l.lv_refcount) lib.tv_list_append_list(l, l) @@ -748,6 +747,139 @@ describe('typval.c', function() eq(1, l_copy1.lv_refcount) end) end) + describe('extend()', function() + itp('can extend list with itself', function() + local l + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, nil) + alloc_log:check({ + a.li(l.lv_last.li_prev), + a.li(l.lv_last), + }) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq({1, {}, 1, {}}, lst2tbl(l)) + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, l.lv_last) + alloc_log:check({ + a.li(l.lv_last.li_prev.li_prev), + a.li(l.lv_last.li_prev), + }) + eq({1, 1, {}, {}}, lst2tbl(l)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + l = list(1, {}) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.li(l.lv_first.li_next), + }) + eq({1, {}, 1, {}}, lst2tbl(l)) + eq(1, l.lv_refcount) + eq(2, l.lv_last.li_tv.vval.v_dict.dv_refcount) + end) + itp('can extend list with an empty list', function() + local l = list(1, {}) + local el = list() + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + + lib.tv_list_extend(l, el, nil) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + + lib.tv_list_extend(l, el, l.lv_first) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + + lib.tv_list_extend(l, el, l.lv_last) + alloc_log:check({ + }) + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + eq(1, el.lv_refcount) + eq({1, {}}, lst2tbl(l)) + end) + itp('can extend list with another non-empty list', function() + local l + local l2 = list(42, empty_list) + eq(1, l2.lv_refcount) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, nil) + alloc_log:check({ + a.li(l.lv_last.li_prev), + a.li(l.lv_last), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({1, {}, 42, empty_list}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, l.lv_first) + alloc_log:check({ + a.li(l.lv_first), + a.li(l.lv_first.li_next), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({42, empty_list, 1, {}}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + + l = ffi.gc(list(1, {}), nil) + alloc_log:clear() + eq(1, l.lv_refcount) + eq(1, l.lv_last.li_tv.vval.v_dict.dv_refcount) + + lib.tv_list_extend(l, l2, l.lv_last) + alloc_log:check({ + a.li(l.lv_first.li_next), + a.li(l.lv_first.li_next.li_next), + }) + eq(1, l2.lv_refcount) + eq(2, l2.lv_last.li_tv.vval.v_list.lv_refcount) + eq({1, 42, empty_list, {}}, lst2tbl(l)) + lib.tv_list_free(l) + eq(1, l2.lv_last.li_tv.vval.v_list.lv_refcount) + end) + end) describe('concat()', function() itp('works with NULL lists', function() local l = list(1, {}) @@ -789,7 +921,7 @@ describe('typval.c', function() end) itp('works with two different lists', function() local l1 = list(1, {}) - local l2 = list(3, {[type_key]=list_type}) + local l2 = list(3, empty_list) eq(1, l1.lv_refcount) eq(1, l1.lv_last.li_tv.vval.v_dict.dv_refcount) eq(1, l2.lv_refcount) @@ -809,7 +941,7 @@ describe('typval.c', function() a.li(rettv.vval.v_list.lv_last.li_prev), a.li(rettv.vval.v_list.lv_last), }) - eq({1, {}, 3, {[type_key]=list_type}}, typvalt2lua(rettv)) + eq({1, {}, 3, empty_list}, typvalt2lua(rettv)) end) itp('can concatenate list with itself', function() local l = list(1, {}) @@ -875,7 +1007,7 @@ describe('typval.c', function() alloc_log:check({ a.list(rettv3.vval.v_list), }) - eq({[type_key]=list_type}, typvalt2lua(rettv3)) + eq(empty_list, typvalt2lua(rettv3)) local rettv4 = typvalt() eq(OK, lib.tv_list_concat(le, le2, rettv4)) @@ -886,7 +1018,7 @@ describe('typval.c', function() alloc_log:check({ a.list(rettv4.vval.v_list), }) - eq({[type_key]=list_type}, typvalt2lua(rettv4)) + eq(empty_list, typvalt2lua(rettv4)) end) end) end) -- cgit From cf45c7bb059dafeb882133273f77042cb06b37e4 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 25 Sep 2016 18:16:17 +0300 Subject: unittests: Fix tests crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests crash at some point without - `after_each(collectgarbage)` right before “typval.c list copy() copies list correctly and converts items” test. - Commenting out that test. - Adding `collectgarbage()` after the test (what actually this commit does). Adding `collectgarbage()` to top-level `after_each` block right after `restore_allocators` makes running this file crash even if it is run alone. --- test/unit/eval/typval_spec.lua | 113 +++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 926bf1c478..2c9b7c66db 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -606,63 +606,66 @@ describe('typval.c', function() eq(nil, lib.tv_list_copy(nil, nil, false, 1)) end) itp('copies list correctly without converting items', function() - local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} - local l_tv = lua2typvalt(v) - local l = l_tv.vval.v_list - local lis = list_items(l) - alloc_log:clear() + do + local v = {{['«']='»'}, {'„'}, 1, '“', null_string, null_list, null_dict} + local l_tv = lua2typvalt(v) + local l = l_tv.vval.v_list + local lis = list_items(l) + alloc_log:clear() - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local l_copy1 = tv_list_copy(nil, l, false, 0) - eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(2, lis[2].li_tv.vval.v_list.lv_refcount) - local lis_copy1 = list_items(l_copy1) - eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) - eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) - eq(v, lst2tbl(l_copy1)) - alloc_log:check({ - a.list(l_copy1), - a.li(lis_copy1[1]), - a.li(lis_copy1[2]), - a.li(lis_copy1[3]), - a.li(lis_copy1[4]), - a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), - a.li(lis_copy1[5]), - a.li(lis_copy1[6]), - a.li(lis_copy1[7]), - }) - lib.tv_list_free(ffi.gc(l_copy1, nil)) - alloc_log:clear() + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_copy1 = tv_list_copy(nil, l, false, 0) + eq(2, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(2, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_copy1 = list_items(l_copy1) + eq(lis[1].li_tv.vval.v_dict, lis_copy1[1].li_tv.vval.v_dict) + eq(lis[2].li_tv.vval.v_list, lis_copy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_copy1)) + alloc_log:check({ + a.list(l_copy1), + a.li(lis_copy1[1]), + a.li(lis_copy1[2]), + a.li(lis_copy1[3]), + a.li(lis_copy1[4]), + a.str(lis_copy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_copy1[5]), + a.li(lis_copy1[6]), + a.li(lis_copy1[7]), + }) + lib.tv_list_free(ffi.gc(l_copy1, nil)) + alloc_log:clear() - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local l_deepcopy1 = tv_list_copy(nil, l, true, 0) - neq(nil, l_deepcopy1) - eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) - eq(1, lis[2].li_tv.vval.v_list.lv_refcount) - local lis_deepcopy1 = list_items(l_deepcopy1) - neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) - neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) - eq(v, lst2tbl(l_deepcopy1)) - local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) - alloc_log:check({ - a.list(l_deepcopy1), - a.li(lis_deepcopy1[1]), - a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), - a.di(di_deepcopy1, #('«')), - a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), - a.li(lis_deepcopy1[2]), - a.list(lis_deepcopy1[2].li_tv.vval.v_list), - a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), - a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), - a.li(lis_deepcopy1[3]), - a.li(lis_deepcopy1[4]), - a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), - a.li(lis_deepcopy1[5]), - a.li(lis_deepcopy1[6]), - a.li(lis_deepcopy1[7]), - }) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local l_deepcopy1 = tv_list_copy(nil, l, true, 0) + neq(nil, l_deepcopy1) + eq(1, lis[1].li_tv.vval.v_dict.dv_refcount) + eq(1, lis[2].li_tv.vval.v_list.lv_refcount) + local lis_deepcopy1 = list_items(l_deepcopy1) + neq(lis[1].li_tv.vval.v_dict, lis_deepcopy1[1].li_tv.vval.v_dict) + neq(lis[2].li_tv.vval.v_list, lis_deepcopy1[2].li_tv.vval.v_list) + eq(v, lst2tbl(l_deepcopy1)) + local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) + alloc_log:check({ + a.list(l_deepcopy1), + a.li(lis_deepcopy1[1]), + a.dict(lis_deepcopy1[1].li_tv.vval.v_dict), + a.di(di_deepcopy1, #('«')), + a.str(di_deepcopy1.di_tv.vval.v_string, #v[1]['«']), + a.li(lis_deepcopy1[2]), + a.list(lis_deepcopy1[2].li_tv.vval.v_list), + a.li(lis_deepcopy1[2].li_tv.vval.v_list.lv_first), + a.str(lis_deepcopy1[2].li_tv.vval.v_list.lv_first.li_tv.vval.v_string, #v[2][1]), + a.li(lis_deepcopy1[3]), + a.li(lis_deepcopy1[4]), + a.str(lis_deepcopy1[4].li_tv.vval.v_string, #v[4]), + a.li(lis_deepcopy1[5]), + a.li(lis_deepcopy1[6]), + a.li(lis_deepcopy1[7]), + }) + end + collectgarbage() end) itp('copies list correctly and converts items', function() local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) -- cgit From 4f9e7844272335943c075b9444934f77d3cba234 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 25 Sep 2016 22:50:03 +0300 Subject: unittests: Test tv_list_join() --- test/unit/eval/typval_spec.lua | 43 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2c9b7c66db..382ec79429 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -27,7 +27,7 @@ local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', - './src/nvim/mbyte.h') + './src/nvim/mbyte.h', './src/nvim/garray.h') local function list_items(l) local lis = {} @@ -95,6 +95,13 @@ after_each(function() alloc_log:after_each() end) +local function ga_alloc(itemsize, growsize) + local ga = ffi.gc(ffi.cast('garray_T*', ffi.new('garray_T[1]', {})), + lib.ga_clear) + lib.ga_init(ga, itemsize or 1, growsize or 80) + return ga +end + describe('typval.c', function() describe('list', function() describe('item', function() @@ -1025,4 +1032,38 @@ describe('typval.c', function() end) end) end) + describe('join()', function() + local function list_join(l, sep, ret) + local ga = ga_alloc() + eq(ret or OK, lib.tv_list_join(ga, l, sep)) + if ga.ga_data == nil then return '' + else return ffi.string(ga.ga_data) + end + end + it('works', function() + local l + l = list('boo', 'far') + eq('boo far', list_join(l, ' ')) + eq('boofar', list_join(l, '')) + + l = list('boo') + eq('boo', list_join(l, ' ')) + + l = list() + eq('', list_join(l, ' ')) + + l = list({}, 'far') + eq('{} far', list_join(l, ' ')) + + local recursive_list = {} + recursive_list[1] = recursive_list + l = ffi.gc(list(recursive_list, 'far'), nil) + eq('[[...@0]] far', list_join(l, ' ')) + + local recursive_l = l.lv_first.li_tv.vval.v_list + local recursive_li = recursive_l.lv_first + lib.tv_list_item_remove(recursive_l, recursive_li) + lib.tv_list_free(l, true) + end) + end) end) -- cgit From b3672ae2fced4715963442d2e19048f8fadbe0b8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 2 Oct 2016 04:34:30 +0300 Subject: eval/typval: Add tv_list_equal() tests, compare NULL lists equal --- src/nvim/eval/typval.c | 7 +- test/functional/eval/null_spec.lua | 9 +-- test/unit/eval/typval_spec.lua | 133 ++++++++++++++++++++++++++++--------- 3 files changed, 107 insertions(+), 42 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 746cbbfe1c..b0b95e955f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -623,13 +623,12 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, const bool recursive) FUNC_ATTR_WARN_UNUSED_RESULT { - if (l1 == NULL || l2 == NULL) { - // FIXME? compare empty list with NULL list equal - return false; - } if (l1 == l2) { return true; } + if (l1 == NULL || l2 == NULL) { + return false; + } if (tv_list_len(l1) != tv_list_len(l2)) { return false; } diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index ccdd1ce34c..e587ede319 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -57,12 +57,6 @@ describe('NULL', function() null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) -- FIXME extend() should not return 0 at all null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) - -- FIXME Should return 1 - null_list_expr_test('is equal to itself', 'L == L', 0, 0) - -- FIXME Should return 0 - null_list_expr_test('is not not equal to itself', 'L != L', 0, 1) - -- FIXME Should return 1 - null_list_expr_test('counts correctly', 'count([L], L)', 0, 0) -- FIXME should be accepted by inputlist() null_list_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) @@ -133,5 +127,8 @@ describe('NULL', function() null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) + null_list_expr_test('is equal to itself', 'L == L', 0, 1) + null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) + null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 382ec79429..8740e9eb1f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1031,39 +1031,108 @@ describe('typval.c', function() eq(empty_list, typvalt2lua(rettv4)) end) end) - end) - describe('join()', function() - local function list_join(l, sep, ret) - local ga = ga_alloc() - eq(ret or OK, lib.tv_list_join(ga, l, sep)) - if ga.ga_data == nil then return '' - else return ffi.string(ga.ga_data) + describe('join()', function() + local function list_join(l, sep, ret) + local ga = ga_alloc() + eq(ret or OK, lib.tv_list_join(ga, l, sep)) + if ga.ga_data == nil then return '' + else return ffi.string(ga.ga_data) + end end - end - it('works', function() - local l - l = list('boo', 'far') - eq('boo far', list_join(l, ' ')) - eq('boofar', list_join(l, '')) - - l = list('boo') - eq('boo', list_join(l, ' ')) - - l = list() - eq('', list_join(l, ' ')) - - l = list({}, 'far') - eq('{} far', list_join(l, ' ')) - - local recursive_list = {} - recursive_list[1] = recursive_list - l = ffi.gc(list(recursive_list, 'far'), nil) - eq('[[...@0]] far', list_join(l, ' ')) - - local recursive_l = l.lv_first.li_tv.vval.v_list - local recursive_li = recursive_l.lv_first - lib.tv_list_item_remove(recursive_l, recursive_li) - lib.tv_list_free(l, true) + itp('works', function() + local l + l = list('boo', 'far') + eq('boo far', list_join(l, ' ')) + eq('boofar', list_join(l, '')) + + l = list('boo') + eq('boo', list_join(l, ' ')) + + l = list() + eq('', list_join(l, ' ')) + + l = list({}, 'far') + eq('{} far', list_join(l, ' ')) + + local recursive_list = {} + recursive_list[1] = recursive_list + l = ffi.gc(list(recursive_list, 'far'), nil) + eq('[[...@0]] far', list_join(l, ' ')) + + local recursive_l = l.lv_first.li_tv.vval.v_list + local recursive_li = recursive_l.lv_first + lib.tv_list_item_remove(recursive_l, recursive_li) + lib.tv_list_free(l, true) + end) + end) + describe('equal()', function() + itp('compares empty and NULL lists correctly', function() + local l = list() + local l2 = list() + + -- NULL lists are not equal to empty lists + eq(false, lib.tv_list_equal(l, nil, true, false)) + eq(false, lib.tv_list_equal(nil, l, false, false)) + eq(false, lib.tv_list_equal(nil, l, false, true)) + eq(false, lib.tv_list_equal(l, nil, true, true)) + + -- Yet NULL lists are equal themselves + eq(true, lib.tv_list_equal(nil, nil, true, false)) + eq(true, lib.tv_list_equal(nil, nil, false, false)) + eq(true, lib.tv_list_equal(nil, nil, false, true)) + eq(true, lib.tv_list_equal(nil, nil, true, true)) + + -- As well as empty lists + eq(true, lib.tv_list_equal(l, l, true, false)) + eq(true, lib.tv_list_equal(l, l2, false, false)) + eq(true, lib.tv_list_equal(l2, l, false, true)) + eq(true, lib.tv_list_equal(l2, l2, true, true)) + end) + -- Must not use recursive=true argument in the following tests because it + -- indicates that tv_equal_recurse_limit and recursive_cnt were set which + -- is essential. This argument will be set when comparing inner lists. + itp('compares lists correctly when case is not ignored', function() + local l1 = list('abc', {1, 2, 'Abc'}, 'def') + local l2 = list('abc', {1, 2, 'Abc'}) + local l3 = list('abc', {1, 2, 'Abc'}, 'Def') + local l4 = list('abc', {1, 2, 'Abc', 4}, 'def') + local l5 = list('Abc', {1, 2, 'Abc'}, 'def') + local l6 = list('abc', {1, 2, 'Abc'}, 'def') + local l7 = list('abc', {1, 2, 'abc'}, 'def') + local l8 = list('abc', nil, 'def') + local l9 = list('abc', {1, 2, nil}, 'def') + + eq(true, lib.tv_list_equal(l1, l1, false, false)) + eq(false, lib.tv_list_equal(l1, l2, false, false)) + eq(false, lib.tv_list_equal(l1, l3, false, false)) + eq(false, lib.tv_list_equal(l1, l4, false, false)) + eq(false, lib.tv_list_equal(l1, l5, false, false)) + eq(true, lib.tv_list_equal(l1, l6, false, false)) + eq(false, lib.tv_list_equal(l1, l7, false, false)) + eq(false, lib.tv_list_equal(l1, l8, false, false)) + eq(false, lib.tv_list_equal(l1, l9, false, false)) + end) + itp('compares lists correctly when case is ignored', function() + local l1 = list('abc', {1, 2, 'Abc'}, 'def') + local l2 = list('abc', {1, 2, 'Abc'}) + local l3 = list('abc', {1, 2, 'Abc'}, 'Def') + local l4 = list('abc', {1, 2, 'Abc', 4}, 'def') + local l5 = list('Abc', {1, 2, 'Abc'}, 'def') + local l6 = list('abc', {1, 2, 'Abc'}, 'def') + local l7 = list('abc', {1, 2, 'abc'}, 'def') + local l8 = list('abc', nil, 'def') + local l9 = list('abc', {1, 2, nil}, 'def') + + eq(true, lib.tv_list_equal(l1, l1, true, false)) + eq(false, lib.tv_list_equal(l1, l2, true, false)) + eq(true, lib.tv_list_equal(l1, l3, true, false)) + eq(false, lib.tv_list_equal(l1, l4, true, false)) + eq(true, lib.tv_list_equal(l1, l5, true, false)) + eq(true, lib.tv_list_equal(l1, l6, true, false)) + eq(true, lib.tv_list_equal(l1, l7, true, false)) + eq(false, lib.tv_list_equal(l1, l8, true, false)) + eq(false, lib.tv_list_equal(l1, l9, true, false)) + end) end) end) end) -- cgit From e5edf07ec44f8d147d7482cae2997be62c30373f Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 4 Nov 2016 19:33:36 +0300 Subject: unittests: Add tests for tv_list_find*() functions Additional modifications: - More `const` qualifiers in tested functions. - `tv_list_find_str()` second argument is more in-line with other `tv_list_find*()` functions. --- src/nvim/eval.c | 2 +- src/nvim/eval/typval.c | 12 +-- src/nvim/ex_docmd.c | 2 +- test/unit/eval/helpers.lua | 6 ++ test/unit/eval/typval_spec.lua | 190 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 8 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7c3754607e..d5624f354a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -21783,7 +21783,7 @@ void ex_oldfiles(exarg_T *eap) nr = prompt_for_number(false); msg_starthere(); if (nr > 0 && nr <= l->lv_len) { - const char *const p = tv_list_find_str(l, nr); + const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index b0b95e955f..ef64467ee5 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -731,7 +731,7 @@ listitem_T *tv_list_find(list_T *const l, int n) /// `*ret_error` is not touched. /// /// @return Integer value at the given index or -1. -varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) +varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) FUNC_ATTR_WARN_UNUSED_RESULT { const listitem_T *const li = tv_list_find(l, n); @@ -744,16 +744,16 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *ret_error) return tv_get_number_chk(&li->li_tv, ret_error); } -/// Get list item l[n - 1] as a string +/// Get list item l[n] as a string /// /// @param[in] l List to index. /// @param[in] n Index in a list. /// -/// @return [allocated] Copy of the list item string value. -const char *tv_list_find_str(list_T *l, int n) - FUNC_ATTR_MALLOC +/// @return List item string value or NULL in case of error. +const char *tv_list_find_str(list_T *const l, const int n) + FUNC_ATTR_WARN_UNUSED_RESULT { - const listitem_T *const li = tv_list_find(l, n - 1); + const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { EMSGN(_(e_listidx), n); return NULL; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 73b81ac2d9..26cfec991f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8424,7 +8424,7 @@ eval_vars ( return NULL; } result = (char_u *)tv_list_find_str(get_vim_var_list(VV_OLDFILES), - (long)i); + i - 1); if (result == NULL) { *errormsg = (char_u *)""; return NULL; diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index eea5cc8880..cd85b143d9 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -405,7 +405,13 @@ local alloc_logging_helpers = { freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, } +local function int(n) + return {[type_key]=int_type, value=n} +end + return { + int=int, + null_string=null_string, null_list=null_list, null_dict=null_dict, diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 8740e9eb1f..94ee394009 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -12,6 +12,7 @@ local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers +local int = eval_helpers.int local list = eval_helpers.list local lst2tbl = eval_helpers.lst2tbl local typvalt = eval_helpers.typvalt @@ -1134,5 +1135,194 @@ describe('typval.c', function() eq(false, lib.tv_list_equal(l1, l9, true, false)) end) end) + describe('find', function() + describe('()', function() + itp('correctly indexes list', function() + local l = list(1, 2, 3, 4, 5) + local lis = list_items(l) + clear_alloc_log() + + eq(nil, lib.tv_list_find(nil, -1)) + eq(nil, lib.tv_list_find(nil, 0)) + eq(nil, lib.tv_list_find(nil, 1)) + + eq(nil, lib.tv_list_find(l, 5)) + eq(nil, lib.tv_list_find(l, -6)) + eq(lis[1], lib.tv_list_find(l, -5)) + eq(lis[5], lib.tv_list_find(l, 4)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + + l.lv_idx_item = nil + eq(lis[1], lib.tv_list_find(l, -5)) + l.lv_idx_item = nil + eq(lis[5], lib.tv_list_find(l, 4)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, -3)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, -3)) + + l.lv_idx_item = nil + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[1], lib.tv_list_find(l, -5)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[5], lib.tv_list_find(l, 4)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, 2)) + eq(lis[3], lib.tv_list_find(l, -3)) + + check_alloc_log({}) + end) + end) + local function check_emsg(f, msg) + local saved_last_msg_hist = lib.last_msg_hist + local ret = {f()} + if msg ~= nil then + neq(saved_last_msg_hist, lib.last_msg_hist) + eq(msg, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end + return unpack(ret) + end + describe('nr()', function() + local function tv_list_find_nr(l, n, msg) + return check_emsg(function() + local err = ffi.new('bool[1]', {false}) + local ret = lib.tv_list_find_nr(l, n, err) + return (err[0] == true), ret + end, msg) + end + it('returns correct number', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq({false, 1}, {tv_list_find_nr(l, -5)}) + eq({false, 5}, {tv_list_find_nr(l, 4)}) + eq({false, 3}, {tv_list_find_nr(l, 2)}) + eq({false, 3}, {tv_list_find_nr(l, -3)}) + + check_alloc_log({}) + end) + it('returns correct number when given a string', function() + local l = list('1', '2', '3', '4', '5') + clear_alloc_log() + + eq({false, 1}, {tv_list_find_nr(l, -5)}) + eq({false, 5}, {tv_list_find_nr(l, 4)}) + eq({false, 3}, {tv_list_find_nr(l, 2)}) + eq({false, 3}, {tv_list_find_nr(l, -3)}) + + check_alloc_log({}) + end) + it('returns zero when given a NULL string', function() + local l = list(null_string) + clear_alloc_log() + + eq({false, 0}, {tv_list_find_nr(l, 0)}) + + check_alloc_log({}) + end) + it('errors out on NULL lists', function() + eq({true, -1}, {tv_list_find_nr(nil, -5)}) + eq({true, -1}, {tv_list_find_nr(nil, 4)}) + eq({true, -1}, {tv_list_find_nr(nil, 2)}) + eq({true, -1}, {tv_list_find_nr(nil, -3)}) + + check_alloc_log({}) + end) + it('errors out on out-of-range indexes', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq({true, -1}, {tv_list_find_nr(l, -6)}) + eq({true, -1}, {tv_list_find_nr(l, 5)}) + + check_alloc_log({}) + end) + it('errors out on invalid types', function() + local l = list(1, empty_list, {}) + + eq({true, 0}, {tv_list_find_nr(l, 0, 'E805: Using a Float as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, 1, 'E745: Using a List as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, 2, 'E728: Using a Dictionary as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -1, 'E728: Using a Dictionary as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -2, 'E745: Using a List as a Number')}) + eq({true, 0}, {tv_list_find_nr(l, -3, 'E805: Using a Float as a Number')}) + end) + end) + local function tv_list_find_str(l, n, msg) + return check_emsg(function() + local ret = lib.tv_list_find_str(l, n) + local s = nil + if ret ~= nil then + s = ffi.string(ret) + end + return s + end, msg) + end + describe('str()', function() + it('returns correct string', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + clear_alloc_log() + + eq('1', tv_list_find_str(l, -5)) + eq('5', tv_list_find_str(l, 4)) + eq('3', tv_list_find_str(l, 2)) + eq('3', tv_list_find_str(l, -3)) + + check_alloc_log({}) + end) + it('returns string when used with VAR_STRING items', function() + local l = list('1', '2', '3', '4', '5') + clear_alloc_log() + + eq('1', tv_list_find_str(l, -5)) + eq('5', tv_list_find_str(l, 4)) + eq('3', tv_list_find_str(l, 2)) + eq('3', tv_list_find_str(l, -3)) + + check_alloc_log({}) + end) + it('returns empty when used with NULL string', function() + local l = list(null_string) + clear_alloc_log() + + eq('', tv_list_find_str(l, 0)) + + check_alloc_log({}) + end) + it('fails with error message when index is out of range', function() + local l = list(int(1), int(2), int(3), int(4), int(5)) + + eq(nil, tv_list_find_str(l, -6, 'E684: list index out of range: -6')) + eq(nil, tv_list_find_str(l, 5, 'E684: list index out of range: 5')) + end) + it('fails with error message on invalid types', function() + local l = list(1, empty_list, {}) + + eq('', tv_list_find_str(l, 0, 'E806: using Float as a String')) + eq('', tv_list_find_str(l, 1, 'E730: using List as a String')) + eq('', tv_list_find_str(l, 2, 'E731: using Dictionary as a String')) + eq('', tv_list_find_str(l, -1, 'E731: using Dictionary as a String')) + eq('', tv_list_find_str(l, -2, 'E730: using List as a String')) + eq('', tv_list_find_str(l, -3, 'E806: using Float as a String')) + end) + end) + end) end) end) -- cgit From 56e51033abf00d66e9c6f9412e8f57c9a24b86ae Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:07:34 +0300 Subject: unittests: Add tests for tv_list_idx_of_item --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index ef64467ee5..4adc31d10a 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -774,7 +774,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) return -1; } long idx = 0; - listitem_T *li; + const listitem_T *li; for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { idx++; } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 94ee394009..d308ee5794 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1324,5 +1324,21 @@ describe('typval.c', function() end) end) end) + describe('idx_of_item()', function() + it('works', function() + local l = list(1, 2, 3, 4, 5) + local l2 = list(42, empty_list) + local lis = list_items(l) + local lis2 = list_items(l2) + + for i, li in ipairs(lis) do + eq(i - 1, lib.tv_list_idx_of_item(l, li)) + end + eq(-1, lib.tv_list_idx_of_item(l, lis2[1])) + eq(-1, lib.tv_list_idx_of_item(l, nil)) + eq(-1, lib.tv_list_idx_of_item(nil, nil)) + eq(-1, lib.tv_list_idx_of_item(nil, lis[1])) + end) + end) end) end) -- cgit From 9ed9af7e11e3a707f65abfeb1d02b029e39241ea Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 00:26:33 +0300 Subject: eval/typval: More `const` qualifiers in `tv_dict*` function signatures --- src/nvim/eval/typval.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4adc31d10a..57025250d4 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1096,7 +1096,7 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, /// @param[in] key Key to find in dictionary. /// /// @return Dictionary item. -varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) +varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { dictitem_T *const di = tv_dict_find(d, key, -1); @@ -1116,7 +1116,7 @@ varnumber_T tv_dict_get_number(dict_T *const d, const char *const key) /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. If returned value is not NULL, it may /// be allocated depending on `save` argument. -char *tv_dict_get_string(dict_T *const d, const char *const key, +char *tv_dict_get_string(const dict_T *const d, const char *const key, const bool save) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -1136,11 +1136,11 @@ char *tv_dict_get_string(dict_T *const d, const char *const key, /// /// @return NULL if key does not exist, empty string in case of type error, /// string item value otherwise. -const char *tv_dict_get_string_buf(dict_T *const d, const char *const key, +const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, char *const numbuf) FUNC_ATTR_WARN_UNUSED_RESULT { - dictitem_T *const di = tv_dict_find(d, key, -1); + const dictitem_T *const di = tv_dict_find(d, key, -1); if (di == NULL) { return NULL; } -- cgit From 4bcee963471abd939bb9edd1709418e30be7290f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 01:03:44 +0300 Subject: *: Fix some Windows-specific warnings Also fixed an error in path_fnamecmp(). --- src/nvim/api/buffer.c | 4 +- src/nvim/event/rstream.c | 10 +++- src/nvim/ex_getln.c | 2 +- src/nvim/file_search.c | 5 +- src/nvim/fileio.c | 2 +- src/nvim/indent_c.c | 2 +- src/nvim/os/env.c | 1 + src/nvim/os/pty_process_win.h | 5 +- src/nvim/path.c | 26 ++++----- test/unit/eval/typval_spec.lua | 130 ++++++++++++++++++++++++++++------------- 10 files changed, 122 insertions(+), 65 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index b75a2c7211..037a6ee1da 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -395,10 +395,10 @@ void nvim_buf_set_lines(uint64_t channel_id, mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra); } - changed_lines((linenr_T)start, 0, (linenr_T)end, extra); + changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); if (save_curbuf.br_buf == NULL) { - fix_cursor((linenr_T)start, (linenr_T)end, extra); + fix_cursor((linenr_T)start, (linenr_T)end, (linenr_T)extra); } end: diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 92efc9fa2e..2737dad305 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -89,7 +89,10 @@ static void on_rbuffer_nonfull(RBuffer *buf, void *data) static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { Stream *stream = handle->data; - buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + buf->base = rbuffer_write_ptr(stream->buffer, &write_count); + buf->len = write_count; } // Callback invoked by libuv after it copies the data into the buffer provided @@ -136,7 +139,10 @@ static void fread_idle_cb(uv_idle_t *handle) uv_fs_t req; Stream *stream = handle->data; - stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); + // `uv_buf_t.len` happens to have different size on Windows. + size_t write_count; + stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); + stream->uvbuf.len = write_count; // the offset argument to uv_fs_read is int64_t, could someone really try // to read more than 9 quintillion (9e18) bytes? diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a0981a42ce..e140dfa886 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -579,7 +579,7 @@ static int command_line_execute(VimState *state, int key) } if (vim_ispathsep(ccline.cmdbuff[s->j]) #ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) + && strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) == NULL #endif ) { diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index f7932bc296..9592235905 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -322,8 +322,11 @@ vim_findfile_init ( drive[0] = path[0]; drive[1] = ':'; drive[2] = NUL; - if (vim_FullName(drive, ff_expand_buffer, MAXPATHL, TRUE) == FAIL) + if (vim_FullName((const char *)drive, (char *)ff_expand_buffer, MAXPATHL, + true) + == FAIL) { goto error_return; + } path += 2; } else #endif diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 127efda65c..bd632b2755 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5207,7 +5207,7 @@ void forward_slash(char_u *fname) { char_u *p; - if (path_with_url(fname)) { + if (path_with_url((const char *)fname)) { return; } for (p = fname; *p != NUL; p++) { diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 7b758b4dac..4a73fbaf61 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -174,7 +174,7 @@ static char_u *skip_string(char_u *p) char_u *paren = vim_strchr(delim, '('); if (paren != NULL) { - ptrdiff_t delim_len = paren - delim; + const ptrdiff_t delim_len = paren - delim; for (p += 3; *p; ++p) if (p[0] == ')' && STRNCMP(p + 1, delim, delim_len) == 0 diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index a73d753e46..ae69462055 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,6 +18,7 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" +#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 20cc589925..8e2b37a1c1 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -12,8 +12,9 @@ typedef struct pty_process { #define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) #define pty_process_close(job) libuv_process_close((LibuvProcess *)job) #define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) -#define pty_process_teardown(loop) +#define pty_process_resize(job, width, height) ( \ + (void)job, (void)width, (void)height, 0) +#define pty_process_teardown(loop) ((void)loop, 0) static inline PtyProcess pty_process_init(Loop *loop, void *data) { diff --git a/src/nvim/path.c b/src/nvim/path.c index e92261f4fd..d0248690d9 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -312,7 +312,7 @@ int path_fnamecmp(const char *fname1, const char *fname2) /// /// @return 0 if they are equal, non-zero otherwise. int path_fnamencmp(const char *const fname1, const char *const fname2, - const size_t len) + size_t len) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { #ifdef BACKSLASH_IN_FILENAME @@ -322,16 +322,16 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, const char *p1 = fname1; const char *p2 = fname2; while (len > 0) { - c1 = PTR2CHAR(p1); - c2 = PTR2CHAR(p2); - if (c1 == NUL || c2 == NUL - || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))) - || (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { + c1 = PTR2CHAR((const char_u *)p1); + c2 = PTR2CHAR((const char_u *)p2); + if ((c1 == NUL || c2 == NUL + || (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/')))) + && (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; } - len -= MB_PTR2LEN(p1); - p1 += MB_PTR2LEN(p1); - p2 += MB_PTR2LEN(p2); + len -= MB_PTR2LEN((const char_u *)p1); + p1 += MB_PTR2LEN((const char_u *)p1); + p2 += MB_PTR2LEN((const char_u *)p2); } return c1 - c2; #else @@ -819,7 +819,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) } STRMOVE(buf + len + 1, buf); STRCPY(buf, curdir); - buf[len] = PATHSEP; + buf[len] = (char_u)PATHSEP; simplify_filename(buf); } @@ -1333,12 +1333,12 @@ static int expand_backtick( /// When the path looks like a URL leave it unmodified. void slash_adjust(char_u *p) { - if (path_with_url(p)) { + if (path_with_url((const char *)p)) { return; } while (*p) { - if (*p == psepcN) { - *p = psepc; + if (*p == (char_u)psepcN) { + *p = (char_u)psepc; } mb_ptr_adv(p); } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d308ee5794..a785350c15 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -294,8 +294,8 @@ describe('typval.c', function() end) end) -- add() and fix() were tested when testing tv_list_item_remove() - describe('alloc()/free()', function() - itp('recursively frees list with', function() + describe('free()', function() + itp('recursively frees list', function() local l1 = ffi.gc(list(1, 'abc'), nil) local l2 = ffi.gc(list({}), nil) local l3 = ffi.gc(list(empty_list), nil) @@ -332,27 +332,73 @@ describe('typval.c', function() alloc_rets:freed(8), }) end) - itp('does not free container items with recurse=false', function() - local l1 = ffi.gc(list('abc', {}, empty_list), nil) + end) + describe('free_list()', function() + itp('does not free list contents', function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) local alloc_rets = {} alloc_log:check(get_alloc_rets({ a.list(l1), - a.str(l1.lv_first.li_tv.vval.v_string, #('abc')), a.li(l1.lv_first), - a.dict(l1.lv_first.li_next.li_tv.vval.v_dict), - a.li(l1.lv_first.li_next), - a.list(l1.lv_last.li_tv.vval.v_list), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), }, alloc_rets)) - lib.tv_list_free(l1) + lib.tv_list_free_list(l1) + alloc_log:check({ + alloc_rets:freed(1), + }) + lib.tv_list_free_list(l2) + alloc_log:check({ + alloc_rets:freed(5), + }) + lib.tv_list_free_list(l3) + alloc_log:check({ + alloc_rets:freed(8), + }) + end) + end) + describe('free_contents()', function() + itp('recursively frees list, except for the list structure itself', + function() + local l1 = ffi.gc(list(1, 'abc'), nil) + local l2 = ffi.gc(list({}), nil) + local l3 = ffi.gc(list(empty_list), nil) + local alloc_rets = {} + alloc_log:check(get_alloc_rets({ + a.list(l1), + a.li(l1.lv_first), + a.str(l1.lv_last.li_tv.vval.v_string, #('abc')), + a.li(l1.lv_last), + a.list(l2), + a.dict(l2.lv_first.li_tv.vval.v_dict), + a.li(l2.lv_first), + a.list(l3), + a.list(l3.lv_first.li_tv.vval.v_list), + a.li(l3.lv_first), + }, alloc_rets)) + lib.tv_list_free_contents(l1) alloc_log:check({ alloc_rets:freed(2), alloc_rets:freed(3), alloc_rets:freed(4), - alloc_rets:freed(5), + }) + lib.tv_list_free_contents(l2) + alloc_log:check({ alloc_rets:freed(6), alloc_rets:freed(7), - alloc_rets:freed(1), + }) + lib.tv_list_free_contents(l3) + alloc_log:check({ + alloc_rets:freed(9), + alloc_rets:freed(10), }) end) end) @@ -1063,8 +1109,8 @@ describe('typval.c', function() local recursive_l = l.lv_first.li_tv.vval.v_list local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) - lib.tv_list_free(l, true) - end) + lib.tv_list_free(l) + end, true) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() @@ -1140,7 +1186,7 @@ describe('typval.c', function() itp('correctly indexes list', function() local l = list(1, 2, 3, 4, 5) local lis = list_items(l) - clear_alloc_log() + alloc_log:clear() eq(nil, lib.tv_list_find(nil, -1)) eq(nil, lib.tv_list_find(nil, 0)) @@ -1185,7 +1231,7 @@ describe('typval.c', function() eq(lis[3], lib.tv_list_find(l, 2)) eq(lis[3], lib.tv_list_find(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) end) local function check_emsg(f, msg) @@ -1207,54 +1253,54 @@ describe('typval.c', function() return (err[0] == true), ret end, msg) end - it('returns correct number', function() + itp('returns correct number', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq({false, 1}, {tv_list_find_nr(l, -5)}) eq({false, 5}, {tv_list_find_nr(l, 4)}) eq({false, 3}, {tv_list_find_nr(l, 2)}) eq({false, 3}, {tv_list_find_nr(l, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns correct number when given a string', function() + itp('returns correct number when given a string', function() local l = list('1', '2', '3', '4', '5') - clear_alloc_log() + alloc_log:clear() eq({false, 1}, {tv_list_find_nr(l, -5)}) eq({false, 5}, {tv_list_find_nr(l, 4)}) eq({false, 3}, {tv_list_find_nr(l, 2)}) eq({false, 3}, {tv_list_find_nr(l, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns zero when given a NULL string', function() + itp('returns zero when given a NULL string', function() local l = list(null_string) - clear_alloc_log() + alloc_log:clear() eq({false, 0}, {tv_list_find_nr(l, 0)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on NULL lists', function() + itp('errors out on NULL lists', function() eq({true, -1}, {tv_list_find_nr(nil, -5)}) eq({true, -1}, {tv_list_find_nr(nil, 4)}) eq({true, -1}, {tv_list_find_nr(nil, 2)}) eq({true, -1}, {tv_list_find_nr(nil, -3)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on out-of-range indexes', function() + itp('errors out on out-of-range indexes', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq({true, -1}, {tv_list_find_nr(l, -6)}) eq({true, -1}, {tv_list_find_nr(l, 5)}) - check_alloc_log({}) + alloc_log:check({}) end) - it('errors out on invalid types', function() + itp('errors out on invalid types', function() local l = list(1, empty_list, {}) eq({true, 0}, {tv_list_find_nr(l, 0, 'E805: Using a Float as a Number')}) @@ -1276,43 +1322,43 @@ describe('typval.c', function() end, msg) end describe('str()', function() - it('returns correct string', function() + itp('returns correct string', function() local l = list(int(1), int(2), int(3), int(4), int(5)) - clear_alloc_log() + alloc_log:clear() eq('1', tv_list_find_str(l, -5)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns string when used with VAR_STRING items', function() + itp('returns string when used with VAR_STRING items', function() local l = list('1', '2', '3', '4', '5') - clear_alloc_log() + alloc_log:clear() eq('1', tv_list_find_str(l, -5)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - check_alloc_log({}) + alloc_log:check({}) end) - it('returns empty when used with NULL string', function() + itp('returns empty when used with NULL string', function() local l = list(null_string) - clear_alloc_log() + alloc_log:clear() eq('', tv_list_find_str(l, 0)) - check_alloc_log({}) + alloc_log:check({}) end) - it('fails with error message when index is out of range', function() + itp('fails with error message when index is out of range', function() local l = list(int(1), int(2), int(3), int(4), int(5)) eq(nil, tv_list_find_str(l, -6, 'E684: list index out of range: -6')) eq(nil, tv_list_find_str(l, 5, 'E684: list index out of range: 5')) end) - it('fails with error message on invalid types', function() + itp('fails with error message on invalid types', function() local l = list(1, empty_list, {}) eq('', tv_list_find_str(l, 0, 'E806: using Float as a String')) @@ -1325,7 +1371,7 @@ describe('typval.c', function() end) end) describe('idx_of_item()', function() - it('works', function() + itp('works', function() local l = list(1, 2, 3, 4, 5) local l2 = list(42, empty_list) local lis = list_items(l) -- cgit From 5239616297182601a5d244a482e6cef307e901f1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 5 Nov 2016 21:34:22 +0300 Subject: functests: Fix buf_functions test on Windows --- test/functional/eval/buf_functions_spec.lua | 6 ++++-- test/functional/helpers.lua | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/functional/eval/buf_functions_spec.lua b/test/functional/eval/buf_functions_spec.lua index a130da4452..db50874c53 100644 --- a/test/functional/eval/buf_functions_spec.lua +++ b/test/functional/eval/buf_functions_spec.lua @@ -13,6 +13,7 @@ local winmeths = helpers.winmeths local curbufmeths = helpers.curbufmeths local curwinmeths = helpers.curwinmeths local curtabmeths = helpers.curtabmeths +local get_pathsep = helpers.get_pathsep local fname = 'Xtest-functional-eval-buf_functions' local fname2 = fname .. '.2' @@ -66,14 +67,15 @@ describe('bufname() function', function() eq('', funcs.bufname('%')) -- Buffer has no name yet command('file ' .. fname) local wd = lfs.currentdir() + local sep = get_pathsep() local curdirname = funcs.fnamemodify(wd, ':t') for _, arg in ipairs({'%', 1, 'X', wd}) do eq(fname, funcs.bufname(arg)) meths.set_current_dir('..') - eq(curdirname .. '/' .. fname, funcs.bufname(arg)) + eq(curdirname .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir(curdirname) meths.set_current_dir(dirname) - eq(wd .. '/' .. fname, funcs.bufname(arg)) + eq(wd .. sep .. fname, funcs.bufname(arg)) meths.set_current_dir('..') eq(fname, funcs.bufname(arg)) command('enew') diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 13a0cff137..7ce95d0b7c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -570,6 +570,10 @@ local curbufmeths = create_callindex(curbuf) local curwinmeths = create_callindex(curwin) local curtabmeths = create_callindex(curtab) +local function get_pathsep() + return funcs.fnamemodify('.', ':p'):sub(-1) +end + local M = { prepend_argv = prepend_argv, clear = clear, @@ -635,6 +639,7 @@ local M = { tmpname = tmpname, meth_pcall = meth_pcall, NIL = mpack.NIL, + get_pathsep = get_pathsep, } return function(after_each) -- cgit From 1e3e302dc2bdced563ecd2c3fb101af31f72b3df Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 21:58:19 +0300 Subject: eval: Move part of dictwatcher* functions to eval/typval --- src/nvim/eval.c | 57 +++++------------------- src/nvim/eval/typval.c | 89 +++++++++++++++++++++++++++++++++++++- src/nvim/eval/typval.h | 27 ++++++------ src/nvim/os/env.c | 1 - test/functional/eval/null_spec.lua | 3 +- 5 files changed, 112 insertions(+), 65 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index d5624f354a..80278cf3bb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7706,6 +7706,11 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_DICT) { emsgf(_(e_invarg2), "dict"); return; + } else if (argvars[0].vval.v_dict == NULL) { + const char *const arg_errmsg = _("dictwatcheradd() argument"); + const size_t arg_errmsg_len = strlen(arg_errmsg); + emsgf(_(e_readonlyvar), (int)arg_errmsg_len, arg_errmsg); + return; } if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_NUMBER) { @@ -7725,11 +7730,8 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - DictWatcher *watcher = xmalloc(sizeof(DictWatcher)); - watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); - watcher->callback = callback; - watcher->busy = false; - QUEUE_INSERT_TAIL(&argvars[0].vval.v_dict->watchers, &watcher->node); + tv_dict_watcher_add(argvars[0].vval.v_dict, key_pattern, key_pattern_len, + callback); } // dictwatcherdel(dict, key, funcref) function @@ -7759,28 +7761,12 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - dict_T *dict = argvars[0].vval.v_dict; - QUEUE *w = NULL; - DictWatcher *watcher = NULL; - bool matched = false; - QUEUE_FOREACH(w, &dict->watchers) { - watcher = tv_dict_watcher_node_data(w); - if (callback_equal(&watcher->callback, &callback) - && !strcmp(watcher->key_pattern, key_pattern)) { - matched = true; - break; - } - } - - callback_free(&callback); - - if (!matched) { + if (!tv_dict_watcher_remove(argvars[0].vval.v_dict, key_pattern, + strlen(key_pattern), callback)) { EMSG("Couldn't find a watcher matching key and callback"); - return; } - QUEUE_REMOVE(w); - tv_dict_watcher_free(watcher); + callback_free(&callback); } /* @@ -16576,7 +16562,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } - /// Unref/free callback void callback_free(Callback *const callback) FUNC_ATTR_NONNULL_ALL @@ -16601,28 +16586,6 @@ void callback_free(Callback *const callback) callback->type = kCallbackNone; } -static bool callback_equal(Callback *cb1, Callback *cb2) -{ - if (cb1->type != cb2->type) { - return false; - } - switch (cb1->type) { - case kCallbackFuncref: - 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? - return cb1->data.partial == cb2->data.partial; - - case kCallbackNone: - return true; - - default: - abort(); - } -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 57025250d4..243e738c50 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -790,7 +790,7 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) /// Perform all necessary cleanup for a `DictWatcher` instance /// /// @param watcher Watcher to free. -void tv_dict_watcher_free(DictWatcher *watcher) +static void tv_dict_watcher_free(DictWatcher *watcher) FUNC_ATTR_NONNULL_ALL { callback_free(&watcher->callback); @@ -798,6 +798,91 @@ void tv_dict_watcher_free(DictWatcher *watcher) xfree(watcher); } +/// Add watcher to a dictionary +/// +/// @param[in] dict Dictionary to add watcher to. +/// @param[in] key_pattern Pattern to watch for. +/// @param[in] key_pattern_len Key pattern length. +/// @param callback Function to be called on events. +void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); + watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); + watcher->key_pattern_len = key_pattern_len; + watcher->callback = callback; + watcher->busy = false; + QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node); +} + +/// Check whether two callbacks are equal +/// +/// @param[in] cb1 First callback to check. +/// @param[in] cb2 Second callback to check. +/// +/// @return True if they are equal, false otherwise. +static bool tv_callback_equal(const Callback *const cb1, + const Callback *const cb2) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (cb1->type != cb2->type) { + return false; + } + switch (cb1->type) { + case kCallbackFuncref: { + 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? + return cb1->data.partial == cb2->data.partial; + } + case kCallbackNone: { + return true; + } + } +} + +/// Remove watcher from a dictionary +/// +/// @param dict Dictionary to remove watcher from. +/// @param[in] key_pattern Pattern to remove watcher for. +/// @param[in] key_pattern_len Pattern length. +/// @param callback Callback to remove watcher for. +/// +/// @return True on success, false if relevant watcher was not found. +bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern, + const size_t key_pattern_len, + Callback callback) + FUNC_ATTR_NONNULL_ARG(2) +{ + if (dict == NULL) { + return false; + } + + QUEUE *w = NULL; + DictWatcher *watcher = NULL; + bool matched = false; + QUEUE_FOREACH(w, &dict->watchers) { + watcher = tv_dict_watcher_node_data(w); + if (tv_callback_equal(&watcher->callback, &callback) + && watcher->key_pattern_len == key_pattern_len + && memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) { + matched = true; + break; + } + } + + if (!matched) { + return false; + } + + QUEUE_REMOVE(w); + tv_dict_watcher_free(watcher); + return true; +} + /// Test if `key` matches with with `watcher->key_pattern` /// /// @param[in] watcher Watcher to check key pattern from. @@ -810,7 +895,7 @@ static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) // For now only allow very simple globbing in key patterns: a '*' at the end // of the string means it should match everything up to the '*' instead of the // whole string. - const size_t len = strlen(watcher->key_pattern); + 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 { diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 791b191009..fa0105197f 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -59,6 +59,7 @@ typedef struct { typedef struct dict_watcher { Callback callback; char *key_pattern; + size_t key_pattern_len; QUEUE node; bool busy; // prevent recursion if the dict is changed in the callback } DictWatcher; @@ -322,19 +323,6 @@ static inline bool tv_dict_is_watched(const dict_T *const d) return d && !QUEUE_EMPTY(&d->watchers); } -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) - REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE - REAL_FATTR_WARN_UNUSED_RESULT; - -/// Compute the `DictWatcher` address from a QUEUE node. -/// -/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer -/// arithmetic). -static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) -{ - return QUEUE_DATA(q, DictWatcher, node); -} - /// Initialize VimL object /// /// Initializes to unlocked VAR_UNKNOWN object. @@ -407,6 +395,19 @@ static inline bool tv_get_float_chk(const typval_T *const tv, return false; } +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) + REAL_FATTR_NONNULL_ALL REAL_FATTR_NONNULL_RET REAL_FATTR_PURE + REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_ALWAYS_INLINE; + +/// Compute the `DictWatcher` address from a QUEUE node. +/// +/// This only exists for .asan-blacklist (ASAN doesn't handle QUEUE_DATA pointer +/// arithmetic). +static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, DictWatcher, node); +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index ae69462055..a73d753e46 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -18,7 +18,6 @@ #include "nvim/eval.h" #include "nvim/ex_getln.h" #include "nvim/version.h" -#include "nvim/fileio.h" #ifdef WIN32 #include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index e587ede319..f72d05f2a3 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -80,8 +80,7 @@ describe('NULL', function() null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - -- FIXME should not error out - null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 'Vim(for):E714: List required') + null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 0) -- Subjectable behaviour -- cgit From a56f2d27e3c09aaae00a58a70652ac5db3287dee Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:35:10 +0300 Subject: eval: Make dictionary watchers work with empty keys Looks like dict_notifications_spec test used to depend on some state which should not be preserved. Changed all `setup()` calls to `before_each()` and added necessary state in addition to changes required to test empty keys. Note: unit tests for tv_dict_watcher* are still needed. --- src/nvim/eval.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 80278cf3bb..bd88678f16 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -160,7 +160,6 @@ static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( "E712: Argument of %s must be a List or Dictionary"); -static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary"); static char *e_listreq = N_("E714: List required"); static char *e_dictreq = N_("E715: Dictionary required"); static char *e_stringreq = N_("E928: String required"); @@ -2112,8 +2111,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) ; if (len == 0) { - if (!quiet) - EMSG(_(e_emptykey)); + if (!quiet) { + EMSG(_("E713: Cannot use empty key after .")); + } return NULL; } p = key + len; -- cgit From 3025431c81daac873e69a71aee695ebfd00504f7 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 4 Dec 2016 22:59:25 +0300 Subject: eval: Make sure that v:_null_dict does not crash dictwatcher*() Ref #4615 --- src/nvim/eval/typval.c | 3 +++ .../functional/ex_cmds/dict_notifications_spec.lua | 24 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 243e738c50..8c11f4bd8f 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -808,6 +808,9 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, const size_t key_pattern_len, Callback callback) FUNC_ATTR_NONNULL_ARG(2) { + if (dict == NULL) { + return; + } DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); watcher->key_pattern_len = key_pattern_len; diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index 8cc717483e..e3b4a1c504 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -244,6 +244,16 @@ describe('dictionary change notifications', function() end) end) + it('errors out when adding to v:_null_dict', function() + command([[ + function! g:Watcher1(dict, key, value) + call rpcnotify(g:channel, '1', a:key, a:value) + endfunction + ]]) + eq('Vim(call):E46: Cannot change read-only variable "dictwatcheradd() argument"', + exc_exec('call dictwatcheradd(v:_null_dict, "x", "g:Watcher1")')) + end) + describe('errors', function() before_each(function() source([[ @@ -272,6 +282,20 @@ describe('dictionary change notifications', function() command('call dictwatcherdel(g:, "key", "g:InvalidCb")') end) + it('fails to remove watcher from v:_null_dict', function() + eq("Vim(call):Couldn't find a watcher matching key and callback", + exc_exec('call dictwatcherdel(v:_null_dict, "x", "g:Watcher2")')) + end) + + --[[ + [ it("fails to add/remove if the callback doesn't exist", function() + [ eq("Vim(call):Function g:InvalidCb doesn't exist", + [ exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")')) + [ eq("Vim(call):Function g:InvalidCb doesn't exist", + [ exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")')) + [ end) + ]] + it('does not fail to replace a watcher function', function() source([[ let g:key = 'v2' -- cgit From 506b938947b7083fc8bef20eabc08ed033298add Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 14 Feb 2017 00:12:57 +0300 Subject: *: Make some more things const and with length --- src/nvim/buffer.c | 2 +- src/nvim/eval.c | 197 +++++++++++++++++++++++++------------------------ src/nvim/eval/typval.c | 2 +- src/nvim/ex_cmds2.c | 6 +- src/nvim/option.c | 2 +- src/nvim/syntax.c | 19 ++--- 6 files changed, 116 insertions(+), 112 deletions(-) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0fb9a21354..c9101c5b53 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3504,7 +3504,7 @@ int build_stl_str_hl( curbuf = o_curbuf; // Remove the variable we just stored - do_unlet((char_u *)"g:actual_curbuf", true); + do_unlet(S_LEN("g:actual_curbuf"), true); // } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bd88678f16..67f6c8ba3a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -137,25 +137,24 @@ * "newkey" is the key for the new item. */ typedef struct lval_S { - char_u *ll_name; /* start of variable name (can be NULL) */ - char_u *ll_exp_name; /* NULL or expanded name in allocated memory. */ - typval_T *ll_tv; /* Typeval of item being used. If "newkey" - isn't NULL it's the Dict to which to add - the item. */ - listitem_T *ll_li; /* The list item or NULL. */ - list_T *ll_list; /* The list or NULL. */ - int ll_range; /* TRUE when a [i:j] range was used */ - long ll_n1; /* First index for list */ - long ll_n2; /* Second index for list range */ - int ll_empty2; /* Second index is empty: [i:] */ - dict_T *ll_dict; /* The Dictionary or NULL */ - dictitem_T *ll_di; /* The dictitem or NULL */ - char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ + const char *ll_name; ///< Start of variable name (can be NULL). + size_t ll_name_len; ///< Length of the .ll_name. + char *ll_exp_name; ///< NULL or expanded name in allocated memory. + typval_T *ll_tv; ///< Typeval of item being used. If "newkey" + ///< isn't NULL it's the Dict to which to add the item. + listitem_T *ll_li; ///< The list item or NULL. + list_T *ll_list; ///< The list or NULL. + int ll_range; ///< TRUE when a [i:j] range was used. + long ll_n1; ///< First index for list. + long ll_n2; ///< Second index for list range. + int ll_empty2; ///< Second index is empty: [i:]. + dict_T *ll_dict; ///< The Dictionary or NULL. + dictitem_T *ll_di; ///< The dictitem or NULL. + char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL. } lval_T; static char *e_letunexp = N_("E18: Unexpected characters in :let"); -static char *e_undefvar = N_("E121: Undefined variable: %s"); static char *e_missbrac = N_("E111: Missing ']'"); static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_( @@ -729,7 +728,7 @@ void set_internal_string_var(char_u *name, char_u *value) .vval.v_string = value, }; - set_var((const char *)name, (typval_T *)&tv, true); + set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); } static lval_T *redir_lval = NULL; @@ -768,8 +767,8 @@ var_redir_start ( // Parse the variable name (can be a dict or list entry). redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, 0, FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != - NUL) { + if (redir_endp == NULL || redir_lval->ll_name == NULL + || *redir_endp != NUL) { clear_lval(redir_lval); if (redir_endp != NULL && *redir_endp != NUL) /* Trailing characters are present after the variable name */ @@ -2021,15 +2020,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, const int flags, const int fne_flags) FUNC_ATTR_NONNULL_ARG(1, 3) { - char_u *p; - int cc; dictitem_T *v; typval_T var1; typval_T var2; int empty1 = FALSE; listitem_T *ni; - char_u *key = NULL; - int len; hashtab_T *ht; int quiet = flags & GLV_QUIET; @@ -2038,16 +2033,18 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (skip) { // When skipping just find the end of the name. - lp->ll_name = (char_u *)name; - return (char_u *)find_name_end(name, NULL, NULL, FNE_INCL_BR | fne_flags); + lp->ll_name = (const char *)name; + return (char_u *)find_name_end((const char_u *)name, NULL, NULL, + FNE_INCL_BR | fne_flags); } // Find the end of the name. char_u *expr_start; char_u *expr_end; - p = (char_u *)find_name_end(name, - (const char_u **)&expr_start, - (const char_u **)&expr_end, fne_flags); + char_u *p = (char_u *)find_name_end(name, + (const char_u **)&expr_start, + (const char_u **)&expr_end, + fne_flags); if (expr_start != NULL) { /* Don't expand the name when we already know there is an error. */ if (unlet && !ascii_iswhite(*p) && !ends_excmd(*p) @@ -2056,7 +2053,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - lp->ll_exp_name = make_expanded_name(name, expr_start, expr_end, p); + lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, + (char_u *)p); if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2068,22 +2066,22 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } lp->ll_name = lp->ll_exp_name; + lp->ll_name_len = strlen(lp->ll_name); } else { - lp->ll_name = (char_u *)name; + lp->ll_name = (const char *)name; + lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); } - /* Without [idx] or .key we are done. */ - if ((*p != '[' && *p != '.') || lp->ll_name == NULL) + // Without [idx] or .key we are done. + if ((*p != '[' && *p != '.') || lp->ll_name == NULL) { return p; + } - cc = *p; - *p = NUL; - v = find_var((const char *)lp->ll_name, STRLEN(lp->ll_name), &ht, - flags & GLV_NO_AUTOLOAD); + v = find_var(lp->ll_name, lp->ll_name_len, &ht, flags & GLV_NO_AUTOLOAD); if (v == NULL && !quiet) { - EMSG2(_(e_undefvar), lp->ll_name); + emsgf(_("E121: Undefined variable: %.*s"), + (int)lp->ll_name_len, lp->ll_name); } - *p = cc; if (v == NULL) return NULL; @@ -2105,11 +2103,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - len = -1; + int len = -1; + char_u *key; if (*p == '.') { key = p + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) - ; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { + } if (len == 0) { if (!quiet) { EMSG(_("E713: Cannot use empty key after .")); @@ -2380,12 +2379,12 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, STRLEN(lp->ll_name)))) && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { - set_var((const char *)lp->ll_name, &tv, false); + set_var(lp->ll_name, lp->ll_name_len, &tv, false); } tv_clear(&tv); } } else { - set_var((const char *)lp->ll_name, rettv, copy); + set_var(lp->ll_name, lp->ll_name_len, rettv, copy); } *endp = cc; } else if (tv_check_lock(lp->ll_newkey == NULL @@ -2886,17 +2885,17 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) *name_end = NUL; // Normal name or expanded name. - if (do_unlet(lp->ll_name, forceit) == FAIL) { + if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) { ret = FAIL; } *name_end = cc; } else if ((lp->ll_list != NULL && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) + lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name)))) { + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { listitem_T *li; @@ -2906,7 +2905,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, - STRLEN(lp->ll_name))) { + lp->ll_name_len)) { return false; } ll_li = li; @@ -2953,22 +2952,22 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) // TODO(ZyX-I): move to eval/ex_cmds -/* - * "unlet" a variable. Return OK if it existed, FAIL if not. - * When "forceit" is TRUE don't complain if the variable doesn't exist. - */ -int do_unlet(char_u *name, int forceit) +/// unlet a variable +/// +/// @param[in] name Variable name to unlet. +/// @param[in] name_len Variable name length. +/// @param[in] fonceit If true, do not complain if variable doesn’t exist. +/// +/// @return OK if it existed, FAIL otherwise. +int do_unlet(const char *const name, const size_t name_len, const int forceit) + FUNC_ATTR_NONNULL_ALL { - hashtab_T *ht; - hashitem_T *hi; - char_u *varname; - dict_T *d; - dictitem_T *di; + const char *varname; dict_T *dict; - ht = find_var_ht_dict((const char *)name, STRLEN(name), - (const char **)&varname, &dict); + hashtab_T *ht = find_var_ht_dict(name, name_len, &varname, &dict); if (ht != NULL && *varname != NUL) { + dict_T *d; if (ht == &globvarht) { d = &globvardict; } else if (current_funccal != NULL @@ -2977,19 +2976,19 @@ int do_unlet(char_u *name, int forceit) } else if (ht == &compat_hashtab) { d = &vimvardict; } else { - di = find_var_in_ht(ht, *name, "", 0, false); + dictitem_T *const di = find_var_in_ht(ht, *name, "", 0, false); d = di->di_tv.vval.v_dict; } if (d == NULL) { EMSG2(_(e_intern2), "do_unlet()"); return FAIL; } - hi = hash_find(ht, varname); + hashitem_T *hi = hash_find(ht, (const char_u *)varname); if (HASHITEM_EMPTY(hi)) { hi = find_hi_in_scoped_ht((const char *)name, &ht); } if (hi != NULL && !HASHITEM_EMPTY(hi)) { - di = TV_DICT_HI2DI(hi); + dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, STRLEN(name)) || var_check_ro(di->di_flags, (const char *)name, STRLEN(name)) || tv_check_lock(d->dv_lock, (const char *)name, STRLEN(name))) { @@ -3011,7 +3010,7 @@ int do_unlet(char_u *name, int forceit) delete_var(ht, hi); if (watched) { - tv_dict_watcher_notify(dict, (char *)varname, NULL, &oldtv); + tv_dict_watcher_notify(dict, varname, NULL, &oldtv); tv_clear(&oldtv); } return OK; @@ -3041,9 +3040,8 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, if (lp->ll_tv == NULL) { // Normal name or expanded name. - const size_t name_len = (size_t)(name_end - lp->ll_name); dictitem_T *const di = find_var( - (const char *)lp->ll_name, name_len, NULL, + (const char *)lp->ll_name, lp->ll_name_len, NULL, true); if (di == NULL) { ret = FAIL; @@ -6561,8 +6559,9 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } - } else + } else { EMSG(_(e_listreq)); + } } /* @@ -11216,7 +11215,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_trailing)); } else { if (lv.ll_tv == NULL) { - di = find_var((const char *)lv.ll_name, STRLEN(lv.ll_name), NULL, true); + di = find_var((const char *)lv.ll_name, lv.ll_name_len, NULL, true); if (di != NULL) { // Consider a variable locked when: // 1. the variable itself is locked @@ -14351,7 +14350,7 @@ static void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) curbuf = buf; memcpy(bufvarname, "b:", 2); memcpy(bufvarname + 2, varname, varname_len + 1); - set_var(bufvarname, varp, true); + set_var(bufvarname, varname_len + 2, varp, true); xfree(bufvarname); curbuf = save_curbuf; } @@ -14876,7 +14875,7 @@ static void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *const tabvarname = xmalloc(varname_len + 3); memcpy(tabvarname, "t:", 2); memcpy(tabvarname + 2, varname, varname_len + 1); - set_var(tabvarname, varp, true); + set_var(tabvarname, varname_len + 2, varp, true); xfree(tabvarname); // Restore current tabpage. @@ -14944,7 +14943,7 @@ static void setwinvar(typval_T *argvars, typval_T *rettv, int off) char *const winvarname = xmalloc(varname_len + 3); memcpy(winvarname, "w:", 2); memcpy(winvarname + 2, varname, varname_len + 1); - set_var(winvarname, varp, true); + set_var(winvarname, varname_len + 2, varp, true); xfree(winvarname); } } @@ -18766,16 +18765,17 @@ static void list_one_var_a(const char *prefix, const char *name, /// is created. /// /// @param[in] name Variable name to set. +/// @param[in] name_len Length of the variable name. /// @param tv Variable value. /// @param[in] copy True if value in tv is to be copied. -static void set_var(const char *name, typval_T *const tv, const bool copy) +static void set_var(const char *name, const size_t name_len, typval_T *const tv, + const bool copy) FUNC_ATTR_NONNULL_ALL { dictitem_T *v; hashtab_T *ht; dict_T *dict; - const size_t name_len = strlen(name); const char *varname; ht = find_var_ht_dict(name, name_len, &varname, &dict); const bool watched = tv_dict_is_watched(dict); @@ -18799,8 +18799,8 @@ static void set_var(const char *name, typval_T *const tv, const bool copy) typval_T oldtv = TV_INITIAL_VALUE; if (v != NULL) { // existing variable, need to clear the value - if (var_check_ro(v->di_flags, (const char *)name, name_len) - || tv_check_lock(v->di_tv.v_lock, (const char *)name, name_len)) { + if (var_check_ro(v->di_flags, name, name_len) + || tv_check_lock(v->di_tv.v_lock, name, name_len)) { return; } @@ -19852,7 +19852,6 @@ trans_function_name( const char_u *start; const char_u *end; int lead; - char_u sid_buf[20]; int len; lval_T lv; @@ -19938,10 +19937,10 @@ trans_function_name( /* Check if the name is a Funcref. If so, use the value. */ if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name((const char *)lv.ll_exp_name, &len, partial, + len = (int)strlen(lv.ll_exp_name); + name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD); - if (name == lv.ll_exp_name) { + if ((const char *)name == lv.ll_exp_name) { name = NULL; } } else if (!(flags & TFN_NO_DEREF)) { @@ -19966,12 +19965,13 @@ trans_function_name( } if (lv.ll_exp_name != NULL) { - len = (int)STRLEN(lv.ll_exp_name); + len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name - && STRNCMP(lv.ll_name, "s:", 2) == 0) { + && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { /* When there was "s:" already or the name expanded to get a * leading "s:" then remove it. */ lv.ll_name += 2; + lv.ll_name_len -= 2; len -= 2; lead = 2; } @@ -19979,38 +19979,40 @@ trans_function_name( // Skip over "s:" and "g:". if (lead == 2 || (lv.ll_name[0] == 'g' && lv.ll_name[1] == ':')) { lv.ll_name += 2; + lv.ll_name_len -= 2; } - len = (int)(end - lv.ll_name); + len = (int)((const char *)end - lv.ll_name); } - /* - * Copy the function name to allocated memory. - * Accept name() inside a script, translate into 123_name(). - * Accept 123_name() outside a script. - */ - if (skip) - lead = 0; /* do nothing */ - else if (lead > 0) { + size_t sid_buf_len = 0; + char sid_buf[20]; + + // Copy the function name to allocated memory. + // Accept name() inside a script, translate into 123_name(). + // Accept 123_name() outside a script. + if (skip) { + lead = 0; // do nothing + } else if (lead > 0) { lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid((const char *)lv.ll_exp_name)) + if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid((const char *)(*pp))) { // It's "s:" or "". if (current_SID <= 0) { EMSG(_(e_usingsid)); goto theend; } - sprintf((char *)sid_buf, "%" PRId64 "_", (int64_t)current_SID); - lead += (int)STRLEN(sid_buf); + sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + lead += sid_buf_len; } } else if (!(flags & TFN_INT) - && builtin_function((const char *)lv.ll_name, len)) { + && builtin_function(lv.ll_name, lv.ll_name_len)) { EMSG2(_("E128: Function name must start with a capital or \"s:\": %s"), start); goto theend; } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) { - char_u *cp = vim_strchr(lv.ll_name, ':'); + char_u *cp = xmemrchr(lv.ll_name, ':', lv.ll_name_len); if (cp != NULL && cp < end) { EMSG2(_("E884: Function name cannot contain a colon: %s"), start); @@ -20023,10 +20025,11 @@ trans_function_name( name[0] = K_SPECIAL; name[1] = KS_EXTRA; name[2] = (int)KE_SNR; - if (lead > 3) /* If it's "" */ - STRCPY(name + 3, sid_buf); + if (sid_buf_len > 0) { // If it's "" + memcpy(name + 3, sid_buf, sid_buf_len); + } } - memmove(name + lead, lv.ll_name, (size_t)len); + memmove(name + lead, lv.ll_name, len); name[lead + len] = NUL; *pp = (char_u *)end; @@ -21646,7 +21649,7 @@ void var_set_global(const char *const name, typval_T vartv) { funccall_T *const saved_current_funccal = current_funccal; current_funccal = NULL; - set_var(name, &vartv, false); + set_var(name, strlen(name), &vartv, false); current_funccal = saved_current_funccal; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8c11f4bd8f..2f3415a59d 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2041,7 +2041,7 @@ bool tv_islocked(const typval_T *const tv) /// /// @param[in] lock Lock status. /// @param[in] name Variable name, used in the error message. -/// @param[in] use_gettext True if variable name also is to be translated. +/// @param[in] name_len Variable name length. /// /// @return true if variable is locked, false otherwise. bool tv_check_lock(const VarLockStatus lock, const char *const name, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index b84834d351..213641667d 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2256,8 +2256,8 @@ void ex_compiler(exarg_T *eap) } do_cmdline_cmd("command -nargs=* CompilerSet setlocal "); } - do_unlet((char_u *)"g:current_compiler", true); - do_unlet((char_u *)"b:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); + do_unlet(S_LEN("b:current_compiler"), true); snprintf((char *)buf, bufsize, "compiler/%s.vim", eap->arg); if (source_runtime(buf, DIP_ALL) == FAIL) { @@ -2280,7 +2280,7 @@ void ex_compiler(exarg_T *eap) old_cur_comp); xfree(old_cur_comp); } else { - do_unlet((char_u *)"g:current_compiler", true); + do_unlet(S_LEN("g:current_compiler"), true); } } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 8a80c46cf2..9b31e14ea7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2582,7 +2582,7 @@ did_set_string_option ( // The color scheme must have set 'background' back to another // value, that's not what we want here. Disable the color // scheme and set the colors again. - do_unlet((char_u *)"g:colors_name", true); + do_unlet(S_LEN("g:colors_name"), true); free_string_option(p_bg); p_bg = vim_strsave((char_u *)(dark ? "dark" : "light")); check_string_option(&p_bg); diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 3f84b8080f..c0adfb10fc 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3259,9 +3259,10 @@ static void syn_cmd_clear(exarg_T *eap, int syncing) syntax_sync_clear(); else { syntax_clear(curwin->w_s); - if (curwin->w_s == &curwin->w_buffer->b_s) - do_unlet((char_u *)"b:current_syntax", TRUE); - do_unlet((char_u *)"w:current_syntax", TRUE); + if (curwin->w_s == &curwin->w_buffer->b_s) { + do_unlet(S_LEN("b:current_syntax"), true); + } + do_unlet(S_LEN("w:current_syntax"), true); } } else { /* @@ -3337,7 +3338,7 @@ static void syn_cmd_enable(exarg_T *eap, int syncing) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); syn_cmd_onoff(eap, "syntax"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } /* @@ -3350,7 +3351,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) if (!eap->skip) { set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); do_cmdline_cmd("runtime! syntax/syncolor.vim"); - do_unlet((char_u *)"g:syntax_cmd", TRUE); + do_unlet(S_LEN("g:syntax_cmd"), true); } } @@ -5538,9 +5539,9 @@ void ex_ownsyntax(exarg_T *eap) } /* restore value of b:current_syntax */ - if (old_value == NULL) - do_unlet((char_u *)"b:current_syntax", TRUE); - else { + if (old_value == NULL) { + do_unlet(S_LEN("b:current_syntax"), true); + } else { set_internal_string_var((char_u *)"b:current_syntax", old_value); xfree(old_value); } @@ -6231,7 +6232,7 @@ do_highlight ( */ line = linep; if (ends_excmd(*line)) { - do_unlet((char_u *)"colors_name", TRUE); + do_unlet(S_LEN("colors_name"), true); restore_cterm_colors(); /* -- cgit From 6aa6e5007596f1eaa23c45376e3a72d908c6688e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 21:35:23 +0300 Subject: eval: Fix linter errors --- src/nvim/eval.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 67f6c8ba3a..937b56179d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2082,8 +2082,9 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, emsgf(_("E121: Undefined variable: %.*s"), (int)lp->ll_name_len, lp->ll_name); } - if (v == NULL) + if (v == NULL) { return NULL; + } /* * Loop until no more [idx] or .key is following. @@ -13431,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - strncat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len); xfree(p); p = cpy; @@ -19968,8 +19969,8 @@ trans_function_name( len = (int)strlen(lv.ll_exp_name); if (lead <= 2 && lv.ll_name == lv.ll_exp_name && lv.ll_name_len >= 2 && memcmp(lv.ll_name, "s:", 2) == 0) { - /* When there was "s:" already or the name expanded to get a - * leading "s:" then remove it. */ + // When there was "s:" already or the name expanded to get a + // leading "s:" then remove it. lv.ll_name += 2; lv.ll_name_len -= 2; len -= 2; -- cgit From 140174669e359ae7147288366dd94952955980cc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 21:46:24 +0300 Subject: unittests: Run tv_list_join tests in case it stopped failing --- test/unit/eval/typval_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index a785350c15..b0a7e9109a 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1110,7 +1110,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end, true) + end) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() -- cgit From 4c3be98db9b24b7d5b8dc2cf55574e778b7ad137 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:28:54 +0300 Subject: unittests: Add tv_dict_watcher_{add,remove} tests --- test/unit/eval/helpers.lua | 246 ++++++++++++++++++++++++++++------------- test/unit/eval/typval_spec.lua | 115 ++++++++++++++++++- 2 files changed, 282 insertions(+), 79 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index cd85b143d9..21ef51ff20 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -33,18 +33,62 @@ local function li_alloc(nogc) return li end -local function list(...) - local ret = ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref) - eq(0, ret.lv_refcount) - ret.lv_refcount = 1 - for i = 1, select('#', ...) do - local val = select(i, ...) - local li_tv = ffi.gc(lua2typvalt(val), nil) - local li = li_alloc(true) - li.li_tv = li_tv - eval.tv_list_append(ret, li) +local function populate_list(l, lua_l, processed) + processed = processed or {} + eq(0, l.lv_refcount) + l.lv_refcount = 1 + processed[lua_l] = l + for i = 1, #lua_l do + local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil) + local item_li = eval.tv_list_item_alloc() + item_li.li_tv = item_tv + eval.tv_list_append(l, item_li) end - return ret + return l +end + +local function populate_dict(d, lua_d, processed) + processed = processed or {} + eq(0, d.dv_refcount) + d.dv_refcount = 1 + processed[lua_d] = d + for k, v in pairs(lua_d) do + if type(k) == 'string' then + local di = eval.tv_dict_item_alloc(to_cstr(k)) + local val_tv = ffi.gc(lua2typvalt(v, processed), nil) + eval.tv_copy(val_tv, di.di_tv) + eval.tv_clear(val_tv) + eval.tv_dict_add(d, di) + end + end + return d +end + +local function populate_partial(pt, lua_pt, processed) + processed = processed or {} + eq(0, pt.pt_refcount) + processed[lua_pt] = pt + local argv = nil + if lua_pt.args and #lua_pt.args > 0 then + argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #lua_pt.args)), nil) + for i, arg in ipairs(lua_pt.args) do + local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) + argv[i - 1] = arg_tv + end + end + local dict = nil + if lua_pt.dict then + local dict_tv = ffi.gc(lua2typvalt(lua_pt.dict, processed), nil) + assert(dict_tv.v_type == eval.VAR_DICT) + dict = dict_tv.vval.v_dict + end + pt.pt_refcount = 1 + pt.pt_name = eval.xmemdupz(to_cstr(lua_pt.value), #lua_pt.value) + pt.pt_auto = not not lua_pt.auto + pt.pt_argc = lua_pt.args and #lua_pt.args or 0 + pt.pt_argv = argv + pt.pt_dict = dict + return pt end local ptr2key = function(ptr) @@ -55,6 +99,30 @@ local lst2tbl local dct2tbl local typvalt2lua + +local function partial2lua(pt, processed) + processed = processed or {} + local value, auto, dict, argv = nil, nil, nil, nil + if pt ~= nil then + value = ffi.string(pt.pt_name) + auto = pt.pt_auto and true or nil + argv = {} + for i = 1, pt.pt_argc do + argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed) + end + if pt.pt_dict ~= nil then + dict = dct2tbl(pt.pt_dict) + end + end + return { + [type_key]=func_type, + value=value, + auto=auto, + args=argv, + dict=dict, + } +end + local typvalt2lua_tab = nil local function typvalt2lua_tab_init() @@ -97,26 +165,7 @@ local function typvalt2lua_tab_init() if processed[p_key] then return processed[p_key] end - local pt = t.vval.v_partial - local value, auto, dict, argv = nil, nil, nil, nil - if pt ~= nil then - value = ffi.string(pt.pt_name) - auto = pt.pt_auto and true or nil - argv = {} - for i = 1, pt.pt_argc do - argv[i] = typvalt2lua(pt.pt_argv[i - 1], processed) - end - if pt.pt_dict ~= nil then - dict = dct2tbl(pt.pt_dict) - end - end - return { - [type_key]=func_type, - value=value, - auto=auto, - args=argv, - dict=dict, - } + return partial2lua(t.vval.v_partial, processed) end, } end @@ -257,36 +306,16 @@ local lua2typvalt_type_tab = { processed[l].lv_refcount = processed[l].lv_refcount + 1 return typvalt(eval.VAR_LIST, {v_list=processed[l]}) end - local lst = eval.tv_list_alloc() - lst.lv_refcount = 1 - processed[l] = lst - local ret = typvalt(eval.VAR_LIST, {v_list=lst}) - for i = 1, #l do - local item_tv = ffi.gc(lua2typvalt(l[i], processed), nil) - eval.tv_list_append_tv(lst, item_tv) - eval.tv_clear(item_tv) - end - return ret + local lst = populate_list(eval.tv_list_alloc(), l, processed) + return typvalt(eval.VAR_LIST, {v_list=lst}) end, [dict_type] = function(l, processed) if processed[l] then processed[l].dv_refcount = processed[l].dv_refcount + 1 return typvalt(eval.VAR_DICT, {v_dict=processed[l]}) end - local dct = eval.tv_dict_alloc() - dct.dv_refcount = 1 - processed[l] = dct - local ret = typvalt(eval.VAR_DICT, {v_dict=dct}) - for k, v in pairs(l) do - if type(k) == 'string' then - local di = eval.tv_dict_item_alloc(to_cstr(k)) - local val_tv = ffi.gc(lua2typvalt(v, processed), nil) - eval.tv_copy(val_tv, di.di_tv) - eval.tv_clear(val_tv) - eval.tv_dict_add(dct, di) - end - end - return ret + local dct = populate_dict(eval.tv_dict_alloc(), l, processed) + return typvalt(eval.VAR_DICT, {v_dict=dct}) end, [func_type] = function(l, processed) if processed[l] then @@ -294,29 +323,8 @@ local lua2typvalt_type_tab = { return typvalt(eval.VAR_PARTIAL, {v_partial=processed[l]}) end if l.args or l.dict then - local pt = ffi.gc(ffi.cast('partial_T*', eval.xmalloc(ffi.sizeof('partial_T'))), nil) - processed[l] = pt - local argv = nil - if l.args and #l.args > 0 then - argv = ffi.gc(ffi.cast('typval_T*', eval.xmalloc(ffi.sizeof('typval_T') * #l.args)), nil) - for i, arg in ipairs(l.args) do - local arg_tv = ffi.gc(lua2typvalt(arg, processed), nil) - eval.tv_copy(arg_tv, argv[i - 1]) - eval.tv_clear(arg_tv) - end - end - local dict = nil - if l.dict then - local dict_tv = ffi.gc(lua2typvalt(l.dict, processed), nil) - assert(dict_tv.v_type == eval.VAR_DICT) - dict = dict_tv.vval.v_dict - end - pt.pt_refcount = 1 - pt.pt_name = eval.xmemdupz(to_cstr(l.value), #l.value) - pt.pt_auto = not not l.auto - pt.pt_argc = l.args and #l.args or 0 - pt.pt_argv = argv - pt.pt_dict = dict + local pt = populate_partial(ffi.gc(ffi.cast('partial_T*', + eval.xcalloc(1, ffi.sizeof('partial_T'))), nil), l, processed) return typvalt(eval.VAR_PARTIAL, {v_partial=pt}) else return typvalt(eval.VAR_FUNC, { @@ -402,13 +410,91 @@ local alloc_logging_helpers = { return {func='malloc', args={size + 1}, ret=void(s)} end, + dwatcher = function(w) return {func='malloc', args={ffi.sizeof('DictWatcher')}, ret=void(w)} end, + freed = function(p) return {func='free', args={type(p) == 'table' and p or void(p)}} end, + + -- lua_…: allocated by this file, not by some Neovim function + lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end, + lua_tvs = function(argv, argc) return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} end, } local function int(n) return {[type_key]=int_type, value=n} end +local function list(...) + return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref), + {...}, {}) +end + +local function dict(d) + return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), + d, {}) +end + +local callback2tbl_type_tab = nil + +local function init_callback2tbl_type_tab() + if callback2tbl_type_tab then + return + end + callback2tbl_type_tab = { + [tonumber(eval.kCallbackNone)] = function(_) return {type='none'} end, + [tonumber(eval.kCallbackFuncref)] = function(cb) + return {type='fref', fref=ffi.string(cb.data.funcref)} + end, + [tonumber(eval.kCallbackPartial)] = function(cb) + local lua_pt = partial2lua(cb.data.partial) + return {type='pt', fref=ffi.string(lua_pt.value), pt=lua_pt} + end + } +end + +local function callback2tbl(cb) + init_callback2tbl_type_tab() + return callback2tbl_type_tab[tonumber(cb.type)](cb) +end + +local function tbl2callback(tbl) + local ret = nil + if tbl.type == 'none' then + ret = ffi.new('Callback[1]', {{type=eval.kCallbackNone}}) + elseif tbl.type == 'fref' then + ret = ffi.new('Callback[1]', {{type=eval.kCallbackFuncref, + data={funcref=eval.xstrdup(tbl.fref)}}}) + elseif tbl.type == 'pt' then + local pt = ffi.gc(ffi.cast('partial_T*', + eval.xcalloc(1, ffi.sizeof('partial_T'))), eval.partial_unref) + ret = ffi.new('Callback[1]', {{type=eval.kCallbackPartial, + data={partial=populate_partial(pt, tbl.pt, {})}}}) + else + assert(false) + end + return ffi.gc(ffi.cast('Callback*', ret), helpers.callback_free) +end + +local function dict_watchers(d) + local ret = {} + local h = d.watchers + local q = h.next + local qs = {} + local key_patterns = {} + while q ~= h do + local qitem = ffi.cast('DictWatcher *', + ffi.cast('char *', q) - ffi.offsetof('DictWatcher', 'node')) + ret[#ret + 1] = { + cb=callback2tbl(qitem.callback), + pat=ffi.string(qitem.key_pattern, qitem.key_pattern_len), + busy=qitem.busy, + } + qs[#qs + 1] = qitem + key_patterns[#key_patterns + 1] = {qitem.key_pattern, qitem.key_pattern_len} + q = q.next + end + return ret, qs, key_patterns +end + return { int=int, @@ -427,6 +513,7 @@ return { locks_key=locks_key, list=list, + dict=dict, lst2tbl=lst2tbl, dct2tbl=dct2tbl, @@ -446,5 +533,8 @@ return { list_items=list_items, dict_items=dict_items, + dict_watchers=dict_watchers, + tbl2callback=tbl2callback, + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index b0a7e9109a..4efde9c6c1 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -14,21 +14,26 @@ local alloc_log_new = helpers.alloc_log_new local a = eval_helpers.alloc_logging_helpers local int = eval_helpers.int local list = eval_helpers.list +local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local int_type = eval_helpers.int_type local first_di = eval_helpers.first_di +local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua local null_string = eval_helpers.null_string +local tbl2callback = eval_helpers.tbl2callback +local dict_watchers = eval_helpers.dict_watchers local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', - './src/nvim/mbyte.h', './src/nvim/garray.h') + './src/nvim/mbyte.h', './src/nvim/garray.h', + './src/nvim/eval.h') local function list_items(l) local lis = {} @@ -1387,4 +1392,112 @@ describe('typval.c', function() end) end) end) + describe('dict', function() + describe('watcher', function() + describe('add/remove', function() + itp('works with an empty key', function() + local d = dict({}) + eq({}, dict_watchers(d)) + local cb = ffi.gc(tbl2callback({type='none'}), nil) + alloc_log:clear() + lib.tv_dict_watcher_add(d, '*', 0, cb[0]) + local ws, qs = dict_watchers(d) + local key_p = qs[1].key_pattern + alloc_log:check({ + a.dwatcher(qs[1]), + a.str(key_p, 0), + }) + eq({{busy=false, cb={type='none'}, pat=''}}, ws) + eq(true, lib.tv_dict_watcher_remove(d, 'x', 0, cb[0])) + alloc_log:check({ + a.freed(key_p), + a.freed(qs[1]), + }) + eq({}, dict_watchers(d)) + end) + itp('works with multiple callbacks', function() + local d = dict({}) + eq({}, dict_watchers(d)) + alloc_log:check({a.dict(d)}) + local cbs = {} + cbs[1] = {'te', ffi.gc(tbl2callback({type='none'}), nil)} + alloc_log:check({}) + cbs[2] = {'foo', ffi.gc(tbl2callback({type='fref', fref='tr'}), nil)} + alloc_log:check({ + a.str(cbs[2][2].data.funcref, #('tr')), + }) + cbs[3] = {'te', ffi.gc(tbl2callback({type='pt', fref='tr', pt={ + value='tr', + args={'test'}, + dict={}, + }}), nil)} + local pt3 = cbs[3][2].data.partial + local pt3_argv = pt3.pt_argv + local pt3_dict = pt3.pt_dict + local pt3_name = pt3.pt_name + local pt3_str_arg = pt3.pt_argv[0].vval.v_string + alloc_log:check({ + a.lua_pt(pt3), + a.lua_tvs(pt3_argv, pt3.pt_argc), + a.str(pt3_str_arg, #('test')), + a.dict(pt3_dict), + a.str(pt3_name, #('tr')), + }) + for _, v in ipairs(cbs) do + lib.tv_dict_watcher_add(d, v[1], #(v[1]), v[2][0]) + end + local ws, qs, kps = dict_watchers(d) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}, + {busy=false, pat=cbs[2][1], cb={type='fref', fref='tr'}}, + {busy=false, pat=cbs[3][1], cb={type='pt', fref='tr', pt={ + [type_key]=func_type, + value='tr', + args={'test'}, + dict={}, + }}}}, ws) + alloc_log:check({ + a.dwatcher(qs[1]), + a.str(kps[1][1], kps[1][2]), + a.dwatcher(qs[2]), + a.str(kps[2][1], kps[2][2]), + a.dwatcher(qs[3]), + a.str(kps[3][1], kps[3][2]), + }) + eq(true, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) + alloc_log:check({ + a.freed(cbs[2][2].data.funcref), + a.freed(kps[2][1]), + a.freed(qs[2]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[2][1], #cbs[2][1], cbs[2][2][0])) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}, + {busy=false, pat=cbs[3][1], cb={type='pt', fref='tr', pt={ + [type_key]=func_type, + value='tr', + args={'test'}, + dict={}, + }}}}, dict_watchers(d)) + eq(true, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) + alloc_log:check({ + a.freed(pt3_str_arg), + a.freed(pt3_argv), + a.freed(pt3_dict), + a.freed(pt3_name), + a.freed(pt3), + a.freed(kps[3][1]), + a.freed(qs[3]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[3][1], #cbs[3][1], cbs[3][2][0])) + eq({{busy=false, pat=cbs[1][1], cb={type='none'}}}, dict_watchers(d)) + eq(true, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) + alloc_log:check({ + a.freed(kps[1][1]), + a.freed(qs[1]), + }) + eq(false, lib.tv_dict_watcher_remove(d, cbs[1][1], #cbs[1][1], cbs[1][2][0])) + eq({}, dict_watchers(d)) + end) + end) + end) + end) end) -- cgit From 78a0de2c1b722d5445dfc44cc0635e5ce4bcd23e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:10 +0300 Subject: eval/typval: Fix -Werror=return-type --- src/nvim/eval/typval.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 2f3415a59d..326334f149 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -845,6 +845,8 @@ static bool tv_callback_equal(const Callback *const cb1, return true; } } + assert(false); + return false; } /// Remove watcher from a dictionary -- cgit From c6c48e8672ab6adbf78453e2b133fe3bd2ee1eb5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 4 Mar 2017 23:30:42 +0300 Subject: syntax: Fix linter error --- src/nvim/syntax.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index c0adfb10fc..a54e36a609 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5538,7 +5538,7 @@ void ex_ownsyntax(exarg_T *eap) set_internal_string_var((char_u *)"w:current_syntax", new_value); } - /* restore value of b:current_syntax */ + // Restore value of b:current_syntax. if (old_value == NULL) { do_unlet(S_LEN("b:current_syntax"), true); } else { -- cgit From 2c8ad276523489223d2d804075a19367280b0f10 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:14:11 +0300 Subject: ascii: Readd DEL_STR define --- src/nvim/ascii.h | 1 + src/nvim/tui/tui.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index 37b83efb61..adde91f9ec 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -26,6 +26,7 @@ #define ESC '\033' #define ESC_STR "\033" #define DEL 0x7f +#define DEL_STR "\177" #define CSI 0x9b // Control Sequence Introducer #define CSI_STR "\233" #define DCS 0x90 /* Device Control String */ diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 9fbbe8be92..55936ad58d 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1015,7 +1015,7 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, ILOG("libtermkey:kdch1=%s", value); // Vim: "If and are now the same, redefine ." if (stty_erase != NULL && value != NULL && strcmp(stty_erase, value) == 0) { - return stty_erase[0] == DEL ? (char *)CTRL_H_STR : (char *)DEL_STR; + return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } } -- cgit From cdb1aa3e47cb0ec19d2ae597c1d21b7e892d0d7e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 00:47:37 +0300 Subject: eval: Fix len argument to xstrlcat --- src/nvim/eval.c | 2 +- test/functional/shada/shada_spec.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 937b56179d..cab9d07e56 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -13432,7 +13432,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t p_len = strlen(p); cpy = xmallocz(p_len + len); memcpy(cpy, p, p_len + 1); - xstrlcat(cpy + p_len, remain, len); + xstrlcat(cpy + p_len, remain, len + 1); xfree(p); p = cpy; diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index 32598fc399..ca44026852 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -180,8 +180,7 @@ describe('ShaDa support code', function() nvim_command('undo') nvim_command('set shada+=%') nvim_command('wshada! ' .. shada_fname) - local readme_fname = paths.test_source_path .. '/README.md' - readme_fname = helpers.eval( 'resolve("' .. readme_fname .. '")' ) + local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md' eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) nvim_command('set shada+=r~') nvim_command('wshada! ' .. shada_fname) @@ -189,7 +188,8 @@ describe('ShaDa support code', function() nvim_command('set shada-=r~') nvim_command('wshada! ' .. shada_fname) eq({[7]=1, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname)) - nvim_command('set shada+=r' .. paths.test_source_path) + nvim_command('set shada+=r' .. funcs.escape( + funcs.escape(paths.test_source_path, '$~'), ' "\\,')) nvim_command('wshada! ' .. shada_fname) eq({}, find_file(readme_fname)) end) -- cgit From ffaf7c7521399826d9a32b12a1180bcd162f88be Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:09:55 +0300 Subject: unittests: Add tv_dict_item_{alloc,free} tests --- test/helpers.lua | 14 +++++++++++++ test/unit/eval/typval_spec.lua | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/test/helpers.lua b/test/helpers.lua index e5224349c2..1a86effa1c 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -225,6 +225,19 @@ local function which(exe) end end +local function concat_tables(...) + local ret = {} + for i = 1, select('#', ...) do + local tbl = select(i, ...) + if tbl then + for _, v in ipairs(tbl) do + ret[#ret + 1] = v + end + end + end + return ret +end + return { eq = eq, neq = neq, @@ -238,4 +251,5 @@ return { check_cores = check_cores, hasenv = hasenv, which = which, + concat_tables = concat_tables, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 4efde9c6c1..3bd7d9dbaf 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1,4 +1,5 @@ local helpers = require('test.unit.helpers')(after_each) +local global_helpers = require('test.helpers') local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) @@ -31,6 +32,8 @@ local null_string = eval_helpers.null_string local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers +local concat_tables = global_helpers.concat_tables + local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', './src/nvim/eval.h') @@ -1498,6 +1501,48 @@ describe('typval.c', function() eq({}, dict_watchers(d)) end) end) + describe('notify', function() + -- Way too hard to test it here, functional tests in + -- dict_notifications_spec.lua. + end) + end) + describe('item', function() + describe('alloc/free', function() + local function check_tv_dict_item_alloc_len(s, len, tv, more_frees) + local di + if len == nil then + di = ffi.gc(lib.tv_dict_item_alloc(s), nil) + len = #s + else + di = ffi.gc(lib.tv_dict_item_alloc_len(s, len or #s), nil) + end + eq(s:sub(1, len), ffi.string(di.di_key)) + alloc_log:check({a.di(di, len)}) + if tv then + di.di_tv = tv + else + di.di_tv.v_type = lib.VAR_UNKNOWN + end + lib.tv_dict_item_free(di) + alloc_log:check(concat_tables(more_frees, {a.freed(di)})) + end + local function check_tv_dict_item_alloc(s, tv, more_frees) + return check_tv_dict_item_alloc_len(s, nil, tv, more_frees) + end + itp('works', function() + check_tv_dict_item_alloc('') + check_tv_dict_item_alloc('t') + check_tv_dict_item_alloc('TEST') + check_tv_dict_item_alloc_len('', 0) + check_tv_dict_item_alloc_len('TEST', 2) + local tv = lua2typvalt('test') + alloc_log:check({a.str(tv.vval.v_string, #('test'))}) + check_tv_dict_item_alloc('', tv, {a.freed(tv.vval.v_string)}) + tv = lua2typvalt('test') + alloc_log:check({a.str(tv.vval.v_string, #('test'))}) + check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) + end) + end) end) end) end) -- cgit From 6c622ed08bb56f08f4cc4dbf6251220ccf7ba2ba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:26:26 +0300 Subject: unittests: Add tv_dict_item_{add,remove} tests --- test/unit/eval/helpers.lua | 7 +++++-- test/unit/eval/typval_spec.lua | 46 ++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 21ef51ff20..8faa8dbf5c 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -416,7 +416,10 @@ local alloc_logging_helpers = { -- lua_…: allocated by this file, not by some Neovim function lua_pt = function(pt) return {func='calloc', args={1, ffi.sizeof('partial_T')}, ret=void(pt)} end, - lua_tvs = function(argv, argc) return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} end, + lua_tvs = function(argv, argc) + argc = alloc_len(argc) + return {func='malloc', args={ffi.sizeof('typval_T')*argc}, ret=void(argv)} + end, } local function int(n) @@ -430,7 +433,7 @@ end local function dict(d) return populate_dict(ffi.gc(eval.tv_dict_alloc(), eval.tv_dict_free), - d, {}) + d or {}, {}) end local callback2tbl_type_tab = nil diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 3bd7d9dbaf..ebd53469e8 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -8,6 +8,7 @@ local OK = helpers.OK local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi +local FAIL = helpers.FAIL local cimport = helpers.cimport local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new @@ -17,6 +18,7 @@ local int = eval_helpers.int local list = eval_helpers.list local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl +local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc @@ -111,6 +113,18 @@ local function ga_alloc(itemsize, growsize) return ga end +local function check_emsg(f, msg) + local saved_last_msg_hist = lib.last_msg_hist + local ret = {f()} + if msg ~= nil then + neq(saved_last_msg_hist, lib.last_msg_hist) + eq(msg, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end + return unpack(ret) +end + describe('typval.c', function() describe('list', function() describe('item', function() @@ -1242,17 +1256,6 @@ describe('typval.c', function() alloc_log:check({}) end) end) - local function check_emsg(f, msg) - local saved_last_msg_hist = lib.last_msg_hist - local ret = {f()} - if msg ~= nil then - neq(saved_last_msg_hist, lib.last_msg_hist) - eq(msg, ffi.string(lib.last_msg_hist.msg)) - else - eq(saved_last_msg_hist, lib.last_msg_hist) - end - return unpack(ret) - end describe('nr()', function() local function tv_list_find_nr(l, n, msg) return check_emsg(function() @@ -1543,6 +1546,27 @@ describe('typval.c', function() check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) end) end) + describe('add/remove', function() + itp('works', function() + local d = dict() + eq({}, dct2tbl(d)) + alloc_log:check({a.dict(d)}) + local di = ffi.gc(lib.tv_dict_item_alloc(''), nil) + local tv = lua2typvalt('test') + di.di_tv = tv + alloc_log:check({a.di(di, ''), a.str(tv.vval.v_string, 'test')}) + eq(OK, lib.tv_dict_add(d, di)) + alloc_log:check({}) + eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.tv_dict_item_remove(d, di) + alloc_log:check({ + a.freed(tv.vval.v_string), + a.freed(di), + }) + end) + end) end) end) end) -- cgit From faddd83db8a71623e78a4d919b2bb55e6a58439d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 01:37:16 +0300 Subject: eval: Fix SEGV in test49 --- src/nvim/eval.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab9d07e56..882b2dfa74 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2055,6 +2055,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, lp->ll_exp_name = (char *)make_expanded_name(name, expr_start, expr_end, (char_u *)p); + lp->ll_name = lp->ll_exp_name; if (lp->ll_exp_name == NULL) { /* Report an invalid expression in braces, unless the * expression evaluation has been cancelled due to an @@ -2064,9 +2065,10 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, EMSG2(_(e_invarg2), name); return NULL; } + lp->ll_name_len = 0; + } else { + lp->ll_name_len = strlen(lp->ll_name); } - lp->ll_name = lp->ll_exp_name; - lp->ll_name_len = strlen(lp->ll_name); } else { lp->ll_name = (const char *)name; lp->ll_name_len = (size_t)((const char *)p - lp->ll_name); -- cgit From 38dd81c136c2edbda66614622a4747bf1785af94 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:15:17 +0300 Subject: eval/typval: Fix SEGV in test_alot.vim test --- src/nvim/eval/typval.c | 5 ++++- test/functional/eval/null_spec.lua | 6 ++++++ test/unit/eval/typval_spec.lua | 7 +++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 326334f149..8d1ecf8f14 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1167,8 +1167,11 @@ void tv_dict_unref(dict_T *const d) /// @return found item or NULL if nothing was found. dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptrdiff_t len) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + if (d == NULL) { + return NULL; + } hashitem_T *const hi = (len < 0 ? hash_find(&d->dv_hashtab, (const char_u *)key) : hash_find_len(&d->dv_hashtab, key, (size_t)len)); diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index f72d05f2a3..3b361ceeda 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -130,4 +130,10 @@ describe('NULL', function() null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) end) + describe('dict', function() + it('does not crash when indexing NULL dict', function() + eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', + redir_exec('echo v:_null_dict.test')) + end) + end) end) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index ebd53469e8..937869a7bc 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1568,5 +1568,12 @@ describe('typval.c', function() end) end) end) + describe('indexing', function() + describe('find', function() + itp('works with NULL dict', function() + eq(nil, lib.tv_dict_find(nil, '', 0)) + end) + end) + end) end) end) -- cgit From 3bf87a5a6b0e8b2e81534081196c20dddc45474b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 02:51:44 +0300 Subject: eval: Do not use S_LEN as snprintf argument --- src/nvim/eval.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 882b2dfa74..88ba0b95cc 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20004,7 +20004,8 @@ trans_function_name( EMSG(_(e_usingsid)); goto theend; } - sid_buf_len = snprintf(S_LEN(sid_buf), "%" PRIdSCID "_", current_SID); + sid_buf_len = snprintf(sid_buf, sizeof(sid_buf), + "%" PRIdSCID "_", current_SID); lead += sid_buf_len; } } else if (!(flags & TFN_INT) -- cgit From f830243ff7b78b9bb41e72fd602b99c2893b5437 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 03:10:34 +0300 Subject: mbyte: Include os_defs.h in mbyte.h --- src/nvim/mbyte.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index c20e6d47ff..3565202466 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -6,6 +6,7 @@ #include "nvim/iconv.h" #include "nvim/func_attr.h" +#include "nvim/os/os_defs.h" // For WCHAR, indirect /* * Return byte length of character that starts with byte "b". -- cgit From 52e226ff74f49a02f884247b61d1865fa14e810d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 5 Mar 2017 03:53:42 +0300 Subject: unittests: Disable tv_list_join test on Mac OS only --- test/unit/eval/typval_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 937869a7bc..2653706e5b 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -34,6 +34,7 @@ local null_string = eval_helpers.null_string local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers +local uname = global_helpers.uname local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', @@ -1132,7 +1133,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end) + end, uname() == 'Darwin') end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() -- cgit From 5ce624324136408a3f412d6c2ff437455f1e3344 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 14:55:14 +0300 Subject: unittests: Enable tv_list_join tests back Unable to reproduce the problem on Mac OS X Sierra VPS, need to check whether it is reproducible on travis. --- test/unit/eval/typval_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2653706e5b..2f01e9634e 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1133,7 +1133,7 @@ describe('typval.c', function() local recursive_li = recursive_l.lv_first lib.tv_list_item_remove(recursive_l, recursive_li) lib.tv_list_free(l) - end, uname() == 'Darwin') + end) end) describe('equal()', function() itp('compares empty and NULL lists correctly', function() -- cgit From b222453c95dbe466b79abd578642fa6038770a55 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:38 +0300 Subject: eval/typval: Refactor errors a bit: use emsgf always --- src/nvim/eval/typval.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 8d1ecf8f14..867330c137 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -755,7 +755,7 @@ const char *tv_list_find_str(list_T *const l, const int n) { const listitem_T *const li = tv_list_find(l, n); if (li == NULL) { - EMSGN(_(e_listidx), n); + emsgf(_(e_listidx), (int64_t)n); return NULL; } return tv_get_string(&li->li_tv); @@ -1264,14 +1264,14 @@ bool tv_dict_get_callback(dict_T *const d, if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING && di->di_tv.v_type != VAR_PARTIAL) { - EMSG(_("Argument is not a function or function name")); + emsgf(_("E6000: Argument is not a function or function name")); return false; } typval_T tv; tv_copy(&di->di_tv, &tv); set_selfdict(&tv, d); - bool res = callback_from_typval(result, &tv); + const bool res = callback_from_typval(result, &tv); tv_clear(&tv); return res; } @@ -1942,7 +1942,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_copy(UNKNOWN)"); + emsgf(_(e_intern2), "tv_copy(UNKNOWN)"); break; } } @@ -2259,7 +2259,7 @@ bool tv_check_num(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); return false; } } @@ -2303,7 +2303,7 @@ bool tv_check_str(const typval_T *const tv) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } @@ -2350,7 +2350,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) case VAR_LIST: case VAR_DICT: case VAR_FLOAT: { - EMSG(_(num_errors[tv->v_type])); + emsgf(_(num_errors[tv->v_type])); break; } case VAR_NUMBER: { @@ -2378,7 +2378,7 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "tv_get_number(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_number(UNKNOWN)"); break; } } @@ -2428,27 +2428,27 @@ float_T tv_get_float(const typval_T *const tv) } case VAR_PARTIAL: case VAR_FUNC: { - EMSG(_("E891: Using a Funcref as a Float")); + emsgf(_("E891: Using a Funcref as a Float")); break; } case VAR_STRING: { - EMSG(_("E892: Using a String as a Float")); + emsgf(_("E892: Using a String as a Float")); break; } case VAR_LIST: { - EMSG(_("E893: Using a List as a Float")); + emsgf(_("E893: Using a List as a Float")); break; } case VAR_DICT: { - EMSG(_("E894: Using a Dictionary as a Float")); + emsgf(_("E894: Using a Dictionary as a Float")); break; } case VAR_SPECIAL: { - EMSG(_("E907: Using a special value as a Float")); + emsgf(_("E907: Using a special value as a Float")); break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); break; } } @@ -2490,7 +2490,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_DICT: case VAR_FLOAT: case VAR_UNKNOWN: { - EMSG(_(str_errors[tv->v_type])); + emsgf(_(str_errors[tv->v_type])); return false; } } -- cgit From bc87d23c28c10c70b4addaf18ae16bcbe0682c8a Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 12 Mar 2017 21:58:57 +0300 Subject: unittests: Add tests for dictionary indexing --- src/nvim/vim.h | 2 +- test/unit/eval/helpers.lua | 1 + test/unit/eval/typval_spec.lua | 281 ++++++++++++++++++++++++++++++++++------- test/unit/helpers.lua | 23 ++++ 4 files changed, 263 insertions(+), 44 deletions(-) diff --git a/src/nvim/vim.h b/src/nvim/vim.h index e16ee00309..cc0587fb88 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -30,7 +30,7 @@ Error: configure did not run properly.Check auto/config.log. #include "nvim/os/os_defs.h" /* bring lots of system header files */ /// length of a buffer to store a number in ASCII (64 bits binary + NUL) -#define NUMBUFLEN 65 +enum { NUMBUFLEN = 65 }; // flags for vim_str2nr() #define STR2NR_BIN 1 diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 8faa8dbf5c..6909953022 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -538,6 +538,7 @@ return { dict_watchers=dict_watchers, tbl2callback=tbl2callback, + callback2tbl=callback2tbl, empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 2f01e9634e..d91fd3e78d 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -20,26 +20,26 @@ local dict = eval_helpers.dict local lst2tbl = eval_helpers.lst2tbl local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt -local type_key = eval_helpers.type_key -local li_alloc = eval_helpers.li_alloc -local int_type = eval_helpers.int_type -local first_di = eval_helpers.first_di -local func_type = eval_helpers.func_type -local null_list = eval_helpers.null_list -local null_dict = eval_helpers.null_dict -local empty_list = eval_helpers.empty_list -local lua2typvalt = eval_helpers.lua2typvalt -local typvalt2lua = eval_helpers.typvalt2lua -local null_string = eval_helpers.null_string +local type_key = eval_helpers.type_key +local li_alloc = eval_helpers.li_alloc +local first_di = eval_helpers.first_di +local func_type = eval_helpers.func_type +local null_list = eval_helpers.null_list +local null_dict = eval_helpers.null_dict +local dict_items = eval_helpers.dict_items +local empty_list = eval_helpers.empty_list +local lua2typvalt = eval_helpers.lua2typvalt +local typvalt2lua = eval_helpers.typvalt2lua +local null_string = eval_helpers.null_string +local callback2tbl = eval_helpers.callback2tbl local tbl2callback = eval_helpers.tbl2callback local dict_watchers = eval_helpers.dict_watchers -local uname = global_helpers.uname local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', - './src/nvim/eval.h') + './src/nvim/eval.h', './src/nvim/vim.h') local function list_items(l) local lis = {} @@ -79,30 +79,6 @@ before_each(function() alloc_log:before_each() end) -local function clear_tmp_allocs() - local toremove = {} - local allocs = {} - for i, v in ipairs(alloc_log.log) do - if v.func == 'malloc' or v.func == 'calloc' then - allocs[tostring(v.ret)] = i - elseif v.func == 'realloc' or v.func == 'free' then - if allocs[tostring(v.args[1])] then - toremove[#toremove + 1] = allocs[tostring(v.args[1])] - if v.func == 'free' then - toremove[#toremove + 1] = i - end - end - if v.func == 'realloc' then - allocs[tostring(v.ret)] = i - end - end - end - table.sort(toremove) - for i = #toremove,1,-1 do - table.remove(alloc_log.log, toremove[i]) - end -end - after_each(function() alloc_log:after_each() end) @@ -116,12 +92,19 @@ end local function check_emsg(f, msg) local saved_last_msg_hist = lib.last_msg_hist + if saved_last_msg_hist == nil then + saved_last_msg_hist = nil + end local ret = {f()} if msg ~= nil then - neq(saved_last_msg_hist, lib.last_msg_hist) eq(msg, ffi.string(lib.last_msg_hist.msg)) + neq(saved_last_msg_hist, lib.last_msg_hist) else - eq(saved_last_msg_hist, lib.last_msg_hist) + if saved_last_msg_hist ~= lib.last_msg_hist then + eq(nil, ffi.string(lib.last_msg_hist.msg)) + else + eq(saved_last_msg_hist, lib.last_msg_hist) + end end return unpack(ret) end @@ -667,8 +650,7 @@ describe('typval.c', function() a.li(l.lv_last), }) - eq({{[type_key]=int_type, value=-100500}, - {[type_key]=int_type, value=100500}}, typvalt2lua(l_tv)) + eq({int(-100500), int(100500)}, typvalt2lua(l_tv)) end) end) end) @@ -769,7 +751,7 @@ describe('typval.c', function() eq({{['\171']='\187'}, {'\191'}, 1, '\191', null_string, null_list, null_dict}, lst2tbl(l_deepcopy1)) local di_deepcopy1 = first_di(lis_deepcopy1[1].li_tv.vval.v_dict) - clear_tmp_allocs() + alloc_log:clear_tmp_allocs() alloc_log:check({ a.list(l_deepcopy1), a.li(lis_deepcopy1[1]), @@ -1570,9 +1552,222 @@ describe('typval.c', function() end) end) describe('indexing', function() - describe('find', function() + describe('find()', function() + local function tv_dict_find(d, key, key_len) + local di = lib.tv_dict_find(d, key, key_len or #key) + if di == nil then + return nil, nil, nil + end + return typvalt2lua(di.di_tv), ffi.string(di.di_key), di + end itp('works with NULL dict', function() eq(nil, lib.tv_dict_find(nil, '', 0)) + eq(nil, lib.tv_dict_find(nil, 'test', -1)) + eq(nil, lib.tv_dict_find(nil, nil, 0)) + end) + itp('works with NULL key', function() + local lua_d = { + ['']=0, + t=1, + te=2, + tes=3, + test=4, + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local dis = dict_items(d) + eq({0, '', dis['']}, {tv_dict_find(d, '', 0)}) + eq({0, '', dis['']}, {tv_dict_find(d, nil, 0)}) + end) + itp('works with len properly', function() + local lua_d = { + ['']=0, + t=1, + te=2, + tes=3, + test=4, + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + for i = 0, 5 do + local v, k = tv_dict_find(d, 'testt', i) + eq({i, ('testt'):sub(1, i)}, {v, k}) + end + eq(nil, tv_dict_find(d, 'testt', 6)) -- Should take NUL byte + eq(5, tv_dict_find(d, 'testt', -1)) + alloc_log:check({}) + end) + end) + describe('get_number()', function() + itp('works with NULL dict', function() + eq(0, check_emsg(function() return lib.tv_dict_get_number(nil, 'test') end, + nil)) + end) + itp('works', function() + local d = ffi.gc(dict({test={}}), nil) + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 'test') end, + 'E728: Using a Dictionary as a Number')) + d = ffi.gc(dict({tes=int(42), t=44, te='43'}), nil) + alloc_log:clear() + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 'test') end, + nil)) + eq(42, check_emsg(function() return lib.tv_dict_get_number(d, 'tes') end, + nil)) + eq(43, check_emsg(function() return lib.tv_dict_get_number(d, 'te') end, + nil)) + alloc_log:check({}) + eq(0, check_emsg(function() return lib.tv_dict_get_number(d, 't') end, + 'E805: Using a Float as a Number')) + end) + end) + describe('get_string()', function() + itp('works with NULL dict', function() + eq(nil, check_emsg(function() return lib.tv_dict_get_string(nil, 'test', false) end, + nil)) + end) + itp('works', function() + local d = ffi.gc(dict({test={}}), nil) + eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 'test', false) end, + 'E731: using Dictionary as a String'))) + d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) + alloc_log:clear() + local dis = dict_items(d) + eq(nil, check_emsg(function() return lib.tv_dict_get_string(d, 'test', false) end, + nil)) + local s42 = check_emsg(function() return lib.tv_dict_get_string(d, 'tes', false) end, + nil) + eq('42', ffi.string(s42)) + local s45 = check_emsg(function() return lib.tv_dict_get_string(d, 'xx', false) end, + nil) + eq(s42, s45) + eq('45', ffi.string(s45)) + eq('45', ffi.string(s42)) + local s43 = check_emsg(function() return lib.tv_dict_get_string(d, 'te', false) end, + nil) + eq('43', ffi.string(s43)) + neq(s42, s43) + eq(s43, dis.te.di_tv.vval.v_string) + alloc_log:check({}) + eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, + 'E806: using Float as a String'))) + end) + itp('allocates a string copy when requested', function() + local function tv_dict_get_string_alloc(d, key, emsg) + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + if s_ret then + alloc_log:check({a.str(ret, s_ret)}) + else + alloc_log:check({}) + end + end + lib.xfree(ret) + return s_ret + end + local d = ffi.gc(dict({test={}}), nil) + eq('', tv_dict_get_string_alloc(d, 'test', 'E731: using Dictionary as a String')) + d = ffi.gc(dict({tes=int(42), t=44, te='43', xx=int(45)}), nil) + alloc_log:clear() + eq(nil, tv_dict_get_string_alloc(d, 'test')) + eq('42', tv_dict_get_string_alloc(d, 'tes')) + eq('45', tv_dict_get_string_alloc(d, 'xx')) + eq('43', tv_dict_get_string_alloc(d, 'te')) + eq('', tv_dict_get_string_alloc(d, 't', 'E806: using Float as a String')) + end) + end) + describe('get_string_buf()', function() + local function tv_dict_get_string_buf(d, key, buf, emsg) + buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) + alloc_log:clear() + local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end, + emsg) + local s_ret = (ret ~= nil) and ffi.string(ret) or nil + if not emsg then + alloc_log:check({}) + end + return s_ret, ret, buf + end + itp('works with NULL dict', function() + eq(nil, tv_dict_get_string_buf(nil, 'test')) + end) + itp('works', function() + local lua_d = { + ['']={}, + t=1, + te=int(2), + tes=empty_list, + test='tset', + testt=5, + } + local d = dict(lua_d) + alloc_log:clear() + eq(lua_d, dct2tbl(d)) + alloc_log:check({}) + local s, r, b + s, r, b = tv_dict_get_string_buf(d, 'test') + neq(r, b) + eq('tset', s) + s, r, b = tv_dict_get_string_buf(d, 't', nil, 'E806: using Float as a String') + neq(r, b) + eq('', s) + s, r, b = tv_dict_get_string_buf(d, 'te') + eq(r, b) + eq('2', s) + end) + end) + describe('get_callback()', function() + local function tv_dict_get_callback(d, key, key_len, emsg) + key_len = key_len or #key + local cb = ffi.gc(ffi.cast('Callback*', lib.xmalloc(ffi.sizeof('Callback'))), lib.callback_free) + alloc_log:clear() + local ret = check_emsg(function() + return lib.tv_dict_get_callback(d, key, key_len, cb) + end, emsg) + local cb_lua = callback2tbl(cb[0]) + return cb_lua, ret + end + itp('works with NULL dict', function() + eq({{type='none'}, true}, + {tv_dict_get_callback(nil, nil, 0)}) + end) + itp('works', function() + local lua_d = { + ['']='tr', + t=int(1), + te={[type_key]=func_type, value='tr'}, + tes={[type_key]=func_type, value='tr', args={'a', 'b'}}, + test={[type_key]=func_type, value='Test', dict={test=1}, args={}}, + testt={[type_key]=func_type, value='Test', dict={test=1}, args={1}}, + } + local d = dict(lua_d) + eq(lua_d, dct2tbl(d)) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, nil, 0)}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, '', -1)}) + eq({{type='none'}, true}, + {tv_dict_get_callback(d, 'x', -1)}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, 'testt', 0)}) + eq({{type='none'}, false}, + {tv_dict_get_callback(d, 'test', 1, 'E6000: Argument is not a function or function name')}) + eq({{type='fref', fref='tr'}, true}, + {tv_dict_get_callback(d, 'testt', 2)}) + eq({{ type='pt', fref='tr', pt={ [type_key]=func_type, value='tr', args={ 'a', 'b' } } }, true}, + {tv_dict_get_callback(d, 'testt', 3)}) + eq({{ type='pt', fref='Test', pt={ [type_key]=func_type, value='Test', dict={ test=1 }, args={} } }, true}, + {tv_dict_get_callback(d, 'testt', 4)}) + eq({{ type='pt', fref='Test', pt={ [type_key]=func_type, value='Test', dict={ test=1 }, args={1} } }, true}, + {tv_dict_get_callback(d, 'testt', 5)}) end) end) end) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 612b337ee7..8aad3acd98 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -314,6 +314,29 @@ local function alloc_log_new() eq(exp, self.log) self:clear() end + function log:clear_tmp_allocs() + local toremove = {} + local allocs = {} + for i, v in ipairs(self.log) do + if v.func == 'malloc' or v.func == 'calloc' then + allocs[tostring(v.ret)] = i + elseif v.func == 'realloc' or v.func == 'free' then + if allocs[tostring(v.args[1])] then + toremove[#toremove + 1] = allocs[tostring(v.args[1])] + if v.func == 'free' then + toremove[#toremove + 1] = i + end + end + if v.func == 'realloc' then + allocs[tostring(v.ret)] = i + end + end + end + table.sort(toremove) + for i = #toremove,1,-1 do + table.remove(self.log, toremove[i]) + end + end function log:restore_original_functions() -- Do nothing: set mocks live in a separate process return -- cgit From 270a3889af024485fa7b63f34c4dd3f92f6e0f98 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 01:45:44 +0300 Subject: unittests: Add tv_dict_add* unit tests Also fixes incorrect location of `tv_dict_add` function and three bugs in other functions: 1. `tv_dict_add_list` may free list it does not own (vim/vim#1555). 2. `tv_dict_add_dict` may free dictionary it does not own (vim/vim#1555). 3. `tv_dict_add_dict` ignores `key_len` argument. --- src/nvim/eval/typval.c | 32 ++++++------ test/unit/eval/typval_spec.lua | 116 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 867330c137..d9feb2d88e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1009,18 +1009,6 @@ void tv_dict_item_free(dictitem_T *const item) } } -/// Add item to dictionary -/// -/// @param[out] d Dictionary to add to. -/// @param[in] item Item to add. -/// -/// @return FAIL if key already exists. -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); -} - /// Make a copy of a dictionary item /// /// @param[in] di Item to copy. @@ -1278,6 +1266,18 @@ bool tv_dict_get_callback(dict_T *const d, //{{{2 dict_add* +/// Add item to dictionary +/// +/// @param[out] d Dictionary to add to. +/// @param[in] item Item to add. +/// +/// @return FAIL if key already exists. +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); +} + /// Add a list entry to dictionary /// /// @param[out] d Dictionary to add entry to. @@ -1295,11 +1295,11 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; + list->lv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - list->lv_refcount++; return OK; } @@ -1313,18 +1313,18 @@ int tv_dict_add_list(dict_T *const d, const char *const key, /// @return OK in case of success, FAIL when key already exists. int tv_dict_add_dict(dict_T *const d, const char *const key, const size_t key_len, dict_T *const dict) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ALL { - dictitem_T *const item = tv_dict_item_alloc(key); + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_DICT; item->di_tv.vval.v_dict = dict; + dict->dv_refcount++; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; } - dict->dv_refcount++; return OK; } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d91fd3e78d..c710171779 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1770,6 +1770,122 @@ describe('typval.c', function() {tv_dict_get_callback(d, 'testt', 5)}) end) end) + describe('dict_add()', function() + itp('works', function() + local di = lib.tv_dict_item_alloc_len('t-est', 5) + alloc_log:check({a.di(di, 't-est')}) + di.di_tv.v_type = lib.VAR_NUMBER + di.di_tv.vval.v_number = 42 + local d = dict({test=10}) + local dis = dict_items(d) + alloc_log:check({ + a.dict(d), + a.di(dis.test, 'test') + }) + eq({test=10}, dct2tbl(d)) + alloc_log:clear() + eq(OK, lib.tv_dict_add(d, di)) + alloc_log:check({}) + eq({test=10, ['t-est']=int(42)}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add(d, di) end, + 'E685: Internal error: hash_add()')) + end) + end) + describe('dict_add_list()', function() + itp('works', function() + local l = list(1, 2, 3) + alloc_log:clear() + eq(1, l.lv_refcount) + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_list(d, 'testt', 3, l)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes={1, 2, 3}}, dct2tbl(d)) + eq(2, l.lv_refcount) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end, + 'E685: Internal error: hash_add()')) + eq(2, l.lv_refcount) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_list(d, 'testt', 3, l) end, + nil)) + eq(2, l.lv_refcount) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_dict()', function() + itp('works', function() + local d2 = dict({foo=42}) + alloc_log:clear() + eq(1, d2.dv_refcount) + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_dict(d, 'testt', 3, d2)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes={foo=42}}, dct2tbl(d)) + eq(2, d2.dv_refcount) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end, + 'E685: Internal error: hash_add()')) + eq(2, d2.dv_refcount) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_dict(d, 'testt', 3, d2) end, + nil)) + eq(2, d2.dv_refcount) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_nr()', function() + itp('works', function() + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_nr(d, 'testt', 3, 2)) + local dis = dict_items(d) + alloc_log:check({a.di(dis.tes, 'tes')}) + eq({test=10, tes=int(2)}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_nr(d, 'testt', 3, 2) end, + nil)) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) + describe('dict_add_str()', function() + itp('works', function() + local d = dict({test=10}) + alloc_log:clear() + eq({test=10}, dct2tbl(d)) + eq(OK, lib.tv_dict_add_str(d, 'testt', 3, 'TEST')) + local dis = dict_items(d) + alloc_log:check({ + a.di(dis.tes, 'tes'), + a.str(dis.tes.di_tv.vval.v_string, 'TEST') + }) + eq({test=10, tes='TEST'}, dct2tbl(d)) + eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, + 'E685: Internal error: hash_add()')) + alloc_log:clear() + lib.emsg_skip = lib.emsg_skip + 1 + eq(FAIL, check_emsg(function() return lib.tv_dict_add_str(d, 'testt', 3, 'TEST') end, + nil)) + lib.emsg_skip = lib.emsg_skip - 1 + alloc_log:clear_tmp_allocs() + alloc_log:check({}) + end) + end) end) end) end) -- cgit From 43e9fad1c8835a3136f4db53c82608e034df2a5e Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 13:43:01 +0300 Subject: eval: Use tv_is_func in place of ==VAR_FUNC||==VAR_PARTIAL Also fixes same error as in vim/vim#1557 --- src/nvim/eval.c | 25 ++++++++----------------- src/nvim/eval/typval.c | 9 +++------ src/nvim/eval/typval.h | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 88ba0b95cc..cab22c599a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2225,7 +2225,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, prevval = 0; // Avoid compiler warning. } wrong = ((lp->ll_dict->dv_scope == VAR_DEF_SCOPE - && rettv->v_type == VAR_FUNC + && tv_is_func(*rettv) && !var_check_func_name((const char *)key, lp->ll_di == NULL)) || !valid_varname((const char *)key)); if (len != -1) { @@ -3643,9 +3643,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) n1 = !n1; } } - } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL - || var2.v_type == VAR_PARTIAL) { + } else if (tv_is_func(*rettv) || tv_is_func(var2)) { if (type != TYPE_EQUAL && type != TYPE_NEQUAL) { EMSG(_("E694: Invalid operation for Funcrefs")); tv_clear(rettv); @@ -8957,8 +8955,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv = &di->di_tv; } } - } else if (argvars[0].v_type == VAR_PARTIAL - || argvars[0].v_type == VAR_FUNC) { + } else if (tv_is_func(argvars[0])) { partial_T *pt; partial_T fref_pt; @@ -15994,7 +15991,7 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *const flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); typval_T *expr = NULL; - if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) { + if (tv_is_func(argvars[2])) { expr = &argvars[2]; } else { sub = tv_get_string_buf_chk(&argvars[2], subbuf); @@ -18229,8 +18226,7 @@ handle_subscript( while (ret == OK && (**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT) - || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL))) + || (**arg == '(' && (!evaluate || tv_is_func(*rettv)))) && !ascii_iswhite(*(*arg - 1))) { if (**arg == '(') { partial_T *pt = NULL; @@ -18286,9 +18282,7 @@ handle_subscript( } // Turn "dict.Func" into a partial for "Func" bound to "dict". - if (selfdict != NULL - && (rettv->v_type == VAR_FUNC - || rettv->v_type == VAR_PARTIAL)) { + if (selfdict != NULL && tv_is_func(*rettv)) { set_selfdict(rettv, selfdict); } @@ -18794,8 +18788,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, v = find_var_in_scoped_ht((const char *)name, name_len, true); } - if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL) - && !var_check_func_name(name, v == NULL)) { + if (tv_is_func(*tv) && !var_check_func_name(name, v == NULL)) { return; } @@ -19463,9 +19456,7 @@ void ex_function(exarg_T *eap) arg = name; else arg = fudi.fd_newkey; - if (arg != NULL && (fudi.fd_di == NULL - || (fudi.fd_di->di_tv.v_type != VAR_FUNC - && fudi.fd_di->di_tv.v_type != VAR_PARTIAL))) { + if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) { int j = (*arg == K_SPECIAL) ? 3 : 0; while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index d9feb2d88e..620da0032e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1250,8 +1250,7 @@ bool tv_dict_get_callback(dict_T *const d, return true; } - if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING - && di->di_tv.v_type != VAR_PARTIAL) { + if (!tv_is_func(di->di_tv) && di->di_tv.v_type != VAR_STRING) { emsgf(_("E6000: Argument is not a function or function name")); return false; } @@ -1418,7 +1417,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, // 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 - && di2->di_tv.v_type == VAR_FUNC + && tv_is_func(di2->di_tv) && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { break; } @@ -2101,9 +2100,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, // TODO(ZyX-I): Make this not recursive static int recursive_cnt = 0; // Catch recursive loops. - if (!((tv1->v_type == VAR_FUNC || tv1->v_type == VAR_PARTIAL) - && (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_PARTIAL)) - && tv1->v_type != tv2->v_type) { + if (!(tv_is_func(*tv1) && tv_is_func(*tv2)) && tv1->v_type != tv2->v_type) { return false; } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index fa0105197f..7eab22bc12 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -408,6 +408,21 @@ static inline DictWatcher *tv_dict_watcher_node_data(QUEUE *q) return QUEUE_DATA(q, DictWatcher, node); } +static inline bool tv_is_func(const typval_T tv) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST; + +/// Check whether given typval_T contains a function +/// +/// That is, whether it contains VAR_FUNC or VAR_PARTIAL. +/// +/// @param[in] tv Typval to check. +/// +/// @return True if it is a function or a partial, false otherwise. +static inline bool tv_is_func(const typval_T tv) +{ + return tv.v_type == VAR_FUNC || tv.v_type == VAR_PARTIAL; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.h.generated.h" #endif -- cgit From 4987850cacc9f0c5674bc4cb8197a6fa0a343167 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 13:43:25 +0300 Subject: unittests: Add tv_dict_clear tests --- test/unit/eval/typval_spec.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index c710171779..b16e118053 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1887,5 +1887,23 @@ describe('typval.c', function() end) end) end) + describe('clear()', function() + itp('works', function() + local d = dict() + alloc_log:check({a.dict(d)}) + eq({}, dct2tbl(d)) + lib.tv_dict_clear(d) + eq({}, dct2tbl(d)) + lib.tv_dict_add_str(d, 'TEST', 3, 'tEsT') + local dis = dict_items(d) + local di = dis.TES + local di_s = di.di_tv.vval.v_string + alloc_log:check({a.di(di), a.str(di_s)}) + eq({TES='tEsT'}, dct2tbl(d)) + lib.tv_dict_clear(d) + alloc_log:check({a.freed(di_s), a.freed(di)}) + eq({}, dct2tbl(d)) + end) + end) end) end) -- cgit From fa852e7cdc365b6fcd39d677f4067963274c44c3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:17:35 +0300 Subject: eval: Fix extend() behaviour with NULL lists and dictionaries Ref #4615 Ref vim/vim#768 --- src/nvim/eval.c | 34 +++++---- test/functional/eval/null_spec.lua | 147 ++++++++++++++++++------------------- 2 files changed, 94 insertions(+), 87 deletions(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cab22c599a..9950f8b196 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8155,15 +8155,20 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) const size_t arg_errmsg_len = strlen(arg_errmsg); if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { - list_T *l1, *l2; - listitem_T *item; long before; bool error = false; - l1 = argvars[0].vval.v_list; - l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len) - && l2 != NULL) { + list_T *const l1 = argvars[0].vval.v_list; + list_T *const l2 = argvars[1].vval.v_list; + if (l1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (l2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, arg_errmsg_len)) { + listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); if (error) { @@ -8187,13 +8192,16 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) { - dict_T *d1; - dict_T *d2; - - d1 = argvars[0].vval.v_dict; - d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len) - && d2 != NULL) { + dict_T *const d1 = argvars[0].vval.v_dict; + dict_T *const d2 = argvars[1].vval.v_dict; + if (d1 == NULL) { + const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, arg_errmsg_len); + (void)locked; + assert(locked == true); + } else if (d2 == NULL) { + // Do nothing + tv_copy(&argvars[0], rettv); + } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, arg_errmsg_len)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { diff --git a/test/functional/eval/null_spec.lua b/test/functional/eval/null_spec.lua index 3b361ceeda..6fd30caec9 100644 --- a/test/functional/eval/null_spec.lua +++ b/test/functional/eval/null_spec.lua @@ -20,120 +20,119 @@ describe('NULL', function() after_each(function() os.remove(tmpfname) end) + local null_test = function(name, cmd, err) + it(name, function() + eq(err, exc_exec(cmd)) + end) + end + local null_expr_test = function(name, expr, err, val, after) + it(name, function() + eq((err == 0) and ('') or ('\n' .. err), + redir_exec('let g:_var = ' .. expr)) + if val == nil then + eq(0, funcs.exists('g:_var')) + else + eq(val, meths.get_var('_var')) + end + if after ~= nil then + after() + end + end) + end describe('list', function() - local null_list_test = function(name, cmd, err) - it(name, function() - eq(err, exc_exec(cmd)) - end) - end - local null_list_expr_test = function(name, expr, err, val, after) - it(name, function() - eq((err == 0) and ('') or ('\n' .. err), - redir_exec('let g:_var = ' .. expr)) - if val == nil then - eq(0, funcs.exists('g:_var')) - else - eq(val, meths.get_var('_var')) - end - if after ~= nil then - after() - end - end) - end - -- Incorrect behaviour -- FIXME map() should not return 0 without error - null_list_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) + null_expr_test('does not crash map()', 'map(L, "v:val")', 0, 0) -- FIXME map() should not return 0 without error - null_list_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) + null_expr_test('does not crash filter()', 'filter(L, "1")', 0, 0) -- FIXME map() should at least return L - null_list_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) + null_expr_test('makes map() return v:_null_list', 'map(L, "v:val") is# L', 0, 0) -- FIXME filter() should at least return L - null_list_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) + null_expr_test('makes filter() return v:_null_list', 'map(L, "1") is# L', 0, 0) -- FIXME add() should not return 1 at all - null_list_expr_test('does not crash add()', 'add(L, 0)', 0, 1) - -- FIXME extend() should not return 0 without error - null_list_expr_test('does not crash extend()', 'extend(L, [1])', 0, 0) - -- FIXME extend() should not return 0 at all - null_list_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, 0) + null_expr_test('does not crash add()', 'add(L, 0)', 0, 1) + null_expr_test('does not crash extend()', 'extend(L, [1])', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('does not crash extend() (second position)', 'extend([1], L)', 0, {1}) -- FIXME should be accepted by inputlist() - null_list_expr_test('is accepted as an empty list by inputlist()', + null_expr_test('is accepted as an empty list by inputlist()', '[feedkeys("\\n"), inputlist(L)]', 'E686: Argument of inputlist() must be a List', {0, 0}) -- FIXME should be accepted by writefile(), return {0, {}} - null_list_expr_test('is accepted as an empty list by writefile()', + null_expr_test('is accepted as an empty list by writefile()', ('[writefile(L, "%s"), readfile("%s")]'):format(tmpfname, tmpfname), 'E484: Can\'t open file ' .. tmpfname, {0, {}}) -- FIXME should give error message - null_list_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) + null_expr_test('does not crash remove()', 'remove(L, 0)', 0, 0) -- FIXME should return 0 - null_list_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) + null_expr_test('is accepted by setqflist()', 'setqflist(L)', 0, -1) -- FIXME should return 0 - null_list_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) + null_expr_test('is accepted by setloclist()', 'setloclist(1, L)', 0, -1) -- FIXME should return 0 - null_list_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) + null_expr_test('is accepted by setmatches()', 'setmatches(L)', 0, -1) -- FIXME should return empty list or error out - null_list_expr_test('is accepted by sort()', 'sort(L)', 0, 0) + null_expr_test('is accepted by sort()', 'sort(L)', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) + null_expr_test('is accepted by sort()', 'sort(L) is L', 0, 0) -- FIXME should not error out - null_list_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') + null_test('is accepted by :cexpr', 'cexpr L', 'Vim(cexpr):E777: String or List expected') -- FIXME should not error out - null_list_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') - null_list_test('is accepted by :for', 'for x in L|throw x|endfor', 0) + null_test('is accepted by :lexpr', 'lexpr L', 'Vim(lexpr):E777: String or List expected') + null_test('is accepted by :for', 'for x in L|throw x|endfor', 0) -- Subjectable behaviour -- FIXME Should return 1 - null_list_expr_test('is equal to empty list', 'L == []', 0, 0) + null_expr_test('is equal to empty list', 'L == []', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) + null_expr_test('is equal to empty list (reverse order)', '[] == L', 0, 0) -- FIXME Should return 1 - null_list_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) + null_expr_test('is not locked', 'islocked("v:_null_list")', 0, 0) -- Crashes - -- null_list_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) - -- null_list_expr_test('does not crash setline', 'setline(1, L)', 0, 0) - -- null_list_expr_test('does not crash system()', 'system("cat", L)', 0, '') - -- null_list_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) + -- null_expr_test('does not crash setreg', 'setreg("x", L)', 0, 0) + -- null_expr_test('does not crash setline', 'setline(1, L)', 0, 0) + -- null_expr_test('does not crash system()', 'system("cat", L)', 0, '') + -- null_expr_test('does not crash systemlist()', 'systemlist("cat", L)', 0, {}) -- Correct behaviour - null_list_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() + null_expr_test('does not crash append()', 'append(1, L)', 0, 0, function() eq({''}, curbufmeths.get_lines(0, -1, false)) end) - null_list_expr_test('is identical to itself', 'L is L', 0, 1) - null_list_expr_test('can be sliced', 'L[:]', 0, {}) - null_list_expr_test('can be copied', 'copy(L)', 0, {}) - null_list_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) - null_list_expr_test('does not crash when indexed', 'L[1]', + null_expr_test('is identical to itself', 'L is L', 0, 1) + null_expr_test('can be sliced', 'L[:]', 0, {}) + null_expr_test('can be copied', 'copy(L)', 0, {}) + null_expr_test('can be deepcopied', 'deepcopy(L)', 0, {}) + null_expr_test('does not crash when indexed', 'L[1]', 'E684: list index out of range: 1\nE15: Invalid expression: L[1]', nil) - null_list_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) - null_list_expr_test('does not crash col()', 'col(L)', 0, 0) - null_list_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) - null_list_expr_test('does not crash line()', 'line(L)', 0, 0) - null_list_expr_test('does not crash count()', 'count(L, 1)', 0, 0) - null_list_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) - null_list_expr_test('is empty', 'empty(L)', 0, 1) - null_list_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) - null_list_expr_test('has zero length', 'len(L)', 0, 0) - null_list_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) - null_list_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) - null_list_expr_test('is stringified correctly', 'string(L)', 0, '[]') - null_list_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') - null_list_test('does not crash lockvar', 'lockvar! L', 0) - null_list_expr_test('can be added to itself', '(L + L)', 0, {}) - null_list_expr_test('can be added to itself', '(L + L) is L', 0, 1) - null_list_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) - null_list_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) - null_list_expr_test('is equal to itself', 'L == L', 0, 1) - null_list_expr_test('is not not equal to itself', 'L != L', 0, 0) - null_list_expr_test('counts correctly', 'count([L], L)', 0, 1) + null_expr_test('does not crash call()', 'call("arglistid", L)', 0, 0) + null_expr_test('does not crash col()', 'col(L)', 0, 0) + null_expr_test('does not crash virtcol()', 'virtcol(L)', 0, 0) + null_expr_test('does not crash line()', 'line(L)', 0, 0) + null_expr_test('does not crash count()', 'count(L, 1)', 0, 0) + null_expr_test('does not crash cursor()', 'cursor(L)', 'E474: Invalid argument', -1) + null_expr_test('is empty', 'empty(L)', 0, 1) + null_expr_test('does not crash get()', 'get(L, 1, 10)', 0, 10) + null_expr_test('has zero length', 'len(L)', 0, 0) + null_expr_test('is accepted as an empty list by max()', 'max(L)', 0, 0) + null_expr_test('is accepted as an empty list by min()', 'min(L)', 0, 0) + null_expr_test('is stringified correctly', 'string(L)', 0, '[]') + null_expr_test('is JSON encoded correctly', 'json_encode(L)', 0, '[]') + null_test('does not crash lockvar', 'lockvar! L', 0) + null_expr_test('can be added to itself', '(L + L)', 0, {}) + null_expr_test('can be added to itself', '(L + L) is L', 0, 1) + null_expr_test('can be added to non-empty list', '([1] + L)', 0, {1}) + null_expr_test('can be added to non-empty list (reversed)', '(L + [1])', 0, {1}) + null_expr_test('is equal to itself', 'L == L', 0, 1) + null_expr_test('is not not equal to itself', 'L != L', 0, 0) + null_expr_test('counts correctly', 'count([L], L)', 0, 1) end) describe('dict', function() it('does not crash when indexing NULL dict', function() eq('\nE716: Key not present in Dictionary: test\nE15: Invalid expression: v:_null_dict.test', redir_exec('echo v:_null_dict.test')) end) + null_expr_test('makes extend error out', 'extend(D, {})', 'E742: Cannot change value of extend() argument', 0) + null_expr_test('makes extend do nothing', 'extend({1: 2}, D)', 0, {['1']=2}) end) end) -- cgit From 8b9a1fbf7a630b68b1428a39f25e1fa38fe0cc9f Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:35:53 +0300 Subject: unittests: Add tests for tv_dict_extend --- test/unit/eval/helpers.lua | 12 ++++ test/unit/eval/tricks_spec.lua | 14 ++--- test/unit/eval/typval_spec.lua | 129 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 11 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 6909953022..a3cb062b7b 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -498,6 +498,16 @@ local function dict_watchers(d) return ret, qs, key_patterns end +local function eval0(expr) + local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), + eval.tv_clear) + if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then + return nil + else + return tv + end +end + return { int=int, @@ -540,5 +550,7 @@ return { tbl2callback=tbl2callback, callback2tbl=callback2tbl, + eval0=eval0, + empty_list = {[type_key]=list_type}, } diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index 54029734fb..ae569bed11 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -1,4 +1,6 @@ local helpers = require('test.unit.helpers')(after_each) +local eval_helpers = require('test.unit.eval.helpers') + local itp = helpers.gen_itp(it) local cimport = helpers.cimport @@ -6,19 +8,11 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq +local eval0 = eval_helpers.eval0 + local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', './src/nvim/memory.h') -local eval0 = function(expr) - local tv = ffi.gc(ffi.new('typval_T', {v_type=eval.VAR_UNKNOWN}), - eval.tv_clear) - if eval.eval0(to_cstr(expr), tv, nil, true) == 0 then - return nil - else - return tv - end -end - describe('NULL typval_T', function() itp('is produced by $XXX_UNEXISTENT_VAR_XXX', function() -- Required for various tests which need to check whether typval_T with NULL diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index b16e118053..00dc230e89 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1,6 +1,7 @@ +local bit = require('bit') local helpers = require('test.unit.helpers')(after_each) -local global_helpers = require('test.helpers') local eval_helpers = require('test.unit.eval.helpers') +local global_helpers = require('test.helpers') local itp = helpers.gen_itp(it) @@ -17,6 +18,7 @@ local a = eval_helpers.alloc_logging_helpers local int = eval_helpers.int local list = eval_helpers.list local dict = eval_helpers.dict +local eval0 = eval_helpers.eval0 local lst2tbl = eval_helpers.lst2tbl local dct2tbl = eval_helpers.dct2tbl local typvalt = eval_helpers.typvalt @@ -1905,5 +1907,130 @@ describe('typval.c', function() eq({}, dct2tbl(d)) end) end) + describe('extend()', function() + local function tv_dict_extend(d1, d2, action, emsg) + action = action or "force" + check_emsg(function() return lib.tv_dict_extend(d1, d2, action) end, emsg) + end + itp('works', function() + local d1 = dict() + alloc_log:check({a.dict(d1)}) + eq({}, dct2tbl(d1)) + local d2 = dict() + alloc_log:check({a.dict(d2)}) + eq({}, dct2tbl(d2)) + tv_dict_extend(d1, d2, 'error') + tv_dict_extend(d1, d2, 'keep') + tv_dict_extend(d1, d2, 'force') + alloc_log:check({}) + + d1 = dict({a='TEST'}) + eq({a='TEST'}, dct2tbl(d1)) + local dis1 = dict_items(d1) + local a1_s = dis1.a.di_tv.vval.v_string + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d1), + a.di(dis1.a), + a.str(a1_s), + }) + d2 = dict({a='TSET'}) + eq({a='TSET'}, dct2tbl(d2)) + local dis2 = dict_items(d2) + local a2_s = dis2.a.di_tv.vval.v_string + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d2), + a.di(dis2.a), + a.str(a2_s), + }) + + tv_dict_extend(d1, d2, 'error', 'E737: Key already exists: a') + eq({a='TEST'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + alloc_log:clear() + + tv_dict_extend(d1, d2, 'keep') + alloc_log:check({}) + eq({a='TEST'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + + tv_dict_extend(d1, d2, 'force') + alloc_log:check({ + a.freed(a1_s), + a.str(dis1.a.di_tv.vval.v_string), + }) + eq({a='TSET'}, dct2tbl(d1)) + eq({a='TSET'}, dct2tbl(d2)) + end) + itp('disallows overriding builtin or user functions', function() + local d = dict() + d.dv_scope = lib.VAR_DEF_SCOPE + local f_lua = { + [type_key]=func_type, + value='tr', + } + local f_tv = lua2typvalt(f_lua) + local p_lua = { + [type_key]=func_type, + value='tr', + args={1}, + } + local p_tv = lua2typvalt(p_lua) + eq(lib.VAR_PARTIAL, p_tv.v_type) + local d2 = dict({tr=f_tv}) + local d3 = dict({tr=p_tv}) + local d4 = dict({['TEST:THIS']=p_tv}) + local d5 = dict({Test=f_tv}) + local d6 = dict({Test=p_tv}) + eval0([[execute("function Test()\nendfunction")]]) + tv_dict_extend(d, d2, 'force', + 'E704: Funcref variable name must start with a capital: tr') + tv_dict_extend(d, d3, 'force', + 'E704: Funcref variable name must start with a capital: tr') + tv_dict_extend(d, d4, 'force', + 'E461: Illegal variable name: TEST:THIS') + tv_dict_extend(d, d5, 'force', + 'E705: Variable name conflicts with existing function: Test') + tv_dict_extend(d, d6, 'force', + 'E705: Variable name conflicts with existing function: Test') + eq({}, dct2tbl(d)) + d.dv_scope = lib.VAR_SCOPE + tv_dict_extend(d, d4, 'force', + 'E461: Illegal variable name: TEST:THIS') + eq({}, dct2tbl(d)) + tv_dict_extend(d, d2, 'force') + eq({tr=f_lua}, dct2tbl(d)) + tv_dict_extend(d, d3, 'force') + eq({tr=p_lua}, dct2tbl(d)) + tv_dict_extend(d, d5, 'force') + eq({tr=p_lua, Test=f_lua}, dct2tbl(d)) + tv_dict_extend(d, d6, 'force') + eq({tr=p_lua, Test=p_lua}, dct2tbl(d)) + end) + itp('cares about locks and read-only items', function() + local d_lua = {tv_locked=1, tv_fixed=2, di_ro=3, di_ro_sbx=4} + local d = dict(d_lua) + local dis = dict_items(d) + dis.tv_locked.di_tv.v_lock = lib.VAR_LOCKED + dis.tv_fixed.di_tv.v_lock = lib.VAR_FIXED + dis.di_ro.di_flags = bit.bor(dis.di_ro.di_flags, lib.DI_FLAGS_RO) + dis.di_ro_sbx.di_flags = bit.bor(dis.di_ro_sbx.di_flags, lib.DI_FLAGS_RO_SBX) + lib.sandbox = true + local d1 = dict({tv_locked=41}) + local d2 = dict({tv_fixed=42}) + local d3 = dict({di_ro=43}) + local d4 = dict({di_ro_sbx=44}) + tv_dict_extend(d, d1, 'force', 'E741: Value is locked: extend() argument') + tv_dict_extend(d, d2, 'force', 'E742: Cannot change value of extend() argument') + tv_dict_extend(d, d3, 'force', 'E46: Cannot change read-only variable "extend() argument"') + tv_dict_extend(d, d4, 'force', 'E794: Cannot set variable in the sandbox: "extend() argument"') + eq(d_lua, dct2tbl(d)) + lib.sandbox = false + tv_dict_extend(d, d4, 'force') + d_lua.di_ro_sbx = 44 + eq(d_lua, dct2tbl(d)) + end) + end) end) end) -- cgit From 218fa1d8069c1f1d8c5154b1abc2b686adc0c742 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 14:39:34 +0300 Subject: charset: Remove useless condition from vim_iswordc_tab --- src/nvim/charset.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 6c22108853..ad0efa2c28 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -787,10 +787,9 @@ bool vim_iswordc(int c) bool vim_iswordc_tab(const int c, const uint64_t *const chartab) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - if (c >= 0x100) { - return utf_class(c) >= 2; - } - return c > 0 && c < 0x100 && GET_CHARTAB_TAB(chartab, c) != 0; + return (c >= 0x100 + ? (utf_class(c) >= 2) + : (c > 0 && GET_CHARTAB_TAB(chartab, c) != 0)); } /// Check that "c" is a keyword character: -- cgit From 368a61c5259384d3c32cef4c482953ec318cf900 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:00:36 +0300 Subject: unittests: Add tv_dict_copy tests --- test/unit/eval/typval_spec.lua | 175 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 165 insertions(+), 10 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 00dc230e89..f81f7561b9 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -29,6 +29,7 @@ local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict local dict_items = eval_helpers.dict_items +local list_items = eval_helpers.list_items local empty_list = eval_helpers.empty_list local lua2typvalt = eval_helpers.lua2typvalt local typvalt2lua = eval_helpers.typvalt2lua @@ -43,16 +44,6 @@ local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', './src/nvim/eval.h', './src/nvim/vim.h') -local function list_items(l) - local lis = {} - local li = l.lv_first - for i = 1, l.lv_len do - lis[i] = ffi.gc(li, nil) - li = li.li_next - end - return lis -end - local function list_watch_alloc(li) return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) end @@ -2032,5 +2023,169 @@ describe('typval.c', function() eq(d_lua, dct2tbl(d)) end) end) + describe('equal()', function() + local function tv_dict_equal(d1, d2, ic, recursive) + return lib.tv_dict_equal(d1, d2, ic or false, recursive or false) + end + itp('works', function() + eq(true, tv_dict_equal(nil, nil)) + local d1 = dict() + alloc_log:check({a.dict(d1)}) + eq(1, d1.dv_refcount) + eq(false, tv_dict_equal(nil, d1)) + eq(false, tv_dict_equal(d1, nil)) + eq(true, tv_dict_equal(d1, d1)) + eq(1, d1.dv_refcount) + alloc_log:check({}) + local d_upper = dict({a='TEST'}) + local dis_upper = dict_items(d_upper) + local d_lower = dict({a='test'}) + local dis_lower = dict_items(d_lower) + local d_kupper_upper = dict({A='TEST'}) + local dis_kupper_upper = dict_items(d_kupper_upper) + local d_kupper_lower = dict({A='test'}) + local dis_kupper_lower = dict_items(d_kupper_lower) + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d_upper), + a.di(dis_upper.a), + a.str(dis_upper.a.di_tv.vval.v_string), + + a.dict(d_lower), + a.di(dis_lower.a), + a.str(dis_lower.a.di_tv.vval.v_string), + + a.dict(d_kupper_upper), + a.di(dis_kupper_upper.A), + a.str(dis_kupper_upper.A.di_tv.vval.v_string), + + a.dict(d_kupper_lower), + a.di(dis_kupper_lower.A), + a.str(dis_kupper_lower.A.di_tv.vval.v_string), + }) + eq(true, tv_dict_equal(d_upper, d_upper)) + eq(true, tv_dict_equal(d_upper, d_upper, true)) + eq(false, tv_dict_equal(d_upper, d_lower, false)) + eq(true, tv_dict_equal(d_upper, d_lower, true)) + eq(true, tv_dict_equal(d_kupper_upper, d_kupper_lower, true)) + eq(false, tv_dict_equal(d_kupper_upper, d_lower, true)) + eq(false, tv_dict_equal(d_kupper_upper, d_upper, true)) + eq(true, tv_dict_equal(d_upper, d_upper, true, true)) + alloc_log:check({}) + end) + end) + describe('copy()', function() + local function tv_dict_copy(...) + return ffi.gc(lib.tv_dict_copy(...), lib.tv_dict_unref) + end + itp('copies NULL correctly', function() + eq(nil, lib.tv_dict_copy(nil, nil, true, 0)) + eq(nil, lib.tv_dict_copy(nil, nil, false, 0)) + eq(nil, lib.tv_dict_copy(nil, nil, true, 1)) + eq(nil, lib.tv_dict_copy(nil, nil, false, 1)) + end) + itp('copies dict correctly without converting items', function() + do + local v = {a={['«']='»'}, b={'„'}, ['1']=1, ['«»']='“', ns=null_string, nl=null_list, nd=null_dict} + local d_tv = lua2typvalt(v) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_copy1 = tv_dict_copy(nil, d, false, 0) + eq(2, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(2, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_copy1 = dict_items(d_copy1) + eq(dis.a.di_tv.vval.v_dict, dis_copy1.a.di_tv.vval.v_dict) + eq(dis.b.di_tv.vval.v_list, dis_copy1.b.di_tv.vval.v_list) + eq(v, dct2tbl(d_copy1)) + alloc_log:clear() + lib.tv_dict_free(ffi.gc(d_copy1, nil)) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_deepcopy1 = tv_dict_copy(nil, d, true, 0) + neq(nil, d_deepcopy1) + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_deepcopy1 = dict_items(d_deepcopy1) + neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) + neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) + eq(v, dct2tbl(d_deepcopy1)) + local di_deepcopy1 = first_di(dis_deepcopy1.a.di_tv.vval.v_dict) + alloc_log:clear() + end + collectgarbage() + end) + itp('copies dict correctly and converts items', function() + local vc = ffi.gc(ffi.new('vimconv_T[1]'), function(vc) + lib.convert_setup(vc, nil, nil) + end) + -- UTF-8 ↔ latin1 conversions need no iconv + eq(OK, lib.convert_setup(vc, to_cstr('utf-8'), to_cstr('latin1'))) + + local v = {a={['«']='»'}, b={'„'}, ['1']=1, ['«»']='“', ns=null_string, nl=null_list, nd=null_dict} + local d_tv = lua2typvalt(v) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + alloc_log:clear() + + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local d_deepcopy1 = tv_dict_copy(vc, d, true, 0) + neq(nil, d_deepcopy1) + eq(1, dis.a.di_tv.vval.v_dict.dv_refcount) + eq(1, dis.b.di_tv.vval.v_list.lv_refcount) + local dis_deepcopy1 = dict_items(d_deepcopy1) + neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) + neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) + eq({a={['\171']='\187'}, b={'\191'}, ['1']=1, ['\171\187']='\191', ns=null_string, nl=null_list, nd=null_dict}, + dct2tbl(d_deepcopy1)) + alloc_log:clear_tmp_allocs() + alloc_log:clear() + end) + itp('returns different/same containers with(out) copyID', function() + local d_inner_tv = lua2typvalt({}) + local d_tv = lua2typvalt({a=d_inner_tv, b=d_inner_tv}) + eq(3, d_inner_tv.vval.v_dict.dv_refcount) + local d = d_tv.vval.v_dict + local dis = dict_items(d) + eq(dis.a.di_tv.vval.v_dict, dis.b.di_tv.vval.v_dict) + + local d_copy1 = tv_dict_copy(nil, d, true, 0) + local dis_copy1 = dict_items(d_copy1) + neq(dis_copy1.a.di_tv.vval.v_dict, dis_copy1.b.di_tv.vval.v_dict) + eq({a={}, b={}}, dct2tbl(d_copy1)) + + local d_copy2 = tv_dict_copy(nil, d, true, 2) + local dis_copy2 = dict_items(d_copy2) + eq(dis_copy2.a.di_tv.vval.v_dict, dis_copy2.b.di_tv.vval.v_dict) + eq({a={}, b={}}, dct2tbl(d_copy2)) + + eq(3, d_inner_tv.vval.v_dict.dv_refcount) + end) + itp('works with self-referencing dict with copyID', function() + local d_tv = lua2typvalt({}) + local d = d_tv.vval.v_dict + eq(1, d.dv_refcount) + lib.tv_dict_add_dict(d, 'test', 4, d) + eq(2, d.dv_refcount) + + local d_copy1 = tv_dict_copy(nil, d, true, 2) + eq(2, d_copy1.dv_refcount) + local v = {} + v.test = v + eq(v, dct2tbl(d_copy1)) + + lib.tv_dict_clear(d) + eq(1, d.dv_refcount) + + lib.tv_dict_clear(d_copy1) + eq(1, d_copy1.dv_refcount) + end) + end) end) end) -- cgit From e43de6bb3e1efb58669ab904c5672f4ae87003c0 Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:04:59 +0300 Subject: unittests: Add test for tv_dict_set_keys_readonly --- test/unit/eval/typval_spec.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f81f7561b9..0d894ff8b0 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2187,5 +2187,18 @@ describe('typval.c', function() eq(1, d_copy1.dv_refcount) end) end) + describe('set_keys_readonly()', function() + itp('works', function() + local d = dict({a=true}) + local dis = dict_items(d) + alloc_log:check({a.dict(d), a.di(dis.a)}) + eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) + eq(0, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) + lib.tv_dict_set_keys_readonly(d) + alloc_log:check({}) + eq(lib.DI_FLAGS_RO, bit.band(dis.a.di_flags, lib.DI_FLAGS_RO)) + eq(lib.DI_FLAGS_FIX, bit.band(dis.a.di_flags, lib.DI_FLAGS_FIX)) + end) + end) end) end) -- cgit From f0bbd1e825841c55a1f75d66a9caeaa50cc2259c Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:35:12 +0300 Subject: unittests: Add tests for tv_clear() --- src/nvim/eval/typval.c | 5 +-- test/unit/eval/typval_spec.lua | 73 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 620da0032e..c245b9222e 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1840,10 +1840,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, /// Free memory for a variable value and set the value to NULL or 0 /// /// @param[in,out] tv Value to free. -void tv_clear(typval_T *tv) +void tv_clear(typval_T *const tv) { if (tv != NULL && tv->v_type != VAR_UNKNOWN) { - const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear argument"); + const int evn_ret = encode_vim_to_nothing(NULL, tv, + _("tv_clear() argument")); (void)evn_ret; assert(evn_ret == OK); } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0d894ff8b0..8b0470d3b7 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -55,12 +55,16 @@ local function list_watch(l, li) end local function get_alloc_rets(exp_log, res) + setmetatable(res, { + __index={ + freed=function(r, n) return {func='free', args={r[n]}} end + } + }) for i = 1,#exp_log do if ({malloc=true, calloc=true})[exp_log[i].func] then res[#res + 1] = exp_log[i].ret end end - res.freed = function(r, n) return {func='free', args={r[n]}} end return exp_log end @@ -2201,4 +2205,71 @@ describe('typval.c', function() end) end) end) + describe('tv', function() + describe('alloc', function() + describe('list ret()', function() + itp('works', function() + local rettv = typvalt(lib.VAR_UNKNOWN) + local l = lib.tv_list_alloc_ret(rettv) + eq(empty_list, typvalt2lua(rettv)) + eq(rettv.vval.v_list, l) + end) + end) + describe('dict ret()', function() + itp('works', function() + local rettv = typvalt(lib.VAR_UNKNOWN) + lib.tv_dict_alloc_ret(rettv) + eq({}, typvalt2lua(rettv)) + end) + end) + end) + describe('clear()', function() + itp('works', function() + local function deffrees(alloc_rets) + local ret = {} + for i = #alloc_rets, 1, -1 do + ret[#alloc_rets - i + 1] = alloc_rets:freed(i) + end + return ret + end + local function defalloc() + return {} + end + alloc_log:check({}) + lib.tv_clear(nil) + alloc_log:check({}) + local ll = {} + local ll_l = nil + ll[1] = ll + local dd = {} + dd.dd = dd + for _, v in ipairs({ + {nil}, + {null_string, nil, function() return {a.freed(alloc_log.null)} end}, + {0}, + {int(0)}, + {true}, + {false}, + {{}, function(tv) return {a.dict(tv.vval.v_dict)} end}, + {empty_list, function(tv) return {a.list(tv.vval.v_list)} end}, + {ll, function(tv) + ll_l = tv.vval.v_list + return {a.list(tv.vval.v_list), a.li(tv.vval.v_list.lv_first)} + end, defalloc}, + {dd, function(tv) + dd_d = tv.vval.v_dict + return {a.dict(tv.vval.v_dict), a.di(first_di(tv.vval.v_dict))} + end, defalloc}, + }) do + local tv = lua2typvalt(v[1]) + local alloc_rets = {} + alloc_log:check(get_alloc_rets((v[2] or defalloc)(tv), alloc_rets)) + lib.tv_clear(tv) + alloc_log:check((v[3] or deffrees)(alloc_rets)) + end + eq(1, ll_l.lv_refcount) + eq(1, dd_d.dv_refcount) + end) + end) + end) end) -- cgit From ed4948a93317bf801eb2454fd5597a4388730a7b Mon Sep 17 00:00:00 2001 From: ZyX Date: Mon, 13 Mar 2017 19:49:13 +0300 Subject: unittests: Test tv_copy() --- test/unit/eval/helpers.lua | 8 +++---- test/unit/eval/typval_spec.lua | 47 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index a3cb062b7b..fa76113756 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -132,10 +132,10 @@ local function typvalt2lua_tab_init() typvalt2lua_tab = { [tonumber(eval.VAR_SPECIAL)] = function(t) return ({ - [eval.kSpecialVarFalse] = false, - [eval.kSpecialVarNull] = nil_value, - [eval.kSpecialVarTrue] = true, - })[t.vval.v_special] + [tonumber(eval.kSpecialVarFalse)] = false, + [tonumber(eval.kSpecialVarNull)] = nil_value, + [tonumber(eval.kSpecialVarTrue)] = true, + })[tonumber(t.vval.v_special)] end, [tonumber(eval.VAR_NUMBER)] = function(t) return {[type_key]=int_type, value=tonumber(t.vval.v_number)} diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 8b0470d3b7..9745a432c3 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -25,6 +25,7 @@ local typvalt = eval_helpers.typvalt local type_key = eval_helpers.type_key local li_alloc = eval_helpers.li_alloc local first_di = eval_helpers.first_di +local nil_value = eval_helpers.nil_value local func_type = eval_helpers.func_type local null_list = eval_helpers.null_list local null_dict = eval_helpers.null_dict @@ -2223,6 +2224,9 @@ describe('typval.c', function() end) end) end) + local function defalloc() + return {} + end describe('clear()', function() itp('works', function() local function deffrees(alloc_rets) @@ -2232,9 +2236,6 @@ describe('typval.c', function() end return ret end - local function defalloc() - return {} - end alloc_log:check({}) lib.tv_clear(nil) alloc_log:check({}) @@ -2244,12 +2245,13 @@ describe('typval.c', function() local dd = {} dd.dd = dd for _, v in ipairs({ - {nil}, + {nil_value}, {null_string, nil, function() return {a.freed(alloc_log.null)} end}, {0}, {int(0)}, {true}, {false}, + {'true', function(tv) return {a.str(tv.vval.v_string)} end}, {{}, function(tv) return {a.dict(tv.vval.v_dict)} end}, {empty_list, function(tv) return {a.list(tv.vval.v_list)} end}, {ll, function(tv) @@ -2271,5 +2273,42 @@ describe('typval.c', function() eq(1, dd_d.dv_refcount) end) end) + describe('copy()', function() + itp('works', function() + local function strallocs(tv) + return {a.str(tv.vval.v_string)} + end + for _, v in ipairs({ + {nil_value}, + {null_string}, + {0}, + {int(0)}, + {true}, + {false}, + {{}, function(tv) return {a.dict(tv.vval.v_dict)} end, nil, function(from, to) + eq(2, to.vval.v_dict.dv_refcount) + eq(to.vval.v_dict, from.vval.v_dict) + end}, + {empty_list, function(tv) return {a.list(tv.vval.v_list)} end, nil, function(from, to) + eq(2, to.vval.v_list.lv_refcount) + eq(to.vval.v_list, from.vval.v_list) + end}, + {'test', strallocs, strallocs, function(from, to) + neq(to.vval.v_string, from.vval.v_string) + end}, + }) do + local from = lua2typvalt(v[1]) + alloc_log:check((v[2] or defalloc)(from)) + local to = typvalt(lib.VAR_UNKNOWN) + lib.tv_copy(from, to) + local res = v[1] + eq(res, typvalt2lua(to)) + alloc_log:check((v[3] or defalloc)(to)) + if v[4] then + v[4](from, to) + end + end + end) + end) end) end) -- cgit From 630ff33dc144a64b5488b4132c0fc4351a5c84db Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 19:52:20 +0300 Subject: unittests: Test locks section --- src/nvim/eval/typval.c | 7 +-- test/unit/eval/typval_spec.lua | 114 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index c245b9222e..185dd0e86c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1956,6 +1956,7 @@ void tv_copy(typval_T *const from, typval_T *const to) /// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. /// @param[in] lock True if it is needed to lock an item, false to unlock. void tv_item_lock(typval_T *const tv, const int deep, const bool lock) + FUNC_ATTR_NONNULL_ALL { // TODO(ZyX-I): Make this not recursive static int recurse = 0; @@ -2031,13 +2032,13 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) bool tv_islocked(const typval_T *const tv) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return ((tv->v_lock & VAR_LOCKED) + return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock & VAR_LOCKED)) + && (tv->vval.v_list->lv_lock == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL - && (tv->vval.v_dict->dv_lock & VAR_LOCKED))); + && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); } /// Return true if typval is locked diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 9745a432c3..0619e43d08 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2310,5 +2310,119 @@ describe('typval.c', function() end end) end) + describe('item_lock()', function() + itp('does not alter VAR_PARTIAL', function() + local p_tv = lua2typvalt({ + [type_key]=func_type, + value='tr', + dict={}, + }) + lib.tv_item_lock(p_tv, -1, true) + eq(lib.VAR_UNLOCKED, p_tv.vval.v_partial.pt_dict.dv_lock) + end) + itp('does not change VAR_FIXED values', function() + local d_tv = lua2typvalt({}) + local l_tv = lua2typvalt(empty_list) + alloc_log:clear() + d_tv.v_lock = lib.VAR_FIXED + d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED + l_tv.v_lock = lib.VAR_FIXED + l_tv.vval.v_list.lv_lock = lib.VAR_FIXED + lib.tv_item_lock(d_tv, 1, true) + lib.tv_item_lock(l_tv, 1, true) + eq(lib.VAR_FIXED, d_tv.v_lock) + eq(lib.VAR_FIXED, l_tv.v_lock) + eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) + eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) + lib.tv_item_lock(d_tv, 1, false) + lib.tv_item_lock(l_tv, 1, false) + eq(lib.VAR_FIXED, d_tv.v_lock) + eq(lib.VAR_FIXED, l_tv.v_lock) + eq(lib.VAR_FIXED, d_tv.vval.v_dict.dv_lock) + eq(lib.VAR_FIXED, l_tv.vval.v_list.lv_lock) + alloc_log:check({}) + end) + itp('works with NULL values', function() + local l_tv = lua2typvalt(null_list) + local d_tv = lua2typvalt(null_dict) + local s_tv = lua2typvalt(null_string) + alloc_log:clear() + lib.tv_item_lock(l_tv, 1, true) + lib.tv_item_lock(d_tv, 1, true) + lib.tv_item_lock(s_tv, 1, true) + eq(null_list, typvalt2lua(l_tv)) + eq(null_dict, typvalt2lua(d_tv)) + eq(null_string, typvalt2lua(s_tv)) + eq(lib.VAR_LOCKED, d_tv.v_lock) + eq(lib.VAR_LOCKED, l_tv.v_lock) + eq(lib.VAR_LOCKED, s_tv.v_lock) + alloc_log:check({}) + end) + end) + describe('islocked()', function() + itp('works with NULL values', function() + local l_tv = lua2typvalt(null_list) + local d_tv = lua2typvalt(null_dict) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + end) + itp('works', function() + local tv = lua2typvalt() + local d_tv = lua2typvalt({}) + local l_tv = lua2typvalt(empty_list) + alloc_log:clear() + eq(false, lib.tv_islocked(tv)) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + tv.v_lock = lib.VAR_LOCKED + d_tv.v_lock = lib.VAR_LOCKED + l_tv.v_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(tv)) + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_UNLOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_UNLOCKED + eq(true, lib.tv_islocked(tv)) + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + tv.v_lock = lib.VAR_FIXED + d_tv.v_lock = lib.VAR_FIXED + l_tv.v_lock = lib.VAR_FIXED + eq(false, lib.tv_islocked(tv)) + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_LOCKED + l_tv.vval.v_list.lv_lock = lib.VAR_LOCKED + eq(true, lib.tv_islocked(l_tv)) + eq(true, lib.tv_islocked(d_tv)) + d_tv.vval.v_dict.dv_lock = lib.VAR_FIXED + l_tv.vval.v_list.lv_lock = lib.VAR_FIXED + eq(false, lib.tv_islocked(l_tv)) + eq(false, lib.tv_islocked(d_tv)) + alloc_log:check({}) + end) + end) + describe('check_lock()', function() + local function tv_check_lock(lock, name, name_len, emsg) + return check_emsg(function() + return lib.tv_check_lock(lock, name, name_len) + end, emsg) + end + itp('works', function() + eq(false, tv_check_lock(lib.VAR_UNLOCKED, 'test', 3)) + eq(true, tv_check_lock(lib.VAR_LOCKED, 'test', 3, + 'E741: Value is locked: tes')) + eq(true, tv_check_lock(lib.VAR_FIXED, 'test', 3, + 'E742: Cannot change value of tes')) + eq(true, tv_check_lock(lib.VAR_LOCKED, nil, 0, + 'E741: Value is locked: Unknown')) + eq(true, tv_check_lock(lib.VAR_FIXED, nil, 0, + 'E742: Cannot change value of Unknown')) + end) + end) end) end) -- cgit From 389274bef77798af83013bda1e4f1c261db38de5 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:01:08 +0300 Subject: unittests: Add tv_equal() tests --- test/unit/eval/typval_spec.lua | 121 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 0619e43d08..74dba4377f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2424,5 +2424,126 @@ describe('typval.c', function() 'E742: Cannot change value of Unknown')) end) end) + describe('equal()', function() + itp('compares empty and NULL lists correctly', function() + local l = lua2typvalt(empty_list) + local l2 = lua2typvalt(empty_list) + local nl = lua2typvalt(null_list) + + -- NULL lists are not equal to empty lists + eq(false, lib.tv_equal(l, nl, true, false)) + eq(false, lib.tv_equal(nl, l, false, false)) + eq(false, lib.tv_equal(nl, l, false, true)) + eq(false, lib.tv_equal(l, nl, true, true)) + + -- Yet NULL lists are equal themselves + eq(true, lib.tv_equal(nl, nl, true, false)) + eq(true, lib.tv_equal(nl, nl, false, false)) + eq(true, lib.tv_equal(nl, nl, false, true)) + eq(true, lib.tv_equal(nl, nl, true, true)) + + -- As well as empty lists + eq(true, lib.tv_equal(l, l, true, false)) + eq(true, lib.tv_equal(l, l2, false, false)) + eq(true, lib.tv_equal(l2, l, false, true)) + eq(true, lib.tv_equal(l2, l2, true, true)) + end) + -- Must not use recursive=true argument in the following tests because it + -- indicates that tv_equal_recurse_limit and recursive_cnt were set which + -- is essential. This argument will be set when comparing inner lists. + itp('compares lists correctly when case is not ignored', function() + local l1 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l2 = lua2typvalt({'abc', {1, 2, 'Abc'}}) + local l3 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'Def'}) + local l4 = lua2typvalt({'abc', {1, 2, 'Abc', 4}, 'def'}) + local l5 = lua2typvalt({'Abc', {1, 2, 'Abc'}, 'def'}) + local l6 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l7 = lua2typvalt({'abc', {1, 2, 'abc'}, 'def'}) + local l8 = lua2typvalt({'abc', nil, 'def'}) + local l9 = lua2typvalt({'abc', {1, 2, nil}, 'def'}) + + eq(true, lib.tv_equal(l1, l1, false, false)) + eq(false, lib.tv_equal(l1, l2, false, false)) + eq(false, lib.tv_equal(l1, l3, false, false)) + eq(false, lib.tv_equal(l1, l4, false, false)) + eq(false, lib.tv_equal(l1, l5, false, false)) + eq(true, lib.tv_equal(l1, l6, false, false)) + eq(false, lib.tv_equal(l1, l7, false, false)) + eq(false, lib.tv_equal(l1, l8, false, false)) + eq(false, lib.tv_equal(l1, l9, false, false)) + end) + itp('compares lists correctly when case is ignored', function() + local l1 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l2 = lua2typvalt({'abc', {1, 2, 'Abc'}}) + local l3 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'Def'}) + local l4 = lua2typvalt({'abc', {1, 2, 'Abc', 4}, 'def'}) + local l5 = lua2typvalt({'Abc', {1, 2, 'Abc'}, 'def'}) + local l6 = lua2typvalt({'abc', {1, 2, 'Abc'}, 'def'}) + local l7 = lua2typvalt({'abc', {1, 2, 'abc'}, 'def'}) + local l8 = lua2typvalt({'abc', nil, 'def'}) + local l9 = lua2typvalt({'abc', {1, 2, nil}, 'def'}) + + eq(true, lib.tv_equal(l1, l1, true, false)) + eq(false, lib.tv_equal(l1, l2, true, false)) + eq(true, lib.tv_equal(l1, l3, true, false)) + eq(false, lib.tv_equal(l1, l4, true, false)) + eq(true, lib.tv_equal(l1, l5, true, false)) + eq(true, lib.tv_equal(l1, l6, true, false)) + eq(true, lib.tv_equal(l1, l7, true, false)) + eq(false, lib.tv_equal(l1, l8, true, false)) + eq(false, lib.tv_equal(l1, l9, true, false)) + end) + local function tv_equal(d1, d2, ic, recursive) + return lib.tv_equal(d1, d2, ic or false, recursive or false) + end + itp('works with dictionaries', function() + local nd = lua2typvalt(null_dict) + eq(true, tv_equal(nd, nd)) + alloc_log:check({}) + local d1 = lua2typvalt({}) + alloc_log:check({a.dict(d1.vval.v_dict)}) + eq(1, d1.vval.v_dict.dv_refcount) + eq(false, tv_equal(nd, d1)) + eq(false, tv_equal(d1, nd)) + eq(true, tv_equal(d1, d1)) + eq(1, d1.vval.v_dict.dv_refcount) + alloc_log:check({}) + local d_upper = lua2typvalt({a='TEST'}) + local dis_upper = dict_items(d_upper.vval.v_dict) + local d_lower = lua2typvalt({a='test'}) + local dis_lower = dict_items(d_lower.vval.v_dict) + local d_kupper_upper = lua2typvalt({A='TEST'}) + local dis_kupper_upper = dict_items(d_kupper_upper.vval.v_dict) + local d_kupper_lower = lua2typvalt({A='test'}) + local dis_kupper_lower = dict_items(d_kupper_lower.vval.v_dict) + alloc_log:clear_tmp_allocs() + alloc_log:check({ + a.dict(d_upper.vval.v_dict), + a.di(dis_upper.a), + a.str(dis_upper.a.di_tv.vval.v_string), + + a.dict(d_lower.vval.v_dict), + a.di(dis_lower.a), + a.str(dis_lower.a.di_tv.vval.v_string), + + a.dict(d_kupper_upper.vval.v_dict), + a.di(dis_kupper_upper.A), + a.str(dis_kupper_upper.A.di_tv.vval.v_string), + + a.dict(d_kupper_lower.vval.v_dict), + a.di(dis_kupper_lower.A), + a.str(dis_kupper_lower.A.di_tv.vval.v_string), + }) + eq(true, tv_equal(d_upper, d_upper)) + eq(true, tv_equal(d_upper, d_upper, true)) + eq(false, tv_equal(d_upper, d_lower, false)) + eq(true, tv_equal(d_upper, d_lower, true)) + eq(true, tv_equal(d_kupper_upper, d_kupper_lower, true)) + eq(false, tv_equal(d_kupper_upper, d_lower, true)) + eq(false, tv_equal(d_kupper_upper, d_upper, true)) + eq(true, tv_equal(d_upper, d_upper, true, true)) + alloc_log:check({}) + end) + end) end) end) -- cgit From 49195063fd864960437b84cae5fc8b2ca9885d59 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:15:37 +0300 Subject: unittests: Add tv_check… tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/eval/typval_spec.lua | 98 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 74dba4377f..143f4544e6 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1381,7 +1381,7 @@ describe('typval.c', function() end) describe('dict', function() describe('watcher', function() - describe('add/remove', function() + describe('add()/remove()', function() itp('works with an empty key', function() local d = dict({}) eq({}, dict_watchers(d)) @@ -1491,7 +1491,7 @@ describe('typval.c', function() end) end) describe('item', function() - describe('alloc/free', function() + describe('alloc()/free()', function() local function check_tv_dict_item_alloc_len(s, len, tv, more_frees) local di if len == nil then @@ -1527,7 +1527,7 @@ describe('typval.c', function() check_tv_dict_item_alloc_len('', 0, tv, {a.freed(tv.vval.v_string)}) end) end) - describe('add/remove', function() + describe('add()/remove()', function() itp('works', function() local d = dict() eq({}, dct2tbl(d)) @@ -2545,5 +2545,97 @@ describe('typval.c', function() alloc_log:check({}) end) end) + describe('check', function() + describe('str_or_nr()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E805: Expected a Number or a String, Float found'}, + {lib.VAR_PARTIAL, 'E703: Expected a Number or a String, Funcref found'}, + {lib.VAR_FUNC, 'E703: Expected a Number or a String, Funcref found'}, + {lib.VAR_LIST, 'E745: Expected a Number or a String, List found'}, + {lib.VAR_DICT, 'E728: Expected a Number or a String, Dictionary found'}, + {lib.VAR_SPECIAL, 'E5300: Expected a Number or a String'}, + {lib.VAR_UNKNOWN, 'E685: Internal error: tv_check_str_or_nr(UNKNOWN)'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_str_or_nr(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('num()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E805: Using a Float as a Number'}, + {lib.VAR_PARTIAL, 'E703: Using a Funcref as a Number'}, + {lib.VAR_FUNC, 'E703: Using a Funcref as a Number'}, + {lib.VAR_LIST, 'E745: Using a List as a Number'}, + {lib.VAR_DICT, 'E728: Using a Dictionary as a Number'}, + {lib.VAR_SPECIAL, nil}, + {lib.VAR_UNKNOWN, 'E685: using an invalid value as a Number'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_num(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('str()', function() + itp('works', function() + local tv = typvalt() + local mem = lib.xmalloc(1) + tv.vval.v_list = mem -- Should crash when actually accessed + alloc_log:clear() + for _, v in ipairs({ + {lib.VAR_NUMBER, nil}, + {lib.VAR_FLOAT, 'E806: using Float as a String'}, + {lib.VAR_PARTIAL, 'E729: using Funcref as a String'}, + {lib.VAR_FUNC, 'E729: using Funcref as a String'}, + {lib.VAR_LIST, 'E730: using List as a String'}, + {lib.VAR_DICT, 'E731: using Dictionary as a String'}, + {lib.VAR_SPECIAL, nil}, + {lib.VAR_UNKNOWN, 'E908: using an invalid value as a String'}, + }) do + local typ = v[1] + local emsg = v[2] + local ret = true + if emsg then ret = false end + tv.v_type = typ + eq(ret, check_emsg(function() return lib.tv_check_str(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + end) end) end) -- cgit From 4536c064e43dd93337d4809f6178c1ffc7034ebe Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:17:00 +0300 Subject: unittests: Move tv_dict_add* tests to a proper describe() block --- test/unit/eval/typval_spec.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 143f4544e6..101adf3de4 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1768,7 +1768,9 @@ describe('typval.c', function() {tv_dict_get_callback(d, 'testt', 5)}) end) end) - describe('dict_add()', function() + end) + describe('add', function() + describe('()', function() itp('works', function() local di = lib.tv_dict_item_alloc_len('t-est', 5) alloc_log:check({a.di(di, 't-est')}) @@ -1789,7 +1791,7 @@ describe('typval.c', function() 'E685: Internal error: hash_add()')) end) end) - describe('dict_add_list()', function() + describe('list()', function() itp('works', function() local l = list(1, 2, 3) alloc_log:clear() @@ -1815,7 +1817,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_dict()', function() + describe('dict()', function() itp('works', function() local d2 = dict({foo=42}) alloc_log:clear() @@ -1841,7 +1843,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_nr()', function() + describe('nr()', function() itp('works', function() local d = dict({test=10}) alloc_log:clear() @@ -1861,7 +1863,7 @@ describe('typval.c', function() alloc_log:check({}) end) end) - describe('dict_add_str()', function() + describe('str()', function() itp('works', function() local d = dict({test=10}) alloc_log:clear() -- cgit From e08b27ba4acf22fe180c2a4a6b00f0ea0cfc3a79 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:46:39 +0300 Subject: unittests: Add tv_get number tests --- src/nvim/eval/typval.c | 2 +- test/unit/eval/typval_spec.lua | 124 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 185dd0e86c..48ff3ab623 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2447,7 +2447,7 @@ float_T tv_get_float(const typval_T *const tv) break; } case VAR_UNKNOWN: { - emsgf(_(e_intern2), "get_tv_float(UNKNOWN)"); + emsgf(_(e_intern2), "tv_get_float(UNKNOWN)"); break; } } diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 101adf3de4..f9bb2a942c 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -10,6 +10,7 @@ local eq = helpers.eq local neq = helpers.neq local ffi = helpers.ffi local FAIL = helpers.FAIL +local NULL = helpers.NULL local cimport = helpers.cimport local to_cstr = helpers.to_cstr local alloc_log_new = helpers.alloc_log_new @@ -43,7 +44,8 @@ local concat_tables = global_helpers.concat_tables local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', './src/nvim/mbyte.h', './src/nvim/garray.h', - './src/nvim/eval.h', './src/nvim/vim.h') + './src/nvim/eval.h', './src/nvim/vim.h', + './src/nvim/globals.h') local function list_watch_alloc(li) return ffi.cast('listwatch_T*', ffi.new('listwatch_T[1]', {{lw_item=li}})) @@ -2639,5 +2641,125 @@ describe('typval.c', function() end) end) end) + describe('get', function() + describe('number()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', 0}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_number(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('number_chk()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', 0}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = {v[4], not not emsg} + eq(ret, check_emsg(function() + local err = ffi.new('bool[1]', {false}) + local res = lib.tv_get_number_chk(tv, err) + return {res, err[0]} + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('lnum()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, 100500}, + {lib.VAR_STRING, {v_string=to_cstr('.')}, nil, 46}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E805: Using a Float as a Number', -1}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E703: Using a Funcref as a Number', -1}, + {lib.VAR_FUNC, {v_string=NULL}, 'E703: Using a Funcref as a Number', -1}, + {lib.VAR_LIST, {v_list=NULL}, 'E745: Using a List as a Number', -1}, + {lib.VAR_DICT, {v_dict=NULL}, 'E728: Using a Dictionary as a Number', -1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 1}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_number(UNKNOWN)', -1}, + }) do + lib.curwin.w_cursor.lnum = 46 + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_lnum(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('float()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, 42}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, 'E892: Using a String as a Float', 0}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, 42.53}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E891: Using a Funcref as a Float', 0}, + {lib.VAR_FUNC, {v_string=NULL}, 'E891: Using a Funcref as a Float', 0}, + {lib.VAR_LIST, {v_list=NULL}, 'E893: Using a List as a Float', 0}, + {lib.VAR_DICT, {v_dict=NULL}, 'E894: Using a Dictionary as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, 'E907: Using a special value as a Float', 0}, + {lib.VAR_UNKNOWN, nil, 'E685: Internal error: tv_get_float(UNKNOWN)', 0}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() return lib.tv_get_float(tv) end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + end) end) end) -- cgit From 7826ee1c035a99804dfeba80523c3f2992f7e6b6 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 20:59:59 +0300 Subject: unittests: Add tv_get_string* tests --- test/unit/eval/typval_spec.lua | 166 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index f9bb2a942c..d861f81105 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2760,6 +2760,172 @@ describe('typval.c', function() end end) end) + describe('string()', function() + itp('works', function() + local buf = lib.tv_get_string(lua2typvalt(int(1))) + local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) + neq(buf, buf_chk) + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res = lib.tv_get_string(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_chk()', function() + itp('works', function() + local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res = lib.tv_get_string_chk(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_buf()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', ''}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', ''}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', ''}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', ''}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', ''}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + local res = lib.tv_get_string_buf(tv, buf) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) + describe('string_buf_chk()', function() + itp('works', function() + for _, v in ipairs({ + {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, + {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, + {lib.VAR_FLOAT, {v_float=42.53}, 'E806: using Float as a String', nil}, + {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_FUNC, {v_string=NULL}, 'E729: using Funcref as a String', nil}, + {lib.VAR_LIST, {v_list=NULL}, 'E730: using List as a String', nil}, + {lib.VAR_DICT, {v_dict=NULL}, 'E731: using Dictionary as a String', nil}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarNull}, nil, 'null'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarTrue}, nil, 'true'}, + {lib.VAR_SPECIAL, {v_special=lib.kSpecialVarFalse}, nil, 'false'}, + {lib.VAR_UNKNOWN, nil, 'E908: using an invalid value as a String', nil}, + }) do + local tv = typvalt(v[1], v[2]) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + local res = lib.tv_get_string_buf_chk(tv, buf) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + else + alloc_log:check({}) + end + end + end) + end) end) end) end) -- cgit From 8daf756fb6f92bdeb39e473b34364afd5270dd99 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:07:41 +0300 Subject: unittests: Fix linter errors --- test/unit/eval/tricks_spec.lua | 2 -- test/unit/eval/typval_spec.lua | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/unit/eval/tricks_spec.lua b/test/unit/eval/tricks_spec.lua index ae569bed11..7aa0f0f6e6 100644 --- a/test/unit/eval/tricks_spec.lua +++ b/test/unit/eval/tricks_spec.lua @@ -4,8 +4,6 @@ local eval_helpers = require('test.unit.eval.helpers') local itp = helpers.gen_itp(it) local cimport = helpers.cimport -local to_cstr = helpers.to_cstr -local ffi = helpers.ffi local eq = helpers.eq local eval0 = eval_helpers.eval0 diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index d861f81105..7436115945 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -2124,7 +2124,6 @@ describe('typval.c', function() neq(dis.a.di_tv.vval.v_dict, dis_deepcopy1.a.di_tv.vval.v_dict) neq(dis.b.di_tv.vval.v_list, dis_deepcopy1.b.di_tv.vval.v_list) eq(v, dct2tbl(d_deepcopy1)) - local di_deepcopy1 = first_di(dis_deepcopy1.a.di_tv.vval.v_dict) alloc_log:clear() end collectgarbage() @@ -2247,6 +2246,7 @@ describe('typval.c', function() local ll_l = nil ll[1] = ll local dd = {} + local dd_d = nil dd.dd = dd for _, v in ipairs({ {nil_value}, -- cgit From 29bad04f9e2e70b7c1c198ca0d86d23282bee8a1 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:35:34 +0300 Subject: eval: Do not supply S_LEN to strncmp It may be a macro as well. --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9950f8b196..0d7c585099 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7967,7 +7967,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (s == NULL) { return; } - if (strncmp(s, S_LEN("silent")) == 0) { + if (strncmp(s, "silent", 6) == 0) { msg_silent++; } if (strcmp(s, "silent!") == 0) { -- cgit From f4256243dbdbd2858be716f9f52763bd9a76be4d Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 25 Mar 2017 22:49:23 +0300 Subject: eval: Fix -Werror=unitialized from QB --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0d7c585099..6890bdc522 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2107,7 +2107,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } int len = -1; - char_u *key; + char_u *key = NULL; if (*p == '.') { key = p + 1; for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { -- cgit From 58e34e8d99b01bf3937824fc50502e39a8c39eba Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Mar 2017 03:42:23 +0300 Subject: eval/typval: Allow NULL dict as tv_dict_get_callback() argument Also removes NULL key input: tv_dict_find() does not allow this. --- src/nvim/eval/typval.c | 5 ++--- test/unit/eval/typval_spec.lua | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 48ff3ab623..da58cf5ca9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -825,8 +825,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, /// @param[in] cb2 Second callback to check. /// /// @return True if they are equal, false otherwise. -static bool tv_callback_equal(const Callback *const cb1, - const Callback *const cb2) +bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (cb1->type != cb2->type) { @@ -1240,7 +1239,7 @@ const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, bool tv_dict_get_callback(dict_T *const d, const char *const key, const ptrdiff_t key_len, Callback *const result) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ARG(2, 4) FUNC_ATTR_WARN_UNUSED_RESULT { result->type = kCallbackNone; diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 7436115945..3a4ef0cb92 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -1736,8 +1736,7 @@ describe('typval.c', function() return cb_lua, ret end itp('works with NULL dict', function() - eq({{type='none'}, true}, - {tv_dict_get_callback(nil, nil, 0)}) + eq({{type='none'}, true}, {tv_dict_get_callback(nil, '')}) end) itp('works', function() local lua_d = { -- cgit From 114eaa15f009cbd3e3deb177a2a67affa430fbb8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 01:07:34 +0300 Subject: eval/typval,api/buffer: Fix review comments --- src/nvim/api/buffer.c | 6 +++--- src/nvim/eval/typval.c | 12 ++++-------- test/unit/eval/typval_spec.lua | 3 +++ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 037a6ee1da..26f9a6f592 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -296,7 +296,7 @@ void nvim_buf_set_lines(uint64_t channel_id, tabpage_T *save_curtab = NULL; size_t new_len = replacement.size; size_t old_len = (size_t)(end - start); - ssize_t extra = 0; // lines added to text, can be negative + ptrdiff_t extra = 0; // lines added to text, can be negative char **lines = (new_len != 0) ? xcalloc(new_len, sizeof(char *)) : NULL; for (size_t i = 0; i < new_len; i++) { @@ -342,8 +342,8 @@ void nvim_buf_set_lines(uint64_t channel_id, } } - if ((ssize_t)to_delete > 0) { - extra -= (ssize_t)to_delete; + if (to_delete > 0) { + extra -= (ptrdiff_t)to_delete; } // For as long as possible, replace the existing old_len with the diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index da58cf5ca9..779bb18175 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -164,9 +164,8 @@ void tv_list_free_contents(list_T *const l) } l->lv_len = 0; l->lv_idx_item = NULL; - for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { - lw->lw_item = NULL; - } + l->lv_last = NULL; + assert(l->lv_watch == NULL); } /// Free a list itself, ignoring items it contains @@ -230,13 +229,10 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { - // notify watchers - for (listitem_T *ip = item; ip != NULL; ip = ip->li_next) { + // Notify watchers. + for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; tv_list_watch_fix(l, ip); - if (ip == item2) { - break; - } } if (item2->li_next == NULL) { diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index 3a4ef0cb92..258b5c4c1f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -255,6 +255,9 @@ describe('typval.c', function() eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) alloc_log:clear() + lib.tv_list_watch_remove(l, lws[2]) + lib.tv_list_watch_remove(l, lws[3]) + lib.tv_list_watch_remove(l, lws[1]) lib.tv_list_free(l) alloc_log:check({ a.freed(lis[3]), -- cgit From a1d590a08bd9d40d0e20a9907381573c2d069738 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 02:17:51 +0300 Subject: *: Use const char * in set_one_cmd_context Also renames functions added in master and renamed here. --- src/nvim/charset.c | 23 +-- src/nvim/eval.c | 8 +- src/nvim/ex_cmds2.c | 12 +- src/nvim/ex_docmd.c | 444 +++++++++++++++++++++++++++------------------------ src/nvim/ex_getln.c | 9 +- src/nvim/if_cscope.c | 29 ++-- src/nvim/syntax.c | 69 ++++---- 7 files changed, 311 insertions(+), 283 deletions(-) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index ad0efa2c28..99d3e2dd88 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1397,7 +1397,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, /// /// @return Pointer to character after the skipped whitespace. char_u *skipwhite(const char_u *q) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { const char_u *p = q; while (ascii_iswhite(*p)) { @@ -1406,19 +1407,21 @@ char_u *skipwhite(const char_u *q) return (char_u *)p; } -/// skip over digits +/// Skip over digits /// -/// @param q +/// @param[in] q String to skip digits in. /// /// @return Pointer to the character after the skipped digits. -char_u* skipdigits(char_u *q) +char_u *skipdigits(const char_u *q) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_RET { - char_u *p = q; + const char_u *p = q; while (ascii_isdigit(*p)) { // skip to next non-digit p++; } - return p; + return (char_u *)p; } /// skip over binary digits @@ -1564,17 +1567,17 @@ int vim_tolower(int c) return TOLOWER_LOC(c); } -/// skiptowhite: skip over text until ' ' or '\t' or NUL. +/// Skip over text until ' ' or '\t' or NUL /// -/// @param p +/// @param[in] p Text to skip over. /// /// @return Pointer to the next whitespace or NUL character. -char_u* skiptowhite(char_u *p) +char_u *skiptowhite(const char_u *p) { while (*p != ' ' && *p != '\t' && *p != NUL) { p++; } - return p; + return (char_u *)p; } /// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6890bdc522..80c2fe10d7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9462,8 +9462,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - if (STRCMP(get_tv_string(&argvars[1]), "cmdline") == 0) { - set_one_cmd_context(&xpc, get_tv_string(&argvars[0])); + if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) { + set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); goto theend; } @@ -9484,7 +9484,7 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (xpc.xp_context == EXPAND_CSCOPE) { - set_context_in_cscope_cmd(&xpc, xpc.xp_pattern, CMD_cscope); + set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope); xpc.xp_pattern_len = (int)STRLEN(xpc.xp_pattern); } @@ -14562,7 +14562,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) } else if (title_arg->v_type == VAR_STRING) { title = tv_get_string_chk(title_arg); if (!title) { - // Type error. Error already printed by get_tv_string_chk(). + // Type error. Error already printed by tv_get_string_chk(). return; } } else if (title_arg->v_type == VAR_DICT) { diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 213641667d..eeace789b2 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -957,23 +957,21 @@ char_u *get_profile_name(expand_T *xp, int idx) } /// Handle command line completion for :profile command. -void set_context_in_profile_cmd(expand_T *xp, char_u *arg) +void set_context_in_profile_cmd(expand_T *xp, const char *arg) { - char_u *end_subcmd; - // Default: expand subcommands. xp->xp_context = EXPAND_PROFILE; pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; - end_subcmd = skiptowhite(arg); + char_u *const end_subcmd = skiptowhite((const char_u *)arg); if (*end_subcmd == NUL) { return; } - if (end_subcmd - arg == 5 && STRNCMP(arg, "start", 5) == 0) { + if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) { xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite(end_subcmd); + xp->xp_pattern = skipwhite((const char_u *)end_subcmd); return; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 26cfec991f..0fd4ae48be 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2633,32 +2633,27 @@ int cmd_exists(const char *const name) * perfectly compatible with each other, but then the command line syntax * probably won't change that much -- webb. */ -char_u * -set_one_cmd_context ( +const char * set_one_cmd_context( expand_T *xp, - char_u *buff /* buffer for command string */ + const char *buff // buffer for command string ) { - char_u *p; - char_u *cmd, *arg; - int len = 0; + size_t len = 0; exarg_T ea; - int compl = EXPAND_NOTHING; - int delim; - int forceit = FALSE; - int usefilter = FALSE; /* filter instead of file name */ + int context = EXPAND_NOTHING; + int forceit = false; + int usefilter = false; // Filter instead of file name. ExpandInit(xp); - xp->xp_pattern = buff; - xp->xp_context = EXPAND_COMMANDS; /* Default until we get past command */ + xp->xp_pattern = (char_u *)buff; + xp->xp_context = EXPAND_COMMANDS; // Default until we get past command ea.argt = 0; - /* - * 2. skip comment lines and leading space, colons or bars - */ - for (cmd = buff; vim_strchr((char_u *)" \t:|", *cmd) != NULL; cmd++) - ; - xp->xp_pattern = cmd; + // 2. skip comment lines and leading space, colons or bars + const char *cmd; + for (cmd = buff; strchr(" \t:|", *cmd) != NULL; cmd++) { + } + xp->xp_pattern = (char_u *)cmd; if (*cmd == NUL) return NULL; @@ -2670,14 +2665,15 @@ set_one_cmd_context ( /* * 3. parse a range specifier of the form: addr [,addr] [;addr] .. */ - cmd = skip_range(cmd, &xp->xp_context); + cmd = (const char *)skip_range((const char_u *)cmd, &xp->xp_context); /* * 4. parse command */ - xp->xp_pattern = cmd; - if (*cmd == NUL) + xp->xp_pattern = (char_u *)cmd; + if (*cmd == NUL) { return NULL; + } if (*cmd == '"') { xp->xp_context = EXPAND_NOTHING; return NULL; @@ -2693,6 +2689,7 @@ set_one_cmd_context ( * do accept "keepmarks", "keepalt" and "keepjumps". * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' */ + const char *p; if (*cmd == 'k' && cmd[1] != 'e') { ea.cmdidx = CMD_k; p = cmd + 1; @@ -2715,20 +2712,21 @@ set_one_cmd_context ( } } // check for non-alpha command - if (p == cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) { + if (p == cmd && strchr("@*!=><&~#", *p) != NULL) { p++; } - len = (int)(p - cmd); + len = (size_t)(p - cmd); if (len == 0) { xp->xp_context = EXPAND_UNSUCCESSFUL; return NULL; } for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < (int)CMD_SIZE; - ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) - if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, - (size_t)len) == 0) + ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) { + if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) { break; + } + } if (cmd[0] >= 'A' && cmd[0] <= 'Z') { while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card @@ -2745,16 +2743,15 @@ set_one_cmd_context ( return NULL; if (ea.cmdidx == CMD_SIZE) { - if (*cmd == 's' && vim_strchr((char_u *)"cgriI", cmd[1]) != NULL) { + if (*cmd == 's' && strchr("cgriI", cmd[1]) != NULL) { ea.cmdidx = CMD_substitute; p = cmd + 1; } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') { - ea.cmd = cmd; - p = find_ucmd(&ea, p, NULL, xp, - &compl - ); - if (p == NULL) - ea.cmdidx = CMD_SIZE; /* ambiguous user command */ + ea.cmd = (char_u *)cmd; + p = (const char *)find_ucmd(&ea, (char_u *)p, NULL, xp, &context); + if (p == NULL) { + ea.cmdidx = CMD_SIZE; // Ambiguous user command. + } } } if (ea.cmdidx == CMD_SIZE) { @@ -2777,16 +2774,17 @@ set_one_cmd_context ( ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt; } - arg = skipwhite(p); + const char *arg = (const char *)skipwhite((const char_u *)p); if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) { - if (*arg == '>') { /* append */ - if (*++arg == '>') - ++arg; - arg = skipwhite(arg); - } else if (*arg == '!' && ea.cmdidx == CMD_write) { /* :w !filter */ - ++arg; - usefilter = TRUE; + if (*arg == '>') { // Append. + if (*++arg == '>') { + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); + } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter + arg++; + usefilter = true; } } @@ -2799,23 +2797,24 @@ set_one_cmd_context ( } if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) { - while (*arg == *cmd) /* allow any number of '>' or '<' */ - ++arg; - arg = skipwhite(arg); + while (*arg == *cmd) { // allow any number of '>' or '<' + arg++; + } + arg = (const char *)skipwhite((const char_u *)arg); } /* Does command allow "+command"? */ if ((ea.argt & EDITCMD) && !usefilter && *arg == '+') { /* Check if we're in the +command */ p = arg + 1; - arg = skip_cmd_arg(arg, FALSE); + arg = (const char *)skip_cmd_arg((char_u *)arg, false); /* Still touching the command after '+'? */ if (*arg == NUL) return p; - /* Skip space(s) after +command to get to the real argument */ - arg = skipwhite(arg); + // Skip space(s) after +command to get to the real argument. + arg = (const char *)skipwhite((const char_u *)arg); } /* @@ -2844,19 +2843,18 @@ set_one_cmd_context ( } // no arguments allowed - if (!(ea.argt & EXTRA) && *arg != NUL - && vim_strchr((char_u *)"|\"", *arg) == NULL) { + if (!(ea.argt & EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) { return NULL; } /* Find start of last argument (argument just before cursor): */ p = buff; - xp->xp_pattern = p; - len = (int)STRLEN(buff); + xp->xp_pattern = (char_u *)p; + len = strlen(buff); while (*p && p < buff + len) { if (*p == ' ' || *p == TAB) { - /* argument starts after a space */ - xp->xp_pattern = ++p; + // Argument starts after a space. + xp->xp_pattern = (char_u *)++p; } else { if (*p == '\\' && *(p + 1) != NUL) ++p; /* skip over escaped character */ @@ -2866,25 +2864,26 @@ set_one_cmd_context ( if (ea.argt & XFILE) { int c; - int in_quote = FALSE; - char_u *bow = NULL; /* Beginning of word */ + int in_quote = false; + const char *bow = NULL; // Beginning of word. /* * Allow spaces within back-quotes to count as part of the argument * being expanded. */ - xp->xp_pattern = skipwhite(arg); - p = xp->xp_pattern; + xp->xp_pattern = skipwhite((const char_u *)arg); + p = (const char *)xp->xp_pattern; while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else - c = *p; - if (c == '\\' && p[1] != NUL) - ++p; - else if (c == '`') { + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { + c = (uint8_t)(*p); + } + if (c == '\\' && p[1] != NUL) { + p++; + } else if (c == '`') { if (!in_quote) { - xp->xp_pattern = p; + xp->xp_pattern = (char_u *)p; bow = p + 1; } in_quote = !in_quote; @@ -2897,22 +2896,26 @@ set_one_cmd_context ( || ascii_iswhite(c)) { len = 0; /* avoid getting stuck when space is in 'isfname' */ while (*p != NUL) { - if (has_mbyte) - c = mb_ptr2char(p); - else + if (has_mbyte) { + c = mb_ptr2char((const char_u *)p); + } else { c = *p; - if (c == '`' || vim_isfilec_or_wc(c)) + } + if (c == '`' || vim_isfilec_or_wc(c)) { break; - if (has_mbyte) - len = (*mb_ptr2len)(p); - else + } + if (has_mbyte) { + len = (size_t)(*mb_ptr2len)((const char_u *)p); + } else { len = 1; + } mb_ptr_adv(p); } - if (in_quote) + if (in_quote) { bow = p; - else - xp->xp_pattern = p; + } else { + xp->xp_pattern = (char_u *)p; + } p -= len; } mb_ptr_adv(p); @@ -2922,8 +2925,9 @@ set_one_cmd_context ( * If we are still inside the quotes, and we passed a space, just * expand from there. */ - if (bow != NULL && in_quote) - xp->xp_pattern = bow; + if (bow != NULL && in_quote) { + xp->xp_pattern = (char_u *)bow; + } xp->xp_context = EXPAND_FILES; /* For a shell command more chars need to be escaped. */ @@ -2931,33 +2935,36 @@ set_one_cmd_context ( #ifndef BACKSLASH_IN_FILENAME xp->xp_shell = TRUE; #endif - /* When still after the command name expand executables. */ - if (xp->xp_pattern == skipwhite(arg)) + // When still after the command name expand executables. + if (xp->xp_pattern == skipwhite((const char_u *)arg)) { xp->xp_context = EXPAND_SHELLCMD; + } } - /* Check for environment variable */ - if (*xp->xp_pattern == '$' - ) { - for (p = xp->xp_pattern + 1; *p != NUL; ++p) - if (!vim_isIDc(*p)) + // Check for environment variable. + if (*xp->xp_pattern == '$') { + for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) { + if (!vim_isIDc((uint8_t)(*p))) { break; + } + } if (*p == NUL) { xp->xp_context = EXPAND_ENV_VARS; - ++xp->xp_pattern; - /* Avoid that the assignment uses EXPAND_FILES again. */ - if (compl != EXPAND_USER_DEFINED && compl != EXPAND_USER_LIST) - compl = EXPAND_ENV_VARS; + xp->xp_pattern++; + // Avoid that the assignment uses EXPAND_FILES again. + if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) { + context = EXPAND_ENV_VARS; + } } } /* Check for user names */ if (*xp->xp_pattern == '~') { - for (p = xp->xp_pattern + 1; *p != NUL && *p != '/'; ++p) - ; - /* Complete ~user only if it partially matches a user name. - * A full match ~user will be replaced by user's home - * directory i.e. something like ~user -> /home/user/ */ - if (*p == NUL && p > xp->xp_pattern + 1 + for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) { + } + // Complete ~user only if it partially matches a user name. + // A full match ~user will be replaced by user's home + // directory i.e. something like ~user -> /home/user/ + if (*p == NUL && p > (const char *)xp->xp_pattern + 1 && match_user(xp->xp_pattern + 1) == 1) { xp->xp_context = EXPAND_USER; ++xp->xp_pattern; @@ -2987,7 +2994,7 @@ set_one_cmd_context ( break; case CMD_help: xp->xp_context = EXPAND_HELP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; /* Command modifiers: return the argument. @@ -3030,13 +3037,14 @@ set_one_cmd_context ( if (*arg == NUL || !ends_excmd(*arg)) { /* also complete "None" */ set_context_in_echohl_cmd(xp, arg); - arg = skipwhite(skiptowhite(arg)); + arg = (const char *)skipwhite(skiptowhite((const char_u *)arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = skip_regexp(arg + 1, *arg, p_magic, NULL); + arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg), + p_magic, NULL); } } - return find_nextcmd(arg); + return (const char *)find_nextcmd((char_u *)arg); /* * All completion for the +cmdline_compl feature goes here. @@ -3045,15 +3053,15 @@ set_one_cmd_context ( case CMD_command: /* Check for attributes */ while (*arg == '-') { - arg++; /* Skip "-" */ - p = skiptowhite(arg); + arg++; // Skip "-". + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { - /* Cursor is still in the attribute */ - p = vim_strchr(arg, '='); + // Cursor is still in the attribute. + p = strchr(arg, '='); if (p == NULL) { - /* No "=", so complete attribute names */ + // No "=", so complete attribute names. xp->xp_context = EXPAND_USER_CMD_FLAGS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; return NULL; } @@ -3061,73 +3069,81 @@ set_one_cmd_context ( // their arguments as well. if (STRNICMP(arg, "complete", p - arg) == 0) { xp->xp_context = EXPAND_USER_COMPLETE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "nargs", p - arg) == 0) { xp->xp_context = EXPAND_USER_NARGS; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } else if (STRNICMP(arg, "addr", p - arg) == 0) { xp->xp_context = EXPAND_USER_ADDR_TYPE; - xp->xp_pattern = p + 1; + xp->xp_pattern = (char_u *)p + 1; return NULL; } return NULL; } - arg = skipwhite(p); + arg = (const char *)skipwhite((char_u *)p); } - /* After the attributes comes the new command name */ - p = skiptowhite(arg); + // After the attributes comes the new command name. + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; } - /* And finally comes a normal command */ - return skipwhite(p); + // And finally comes a normal command. + return (const char *)skipwhite((const char_u *)p); case CMD_delcommand: xp->xp_context = EXPAND_USER_COMMANDS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_global: - case CMD_vglobal: - delim = *arg; /* get the delimiter */ - if (delim) - ++arg; /* skip delimiter if there is one */ + case CMD_vglobal: { + const int delim = (uint8_t)(*arg); // Get the delimiter. + if (delim) { + arg++; // Skip delimiter if there is one. + } - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } if (arg[0] != NUL) return arg + 1; break; + } case CMD_and: - case CMD_substitute: - delim = *arg; + case CMD_substitute: { + const int delim = (uint8_t)(*arg); if (delim) { - /* skip "from" part */ - ++arg; - arg = skip_regexp(arg, delim, p_magic, NULL); + // Skip "from" part. + arg++; + arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL); } - /* skip "to" part */ - while (arg[0] != NUL && arg[0] != delim) { - if (arg[0] == '\\' && arg[1] != NUL) - ++arg; - ++arg; + // Skip "to" part. + while (arg[0] != NUL && (uint8_t)arg[0] != delim) { + if (arg[0] == '\\' && arg[1] != NUL) { + arg++; + } + arg++; } - if (arg[0] != NUL) /* skip delimiter */ - ++arg; - while (arg[0] && vim_strchr((char_u *)"|\"#", arg[0]) == NULL) - ++arg; - if (arg[0] != NUL) + if (arg[0] != NUL) { // Skip delimiter. + arg++; + } + while (arg[0] && strchr("|\"#", arg[0]) == NULL) { + arg++; + } + if (arg[0] != NUL) { return arg; + } break; + } case CMD_isearch: case CMD_dsearch: case CMD_ilist: @@ -3137,36 +3153,40 @@ set_one_cmd_context ( case CMD_djump: case CMD_isplit: case CMD_dsplit: - arg = skipwhite(skipdigits(arg)); /* skip count */ - if (*arg == '/') { /* Match regexp, not just whole words */ - for (++arg; *arg && *arg != '/'; arg++) - if (*arg == '\\' && arg[1] != NUL) + // Skip count. + arg = (const char *)skipwhite(skipdigits((const char_u *)arg)); + if (*arg == '/') { // Match regexp, not just whole words. + for (++arg; *arg && *arg != '/'; arg++) { + if (*arg == '\\' && arg[1] != NUL) { arg++; + } + } if (*arg) { - arg = skipwhite(arg + 1); + arg = (const char *)skipwhite((const char_u *)arg + 1); - /* Check for trailing illegal characters */ - if (*arg && vim_strchr((char_u *)"|\"\n", *arg) == NULL) + // Check for trailing illegal characters. + if (*arg && strchr("|\"\n", *arg) == NULL) { xp->xp_context = EXPAND_NOTHING; - else + } else { return arg; + } } } break; case CMD_autocmd: - return set_context_in_autocmd(xp, arg, FALSE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, false); case CMD_doautocmd: case CMD_doautoall: - return set_context_in_autocmd(xp, arg, TRUE); + return (const char *)set_context_in_autocmd(xp, (char_u *)arg, true); case CMD_set: - set_context_in_set_cmd(xp, arg, 0); + set_context_in_set_cmd(xp, (char_u *)arg, 0); break; case CMD_setglobal: - set_context_in_set_cmd(xp, arg, OPT_GLOBAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL); break; case CMD_setlocal: - set_context_in_set_cmd(xp, arg, OPT_LOCAL); + set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL); break; case CMD_tag: case CMD_stag: @@ -3178,15 +3198,16 @@ set_one_cmd_context ( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) + if (*p_wop != NUL) { xp->xp_context = EXPAND_TAGS_LISTFILES; - else + } else { xp->xp_context = EXPAND_TAGS; - xp->xp_pattern = arg; + } + xp->xp_pattern = (char_u *)arg; break; case CMD_augroup: xp->xp_context = EXPAND_AUGROUP; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntax: set_context_in_syntax_cmd(xp, arg); @@ -3203,20 +3224,21 @@ set_one_cmd_context ( case CMD_echoerr: case CMD_call: case CMD_return: - set_context_for_expression(xp, arg, ea.cmdidx); + set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); break; case CMD_unlet: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } xp->xp_context = EXPAND_USER_VARS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_function: case CMD_delfunction: xp->xp_context = EXPAND_USER_FUNC; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_echohl: @@ -3231,33 +3253,37 @@ set_one_cmd_context ( set_context_in_cscope_cmd(xp, arg, ea.cmdidx); break; case CMD_sign: - set_context_in_sign_cmd(xp, arg); + set_context_in_sign_cmd(xp, (char_u *)arg); break; case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: - while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) - arg = xp->xp_pattern + 1; - /*FALLTHROUGH*/ + while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { + arg = (const char *)xp->xp_pattern + 1; + } + // FALLTHROUGH case CMD_buffer: case CMD_sbuffer: case CMD_checktime: xp->xp_context = EXPAND_BUFFERS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_USER: case CMD_USER_BUF: - if (compl != EXPAND_NOTHING) { - /* XFILE: file names are handled above */ + if (context != EXPAND_NOTHING) { + // XFILE: file names are handled above. if (!(ea.argt & XFILE)) { - if (compl == EXPAND_MENUS) - return set_context_in_menu_cmd(xp, cmd, arg, forceit); - if (compl == EXPAND_COMMANDS) + if (context == EXPAND_MENUS) { + return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, + (char_u *)arg, forceit); + } else if (context == EXPAND_COMMANDS) { return arg; - if (compl == EXPAND_MAPPINGS) - return set_context_in_map_cmd(xp, (char_u *)"map", - arg, forceit, FALSE, FALSE, CMD_map); - /* Find start of last argument. */ + } else if (context == EXPAND_MAPPINGS) { + return (const char *)set_context_in_map_cmd( + xp, (char_u *)"map", (char_u *)arg, forceit, false, false, + CMD_map); + } + // Find start of last argument. p = arg; while (*p) { if (*p == ' ') @@ -3267,9 +3293,9 @@ set_one_cmd_context ( ++p; /* skip over escaped character */ mb_ptr_adv(p); } - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } - xp->xp_context = compl; + xp->xp_context = context; } break; case CMD_map: case CMD_noremap: @@ -3281,8 +3307,8 @@ set_one_cmd_context ( case CMD_lmap: case CMD_lnoremap: case CMD_smap: case CMD_snoremap: case CMD_xmap: case CMD_xnoremap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, false, ea.cmdidx); case CMD_unmap: case CMD_nunmap: case CMD_vunmap: @@ -3292,18 +3318,18 @@ set_one_cmd_context ( case CMD_lunmap: case CMD_sunmap: case CMD_xunmap: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - FALSE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, false, true, ea.cmdidx); case CMD_abbreviate: case CMD_noreabbrev: case CMD_cabbrev: case CMD_cnoreabbrev: case CMD_iabbrev: case CMD_inoreabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, FALSE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, false, ea.cmdidx); case CMD_unabbreviate: case CMD_cunabbrev: case CMD_iunabbrev: - return set_context_in_map_cmd(xp, cmd, arg, forceit, - TRUE, TRUE, ea.cmdidx); + return (const char *)set_context_in_map_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit, true, true, ea.cmdidx); case CMD_menu: case CMD_noremenu: case CMD_unmenu: case CMD_amenu: case CMD_anoremenu: case CMD_aunmenu: case CMD_nmenu: case CMD_nnoremenu: case CMD_nunmenu: @@ -3313,47 +3339,49 @@ set_one_cmd_context ( case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: - return set_context_in_menu_cmd(xp, cmd, arg, forceit); + return (const char *)set_context_in_menu_cmd( + xp, (char_u *)cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_compiler: xp->xp_context = EXPAND_COMPILER; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_ownsyntax: xp->xp_context = EXPAND_OWNSYNTAX; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_setfiletype: xp->xp_context = EXPAND_FILETYPE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_packadd: xp->xp_context = EXPAND_PACKADD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; #ifdef HAVE_WORKING_LIBINTL case CMD_language: - p = skiptowhite(arg); + p = (const char *)skiptowhite((const char_u *)arg); if (*p == NUL) { xp->xp_context = EXPAND_LANGUAGE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; } else { - if ( STRNCMP(arg, "messages", p - arg) == 0 - || STRNCMP(arg, "ctype", p - arg) == 0 - || STRNCMP(arg, "time", p - arg) == 0) { + if (strncmp(arg, "messages", p - arg) == 0 + || strncmp(arg, "ctype", p - arg) == 0 + || strncmp(arg, "time", p - arg) == 0) { xp->xp_context = EXPAND_LOCALES; - xp->xp_pattern = skipwhite(p); - } else + xp->xp_pattern = skipwhite((const char_u *)p); + } else { xp->xp_context = EXPAND_NOTHING; + } } break; #endif @@ -3362,16 +3390,16 @@ set_one_cmd_context ( break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_history: xp->xp_context = EXPAND_HISTORY; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; case CMD_syntime: xp->xp_context = EXPAND_SYNTIME; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; break; @@ -3390,10 +3418,9 @@ set_one_cmd_context ( * Also skip white space and ":" characters. * Returns the "cmd" pointer advanced to beyond the range. */ -char_u * -skip_range ( - char_u *cmd, - int *ctx /* pointer to xp_context or NULL */ +char_u *skip_range( + const char_u *cmd, + int *ctx // pointer to xp_context or NULL ) { unsigned delim; @@ -3418,7 +3445,7 @@ skip_range ( while (*cmd == ':') cmd = skipwhite(cmd + 1); - return cmd; + return (char_u *)cmd; } /* @@ -4585,14 +4612,15 @@ int ends_excmd(int c) FUNC_ATTR_CONST * Return the next command, after the first '|' or '\n'. * Return NULL if not found. */ -char_u *find_nextcmd(char_u *p) +char_u *find_nextcmd(const char_u *p) { while (*p != '|' && *p != '\n') { - if (*p == NUL) + if (*p == NUL) { return NULL; - ++p; + } + p++; } - return p + 1; + return (char_u *)p + 1; } /* diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e140dfa886..8810204c03 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3636,7 +3636,6 @@ set_cmd_context ( ) { int old_char = NUL; - char_u *nextcomm; /* * Avoid a UMR warning from Purify, only save the character if it has been @@ -3645,7 +3644,7 @@ set_cmd_context ( if (col < len) old_char = str[col]; str[col] = NUL; - nextcomm = str; + const char *nextcomm = (const char *)str; if (use_ccline && ccline.cmdfirstc == '=') { // pass CMD_SIZE because there is no real command @@ -3654,9 +3653,11 @@ set_cmd_context ( xp->xp_context = ccline.xp_context; xp->xp_pattern = ccline.cmdbuff; xp->xp_arg = ccline.xp_arg; - } else - while (nextcomm != NULL) + } else { + while (nextcomm != NULL) { nextcomm = set_one_cmd_context(xp, nextcomm); + } + } /* Store the string here so that call_user_expand_func() can get to them * easily. */ diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 550d256de5..b647b8146a 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -140,31 +140,30 @@ char_u *get_cscope_name(expand_T *xp, int idx) /* * Handle command line completion for :cscope command. */ -void set_context_in_cscope_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx) +void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_CSCOPE; - xp->xp_pattern = arg; - expand_what = (cmdidx == CMD_scscope) - ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD; + xp->xp_pattern = (char_u *)arg; + expand_what = ((cmdidx == CMD_scscope) + ? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD); /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "add", p - arg) == 0) + } else if (STRNICMP(arg, "add", p - arg) == 0) { xp->xp_context = EXPAND_FILES; - else if (STRNICMP(arg, "kill", p - arg) == 0) + } else if (STRNICMP(arg, "kill", p - arg) == 0) { expand_what = EXP_CSCOPE_KILL; - else if (STRNICMP(arg, "find", p - arg) == 0) + } else if (STRNICMP(arg, "find", p - arg) == 0) { expand_what = EXP_CSCOPE_FIND; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a54e36a609..4a7b4a0eac 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5574,43 +5574,42 @@ void reset_expand_highlight(void) * Handle command line completion for :match and :echohl command: Add "None" * as highlight group. */ -void set_context_in_echohl_cmd(expand_T *xp, char_u *arg) +void set_context_in_echohl_cmd(expand_T *xp, const char *arg) { xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_none = 1; } /* * Handle command line completion for :syntax command. */ -void set_context_in_syntax_cmd(expand_T *xp, char_u *arg) +void set_context_in_syntax_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand subcommands */ + // Default: expand subcommands. xp->xp_context = EXPAND_SYNTAX; expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 0; include_default = 0; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past first word */ - xp->xp_pattern = skipwhite(p); - if (*skiptowhite(xp->xp_pattern) != NUL) + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past first word. + xp->xp_pattern = skipwhite((const char_u *)p); + if (*skiptowhite(xp->xp_pattern) != NUL) { xp->xp_context = EXPAND_NOTHING; - else if (STRNICMP(arg, "case", p - arg) == 0) + } else if (STRNICMP(arg, "case", p - arg) == 0) { expand_what = EXP_CASE; - else if ( STRNICMP(arg, "keyword", p - arg) == 0 + } else if (STRNICMP(arg, "keyword", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0 - || STRNICMP(arg, "list", p - arg) == 0) + || STRNICMP(arg, "list", p - arg) == 0) { xp->xp_context = EXPAND_HIGHLIGHT; - else + } else { xp->xp_context = EXPAND_NOTHING; + } } } } @@ -7474,41 +7473,41 @@ int highlight_changed(void) /* * Handle command line completion for :highlight command. */ -void set_context_in_highlight_cmd(expand_T *xp, char_u *arg) +void set_context_in_highlight_cmd(expand_T *xp, const char *arg) { - char_u *p; - - /* Default: expand group names */ + // Default: expand group names. xp->xp_context = EXPAND_HIGHLIGHT; - xp->xp_pattern = arg; + xp->xp_pattern = (char_u *)arg; include_link = 2; include_default = 1; /* (part of) subcommand already typed */ if (*arg != NUL) { - p = skiptowhite(arg); - if (*p != NUL) { /* past "default" or group name */ + const char *p = (const char *)skiptowhite((const char_u *)arg); + if (*p != NUL) { // Past "default" or group name. include_default = 0; - if (STRNCMP("default", arg, p - arg) == 0) { - arg = skipwhite(p); - xp->xp_pattern = arg; - p = skiptowhite(arg); + if (strncmp("default", arg, p - arg) == 0) { + arg = (const char *)skipwhite((const char_u *)p); + xp->xp_pattern = (char_u *)arg; + p = (const char *)skiptowhite((const char_u *)arg); } if (*p != NUL) { /* past group name */ include_link = 0; - if (arg[1] == 'i' && arg[0] == 'N') + if (arg[1] == 'i' && arg[0] == 'N') { highlight_list(); - if (STRNCMP("link", arg, p - arg) == 0 - || STRNCMP("clear", arg, p - arg) == 0) { - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); - if (*p != NUL) { /* past first group name */ - xp->xp_pattern = skipwhite(p); - p = skiptowhite(xp->xp_pattern); + } + if (strncmp("link", arg, p - arg) == 0 + || strncmp("clear", arg, p - arg) == 0) { + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); + if (*p != NUL) { // Past first group name. + xp->xp_pattern = skipwhite((const char_u *)p); + p = (const char *)skiptowhite(xp->xp_pattern); } } - if (*p != NUL) /* past group name(s) */ + if (*p != NUL) { // Past group name(s). xp->xp_context = EXPAND_NOTHING; + } } } } -- cgit From b9603218be3b7bf3fbc30eee6c7c458b01902584 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 08:12:09 +0300 Subject: eval/executor: Fix check-single-includes --- src/nvim/eval/executor.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nvim/eval/executor.h b/src/nvim/eval/executor.h index 19e2a75914..3d789f76a5 100644 --- a/src/nvim/eval/executor.h +++ b/src/nvim/eval/executor.h @@ -1,6 +1,8 @@ #ifndef NVIM_EVAL_EXECUTOR_H #define NVIM_EVAL_EXECUTOR_H +#include "nvim/eval/typval.h" + extern char *e_listidx; #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 46efe14473fa803f84509592cc1e8fca4eb20640 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 28 Mar 2017 08:18:23 +0300 Subject: functests: Try sleeping a bit more --- test/functional/eval/timer_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/eval/timer_spec.lua b/test/functional/eval/timer_spec.lua index 4353619ff0..b3c4cd07eb 100644 --- a/test/functional/eval/timer_spec.lua +++ b/test/functional/eval/timer_spec.lua @@ -49,7 +49,7 @@ describe('timers', function() it('works with zero timeout', function() -- timer_start does still not invoke the callback immediately eq(0,eval("[timer_start(0, 'MyHandler', {'repeat': 1000}), g:val][1]")) - run(nil, nil, nil, 300) + run(nil, nil, nil, 400) eq(1000,eval("g:val")) end) -- cgit