From 83ef740e150c5f29bd8ed92681d892160474fd7f Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Thu, 20 Jun 2019 22:05:29 -0400 Subject: vim-patch:8.0.1239: cannot use a lambda for the skip argument to searchpair() Problem: Cannot use a lambda for the skip argument to searchpair(). Solution: Evaluate a partial, funcref and lambda. (LemonBoy, closes vim/vim#1454, closes vim/vim#2265) https://github.com/vim/vim/commit/48570488f17e397183ea7d5c7ca67d6e4ffb013d --- src/nvim/eval.c | 118 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 40 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 42ff6ceef2..8c27956242 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -955,6 +955,63 @@ eval_to_bool( return retval; } +static int eval_expr_typval(const typval_T *expr, typval_T *argv, + int argc, typval_T *rettv) +{ + int dummy; + + if (expr->v_type == VAR_FUNC) { + const char_u *const s = expr->vval.v_string; + if (s == NULL || *s == NUL) { + return FAIL; + } + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, true, NULL, NULL) == FAIL) { + return FAIL; + } + } else if (expr->v_type == VAR_PARTIAL) { + partial_T *const partial = expr->vval.v_partial; + const char_u *const s = partial_name(partial); + if (s == NULL || *s == NUL) { + return FAIL; + } + if (call_func(s, (int)STRLEN(s), rettv, argc, argv, NULL, + 0L, 0L, &dummy, true, partial, NULL) == FAIL) { + return FAIL; + } + } else { + char buf[NUMBUFLEN]; + char_u *s = (char_u *)tv_get_string_buf_chk(expr, buf); + if (s == NULL) { + return FAIL; + } + s = skipwhite(s); + if (eval1(&s, rettv, true) == FAIL) { + return FAIL; + } + if (*s != NUL) { // check for trailing chars after expr + emsgf(_(e_invexpr2), s); + return FAIL; + } + } + return OK; +} + +/// Like eval_to_bool() but using a typval_T instead of a string. +/// Works for string, funcref and partial. +static bool eval_expr_to_bool(const typval_T *expr, bool *error) +{ + typval_T rettv; + + if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL) { + *error = true; + return false; + } + const bool res = (tv_get_number_chk(&rettv, error) != 0); + tv_clear(&rettv); + return res; +} + /// Top level evaluation function, returning a string /// /// @param[in] arg String to evaluate. @@ -8864,40 +8921,12 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) typval_T rettv; typval_T argv[3]; int retval = FAIL; - int dummy; 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) { - 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; - } - } else if (expr->v_type == VAR_PARTIAL) { - partial_T *partial = expr->vval.v_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 { - char buf[NUMBUFLEN]; - const char *s = tv_get_string_buf_chk(expr, buf); - if (s == NULL) { - goto theend; - } - 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 - emsgf(_(e_invexpr2), s); - goto theend; - } + if (eval_expr_typval(expr, argv, 2, &rettv) == FAIL) { + goto theend; } if (map) { // map(): replace the list item value. @@ -14501,7 +14530,6 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) // 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); @@ -14529,13 +14557,15 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } // Optional fifth argument: skip expression. - const char *skip; + const typval_T *skip; if (argvars[3].v_type == VAR_UNKNOWN || argvars[4].v_type == VAR_UNKNOWN) { - skip = ""; + skip = NULL; } else { - skip = tv_get_string_buf_chk(&argvars[4], nbuf3); - if (skip == NULL) { + skip = &argvars[4]; + if (skip->v_type != VAR_FUNC + && skip->v_type != VAR_PARTIAL + && skip->v_type != VAR_STRING) { goto theend; // Type error. } if (argvars[5].v_type != VAR_UNKNOWN) { @@ -14553,7 +14583,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) } retval = do_searchpair( - (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, (char_u *)skip, + (char_u *)spat, (char_u *)mpat, (char_u *)epat, dir, skip, flags, match_pos, lnum_stop, time_limit); theend: @@ -14601,7 +14631,7 @@ do_searchpair( char_u *mpat, // middle pattern char_u *epat, // end pattern int dir, // BACKWARD or FORWARD - char_u *skip, // skip expression + const typval_T *skip, // skip expression int flags, // SP_SETPCMARK and other SP_ values pos_T *match_pos, linenr_T lnum_stop, // stop at this line if not zero @@ -14619,6 +14649,7 @@ do_searchpair( int n; int r; int nest = 1; + bool use_skip = false; int options = SEARCH_KEEP; proftime_T tm; size_t pat2_len; @@ -14648,6 +14679,13 @@ do_searchpair( options |= SEARCH_START; } + if (skip != NULL) { + // Empty string means to not use the skip expression. + if (skip->v_type == VAR_STRING || skip->v_type == VAR_FUNC) { + use_skip = skip->vval.v_string != NULL && *skip->vval.v_string != NUL; + } + } + save_cursor = curwin->w_cursor; pos = curwin->w_cursor; clearpos(&firstpos); @@ -14678,11 +14716,11 @@ do_searchpair( options &= ~SEARCH_START; /* If the skip pattern matches, ignore this match. */ - if (*skip != NUL) { + if (use_skip) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; - bool err; - r = eval_to_bool(skip, &err, NULL, false); + bool err = false; + r = eval_expr_to_bool(skip, &err); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ -- cgit From 8bc365c886ecd9ba502dc0d4a7b46ec9bc92387d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 21 Jun 2019 07:31:42 -0400 Subject: lint --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8c27956242..05f001496a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14715,7 +14715,7 @@ do_searchpair( /* clear the start flag to avoid getting stuck here */ options &= ~SEARCH_START; - /* If the skip pattern matches, ignore this match. */ + // If the skip pattern matches, ignore this match. if (use_skip) { save_pos = curwin->w_cursor; curwin->w_cursor = pos; -- cgit From efdc0f6a6949ec9bfb0d9ef454871458489773f0 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 21 Jun 2019 07:38:43 -0400 Subject: vim-patch:8.1.0112: no error when using bad arguments with searchpair() Problem: No error when using bad arguments with searchpair(). Solution: Add error messages. https://github.com/vim/vim/commit/3dddb09c98825acefa6f2d94bb369b8e00d7b3e5 --- src/nvim/eval.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 05f001496a..6c138729a2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14527,7 +14527,8 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) long lnum_stop = 0; long time_limit = 0; - // Get the three pattern arguments: start, middle, end. + // Get the three pattern arguments: start, middle, end. Will result in an + // error if not a valid argument. char nbuf1[NUMBUFLEN]; char nbuf2[NUMBUFLEN]; const char *spat = tv_get_string_chk(&argvars[0]); @@ -14566,16 +14567,19 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos) if (skip->v_type != VAR_FUNC && skip->v_type != VAR_PARTIAL && skip->v_type != VAR_STRING) { + emsgf(_(e_invarg2), tv_get_string(&argvars[4])); goto theend; // Type error. } if (argvars[5].v_type != VAR_UNKNOWN) { lnum_stop = tv_get_number_chk(&argvars[5], NULL); if (lnum_stop < 0) { + emsgf(_(e_invarg2), tv_get_string(&argvars[5])); goto theend; } if (argvars[6].v_type != VAR_UNKNOWN) { time_limit = tv_get_number_chk(&argvars[6], NULL); if (time_limit < 0) { + emsgf(_(e_invarg2), tv_get_string(&argvars[6])); goto theend; } } -- cgit From 6eae28f090b694282bed37fb73e547b34dc3261d Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 21 Jun 2019 19:50:05 -0400 Subject: eval: require nonnull func args to pass ASAN build --- src/nvim/eval.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6c138729a2..3c50647848 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -957,6 +957,7 @@ eval_to_bool( static int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) + FUNC_ATTR_NONNULL_ARG(1, 2, 4) { int dummy; @@ -1000,10 +1001,11 @@ static int eval_expr_typval(const typval_T *expr, typval_T *argv, /// Like eval_to_bool() but using a typval_T instead of a string. /// Works for string, funcref and partial. static bool eval_expr_to_bool(const typval_T *expr, bool *error) + FUNC_ATTR_NONNULL_ARG(1, 2) { - typval_T rettv; + typval_T argv, rettv; - if (eval_expr_typval(expr, NULL, 0, &rettv) == FAIL) { + if (eval_expr_typval(expr, &argv, 0, &rettv) == FAIL) { *error = true; return false; } @@ -6365,6 +6367,7 @@ call_func( partial_T *partial, // optional, can be NULL dict_T *selfdict_in // Dictionary for "self" ) + FUNC_ATTR_NONNULL_ARG(5) { int ret = FAIL; int error = ERROR_NONE; -- cgit From f0d6695e7cc13cd5f1ee006575a369faf2546893 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Fri, 21 Jun 2019 20:46:43 -0400 Subject: vim-patch:8.1.0181: memory leak with trailing characters in skip expression Problem: Memory leak with trailing characters in skip expression. Solution: Free the return value. https://github.com/vim/vim/commit/a43ebe9454386427ca38c75810e2d36991f17812 --- src/nvim/eval.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3c50647848..3e6e183847 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -991,6 +991,7 @@ static int eval_expr_typval(const typval_T *expr, typval_T *argv, return FAIL; } if (*s != NUL) { // check for trailing chars after expr + tv_clear(rettv); emsgf(_(e_invexpr2), s); return FAIL; } -- cgit From bb02ca6defb65110a426b579e81cd1a6f9eb886a Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 22 Jun 2019 05:58:58 -0400 Subject: vim-patch:8.1.0167: lock flag in new dictitem is reset in many places Problem: Lock flag in new dictitem is reset in many places. Solution: Always reset the lock flag. https://github.com/vim/vim/commit/c89d4b35300b98cf68b14c89c8e1add51bd857e3 --- src/nvim/eval.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 3e6e183847..ce0b192545 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -17819,7 +17819,6 @@ static void add_timer_info(typval_T *rettv, timer_T *timer) di->di_tv.v_type = VAR_FUNC; di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref); } - di->di_tv.v_lock = 0; } static void add_timer_info_all(typval_T *rettv) @@ -21180,7 +21179,6 @@ void ex_function(exarg_T *eap) 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); /* behave like "dict" was used */ -- cgit From 4653b5943f2ccdb65fa1e099853d4d680abc3f83 Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sat, 22 Jun 2019 19:40:12 -0400 Subject: vim-patch:8.1.0747: map() with a bad expression doesn't give an error Problem: map() with a bad expression doesn't give an error. (Ingo Karkat) Solution: Check for giving an error message. (closes vim/vim#3800) https://github.com/vim/vim/commit/ce9d50df07402cb8e196537a9c4505845adecabc --- src/nvim/eval.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ce0b192545..c5caf80700 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -955,6 +955,28 @@ eval_to_bool( return retval; } +// Call eval1() and give an error message if not done at a lower level. +static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + const int did_emsg_before = did_emsg; + const int called_emsg_before = called_emsg; + + const int ret = eval1(arg, rettv, evaluate); + if (ret == FAIL) { + // Report the invalid expression unless the expression evaluation has + // been cancelled due to an aborting error, an interrupt, or an + // exception, or we already gave a more specific error. + // Also check called_emsg for when using assert_fails(). + if (!aborting() + && did_emsg == did_emsg_before + && called_emsg == called_emsg_before) { + emsgf(_(e_invexpr2), arg); + } + } + return ret; +} + static int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) @@ -987,7 +1009,7 @@ static int eval_expr_typval(const typval_T *expr, typval_T *argv, return FAIL; } s = skipwhite(s); - if (eval1(&s, rettv, true) == FAIL) { + if (eval1_emsg(&s, rettv, true) == FAIL) { return FAIL; } if (*s != NUL) { // check for trailing chars after expr @@ -6368,7 +6390,7 @@ call_func( partial_T *partial, // optional, can be NULL dict_T *selfdict_in // Dictionary for "self" ) - FUNC_ATTR_NONNULL_ARG(5) + FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) { int ret = FAIL; int error = ERROR_NONE; @@ -8891,6 +8913,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } hash_unlock(ht); } else { + // argvars[0].v_type == VAR_LIST vimvars[VV_KEY].vv_type = VAR_NUMBER; for (listitem_T *li = tv_list_first(l); li != NULL;) { @@ -8921,6 +8944,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) + FUNC_ATTR_NONNULL_ARG(1, 2) { typval_T rettv; typval_T argv[3]; @@ -14655,7 +14679,6 @@ do_searchpair( pos_T save_cursor; pos_T save_pos; int n; - int r; int nest = 1; bool use_skip = false; int options = SEARCH_KEEP; @@ -14728,7 +14751,7 @@ do_searchpair( save_pos = curwin->w_cursor; curwin->w_cursor = pos; bool err = false; - r = eval_expr_to_bool(skip, &err); + const bool r = eval_expr_to_bool(skip, &err); curwin->w_cursor = save_pos; if (err) { /* Evaluating {skip} caused an error, break here. */ @@ -20525,7 +20548,6 @@ void ex_execute(exarg_T *eap) char_u *arg = eap->arg; typval_T rettv; int ret = OK; - char_u *p; garray_T ga; int save_did_emsg = did_emsg; @@ -20534,17 +20556,8 @@ void ex_execute(exarg_T *eap) if (eap->skip) ++emsg_skip; while (*arg != NUL && *arg != '|' && *arg != '\n') { - p = arg; - if (eval1(&arg, &rettv, !eap->skip) == FAIL) { - /* - * Report the invalid expression unless the expression evaluation - * has been cancelled due to an aborting error, an interrupt, or an - * exception. - */ - if (!aborting() && did_emsg == save_did_emsg) { - EMSG2(_(e_invexpr2), p); - } - ret = FAIL; + ret = eval1_emsg(&arg, &rettv, !eap->skip); + if (ret == FAIL) { break; } -- cgit From fa3db26f100bcb1c27b62b491bd63aed0800795b Mon Sep 17 00:00:00 2001 From: Jan Edmund Lazo Date: Sun, 23 Jun 2019 19:10:59 -0400 Subject: eval: assert VAR_LIST branch in filter_map() --- src/nvim/eval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/eval.c') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c5caf80700..f19fbe112f 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8913,7 +8913,7 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } hash_unlock(ht); } else { - // argvars[0].v_type == VAR_LIST + assert(argvars[0].v_type == VAR_LIST); vimvars[VV_KEY].vv_type = VAR_NUMBER; for (listitem_T *li = tv_list_first(l); li != NULL;) { -- cgit