diff options
Diffstat (limited to 'src/nvim/eval/userfunc.c')
| -rw-r--r-- | src/nvim/eval/userfunc.c | 1161 |
1 files changed, 711 insertions, 450 deletions
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 6c6dc3fa43..23b3c4e1b2 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1,20 +1,17 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - // User defined function support #include <assert.h> #include <ctype.h> #include <inttypes.h> +#include <lauxlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "lauxlib.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" -#include "nvim/buffer_defs.h" #include "nvim/charset.h" +#include "nvim/cmdexpand_defs.h" #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" @@ -26,18 +23,20 @@ #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" +#include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" #include "nvim/globals.h" +#include "nvim/hashtab.h" #include "nvim/insexpand.h" #include "nvim/keycodes.h" #include "nvim/lua/executor.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mbyte.h" -#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/option_defs.h" +#include "nvim/option_vars.h" #include "nvim/os/input.h" #include "nvim/path.h" #include "nvim/profile.h" @@ -45,14 +44,21 @@ #include "nvim/runtime.h" #include "nvim/search.h" #include "nvim/strings.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/userfunc.c.generated.h" #endif +/// structure used as item in "fc_defer" +typedef struct { + char *dr_name; ///< function name, allocated + typval_T dr_argvars[MAX_FUNC_ARGS + 1]; + int dr_argcount; +} defer_T; + static hashtab_T func_hashtab; // Used by get_func_tv() @@ -65,12 +71,21 @@ static funccall_T *current_funccal = NULL; // item in it is still being used. static funccall_T *previous_funccal = NULL; -static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it"); -static char *e_funcdict = N_("E717: Dictionary entry already exists"); -static char *e_funcref = N_("E718: Funcref required"); -static char *e_nofunc = N_("E130: Unknown function: %s"); -static char e_no_white_space_allowed_before_str_str[] +static const char *e_unknown_function_str = N_("E117: Unknown function: %s"); +static const char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it"); +static const char *e_funcdict = N_("E717: Dictionary entry already exists"); +static const char *e_funcref = N_("E718: Funcref required"); +static const char *e_nofunc = N_("E130: Unknown function: %s"); +static const char e_function_list_was_modified[] + = N_("E454: Function list was modified"); +static const char e_function_nesting_too_deep[] + = N_("E1058: Function nesting too deep"); +static const char e_no_white_space_allowed_before_str_str[] = N_("E1068: No white space allowed before '%s': %s"); +static const char e_missing_heredoc_end_marker_str[] + = N_("E1145: Missing heredoc end marker: %s"); +static const char e_cannot_use_partial_with_dictionary_for_defer[] + = N_("E1300: Cannot use a partial with dictionary for :defer"); void func_init(void) { @@ -90,7 +105,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * bool mustend = false; char *arg = *argp; char *p = arg; - char_u c; + uint8_t c; int i; if (newargs != NULL) { @@ -128,7 +143,7 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * } if (newargs != NULL) { ga_grow(newargs, 1); - c = (char_u)(*p); + c = (uint8_t)(*p); *p = NUL; arg = xstrdup(arg); @@ -152,14 +167,14 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int * p = skipwhite(p) + 1; p = skipwhite(p); char *expr = p; - if (eval1(&p, &rettv, false) != FAIL) { + if (eval1(&p, &rettv, NULL) != FAIL) { ga_grow(default_args, 1); // trim trailing whitespace while (p > expr && ascii_iswhite(p[-1])) { p--; } - c = (char_u)(*p); + c = (uint8_t)(*p); *p = NUL; expr = xstrdup(expr); ((char **)(default_args->ga_data))[default_args->ga_len] = expr; @@ -223,9 +238,9 @@ static void register_closure(ufunc_T *fp) funccal_unref(fp->uf_scoped, fp, false); fp->uf_scoped = current_funccal; current_funccal->fc_refcount++; - ga_grow(¤t_funccal->fc_funcs, 1); - ((ufunc_T **)current_funccal->fc_funcs.ga_data) - [current_funccal->fc_funcs.ga_len++] = fp; + ga_grow(¤t_funccal->fc_ufuncs, 1); + ((ufunc_T **)current_funccal->fc_ufuncs.ga_data) + [current_funccal->fc_ufuncs.ga_len++] = fp; } /// @return a name for a lambda. Returned in static memory. @@ -252,22 +267,22 @@ static void set_ufunc_name(ufunc_T *fp, char *name) /// Parse a lambda expression and get a Funcref from "*arg". /// /// @return OK or FAIL. Returns NOTDONE for dict or {expr}. -int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) +int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg) { + const bool evaluate = evalarg != NULL && (evalarg->eval_flags & EVAL_EVALUATE); garray_T newargs = GA_EMPTY_INIT_VALUE; garray_T *pnewargs; ufunc_T *fp = NULL; partial_T *pt = NULL; int varargs; - int ret; - char *start = skipwhite(*arg + 1); - char *s, *e; bool *old_eval_lavars = eval_lavars_used; bool eval_lavars = false; + char *tofree = NULL; // First, check if this is a lambda expression. "->" must exists. - ret = get_function_args(&start, '-', NULL, NULL, NULL, true); - if (ret == FAIL || *start != '>') { + char *s = skipwhite(*arg + 1); + int ret = get_function_args(&s, '-', NULL, NULL, NULL, true); + if (ret == FAIL || *s != '>') { return NOTDONE; } @@ -290,12 +305,18 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) // Get the start and the end of the expression. *arg = skipwhite((*arg) + 1); - s = *arg; - ret = skip_expr(arg); + char *start = *arg; + ret = skip_expr(arg, evalarg); + char *end = *arg; if (ret == FAIL) { goto errret; } - e = *arg; + if (evalarg != NULL) { + // avoid that the expression gets freed when another line break follows + tofree = evalarg->eval_tofree; + evalarg->eval_tofree = NULL; + } + *arg = skipwhite(*arg); if (**arg != '}') { semsg(_("E451: Expected }: %s"), *arg); @@ -317,11 +338,11 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) ga_grow(&newlines, 1); // Add "return " before the expression. - size_t len = (size_t)(7 + e - s + 1); + size_t len = (size_t)(7 + end - start + 1); p = xmalloc(len); ((char **)(newlines.ga_data))[newlines.ga_len++] = p; STRCPY(p, "return "); - xstrlcpy(p + 7, s, (size_t)(e - s) + 1); + xstrlcpy(p + 7, start, (size_t)(end - start) + 1); if (strstr(p + 7, "a:") == NULL) { // No a: variables are used for sure. flags |= FC_NOARGS; @@ -359,12 +380,22 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate) } eval_lavars_used = old_eval_lavars; + if (evalarg != NULL && evalarg->eval_tofree == NULL) { + evalarg->eval_tofree = tofree; + } else { + xfree(tofree); + } return OK; errret: ga_clear_strings(&newargs); xfree(fp); xfree(pt); + if (evalarg != NULL && evalarg->eval_tofree == NULL) { + evalarg->eval_tofree = tofree; + } else { + xfree(tofree); + } eval_lavars_used = old_eval_lavars; return FAIL; } @@ -382,9 +413,11 @@ errret: /// is not needed. /// @param[in] no_autoload If true, do not source autoload scripts if function /// was not found. +/// @param[out] found_var If not NULL and a variable was found set it to true. /// /// @return name of the function. -char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload) +char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload, + bool *found_var) FUNC_ATTR_NONNULL_ARG(1, 2) { if (partialp != NULL) { @@ -392,18 +425,25 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b } dictitem_T *const v = find_var(name, (size_t)(*lenp), NULL, no_autoload); - if (v != NULL && v->di_tv.v_type == VAR_FUNC) { - if (v->di_tv.vval.v_string == NULL) { // just in case + if (v == NULL) { + return (char *)name; + } + typval_T *const tv = &v->di_tv; + if (found_var != NULL) { + *found_var = true; + } + + if (tv->v_type == VAR_FUNC) { + if (tv->vval.v_string == NULL) { // just in case *lenp = 0; return ""; } - *lenp = (int)strlen(v->di_tv.vval.v_string); - return v->di_tv.vval.v_string; + *lenp = (int)strlen(tv->vval.v_string); + return tv->vval.v_string; } - if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) { - partial_T *const pt = v->di_tv.vval.v_partial; - + if (tv->v_type == VAR_PARTIAL) { + partial_T *const pt = tv->vval.v_partial; if (pt == NULL) { // just in case *lenp = 0; return ""; @@ -421,63 +461,82 @@ char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, b /// Give an error message with a function name. Handle <SNR> things. /// -/// @param ermsg must be passed without translation (use N_() instead of _()). +/// @param errmsg must be passed without translation (use N_() instead of _()). /// @param name function name -void emsg_funcname(char *ermsg, const char *name) +void emsg_funcname(const char *errmsg, const char *name) { - char *p; + char *p = (char *)name; - if ((uint8_t)(*name) == K_SPECIAL) { + if ((uint8_t)name[0] == K_SPECIAL && name[1] != NUL && name[2] != NUL) { p = concat_str("<SNR>", name + 3); - } else { - p = (char *)name; } - semsg(_(ermsg), p); + semsg(_(errmsg), p); if (p != name) { xfree(p); } } -/// Allocate a variable for the result of a function. -/// -/// @param name name of the function -/// @param len length of "name" or -1 to use strlen() -/// @param arg argument, pointing to the '(' -/// @param funcexe various values -/// -/// @return OK or FAIL. -int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe) +/// Get function arguments at "*arg" and advance it. +/// Return them in "*argvars[MAX_FUNC_ARGS + 1]" and the count in "argcount". +/// On failure FAIL is returned but the "argvars[argcount]" are still set. +static int get_func_arguments(char **arg, evalarg_T *const evalarg, int partial_argc, + typval_T *argvars, int *argcount) { - char *argp; + char *argp = *arg; int ret = OK; - typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments - int argcount = 0; // number of arguments found // Get the arguments. - argp = *arg; - while (argcount < MAX_FUNC_ARGS - - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) { + while (*argcount < MAX_FUNC_ARGS - partial_argc) { argp = skipwhite(argp + 1); // skip the '(' or ',' + if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) { + if (eval1(&argp, &argvars[*argcount], evalarg) == FAIL) { ret = FAIL; break; } - argcount++; + (*argcount)++; if (*argp != ',') { break; } } + + argp = skipwhite(argp); if (*argp == ')') { argp++; } else { ret = FAIL; } + *arg = argp; + return ret; +} +/// Call a function and put the result in "rettv". +/// +/// @param name name of the function +/// @param len length of "name" or -1 to use strlen() +/// @param arg argument, pointing to the '(' +/// @param funcexe various values +/// +/// @return OK or FAIL. +int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, evalarg_T *const evalarg, + funcexe_T *funcexe) +{ + typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments + int argcount = 0; // number of arguments found + const bool evaluate = evalarg == NULL ? false : (evalarg->eval_flags & EVAL_EVALUATE); + + char *argp = *arg; + int ret = get_func_arguments(&argp, evalarg, + (funcexe->fe_partial == NULL + ? 0 + : funcexe->fe_partial->pt_argc), + argvars, &argcount); + + assert(ret == OK || ret == FAIL); // suppress clang false positive if (ret == OK) { int i = 0; @@ -495,7 +554,7 @@ int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_ ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; - } else if (!aborting()) { + } else if (!aborting() && evaluate) { if (argcount == MAX_FUNC_ARGS) { emsg_funcname(N_("E740: Too many arguments for function %s"), name); } else { @@ -594,24 +653,28 @@ ufunc_T *find_func(const char *name) /// Copy the function name of "fp" to buffer "buf". /// "buf" must be able to hold the function name plus three bytes. /// Takes care of script-local function names. -static void cat_func_name(char *buf, ufunc_T *fp) +static void cat_func_name(char *buf, size_t buflen, ufunc_T *fp) { - if ((uint8_t)fp->uf_name[0] == K_SPECIAL) { - STRCPY(buf, "<SNR>"); - STRCAT(buf, fp->uf_name + 3); + int len = -1; + size_t uflen = strlen(fp->uf_name); + assert(uflen > 0); + + if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) { + len = snprintf(buf, buflen, "<SNR>%s", fp->uf_name + 3); } else { - STRCPY(buf, fp->uf_name); + len = snprintf(buf, buflen, "%s", fp->uf_name); } + + (void)len; // Avoid unused warning on release builds + assert(len > 0); } /// Add a number variable "name" to dict "dp" with value "nr". static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) { -#ifndef __clang_analyzer__ STRCPY(v->di_key, name); -#endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&dp->dv_hashtab, (char *)v->di_key); + hash_add(&dp->dv_hashtab, v->di_key); v->di_tv.v_type = VAR_NUMBER; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_number = nr; @@ -620,8 +683,8 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr) /// Free "fc" static void free_funccal(funccall_T *fc) { - for (int i = 0; i < fc->fc_funcs.ga_len; i++) { - ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; + for (int i = 0; i < fc->fc_ufuncs.ga_len; i++) { + ufunc_T *fp = ((ufunc_T **)(fc->fc_ufuncs.ga_data))[i]; // When garbage collecting a funccall_T may be freed before the // function that references it, clear its uf_scoped field. @@ -631,9 +694,9 @@ static void free_funccal(funccall_T *fc) fp->uf_scoped = NULL; } } - ga_clear(&fc->fc_funcs); + ga_clear(&fc->fc_ufuncs); - func_ptr_unref(fc->func); + func_ptr_unref(fc->fc_func); xfree(fc); } @@ -643,13 +706,13 @@ static void free_funccal(funccall_T *fc) static void free_funccal_contents(funccall_T *fc) { // Free all l: variables. - vars_clear(&fc->l_vars.dv_hashtab); + vars_clear(&fc->fc_l_vars.dv_hashtab); // Free all a: variables. - vars_clear(&fc->l_avars.dv_hashtab); + vars_clear(&fc->fc_l_avars.dv_hashtab); // Free the a:000 variables. - TV_LIST_ITER(&fc->l_varlist, li, { + TV_LIST_ITER(&fc->fc_l_varlist, li, { tv_clear(TV_LIST_ITEM_TV(li)); }); @@ -663,11 +726,11 @@ static void cleanup_function_call(funccall_T *fc) bool may_free_fc = fc->fc_refcount <= 0; bool free_fc = true; - current_funccal = fc->caller; + current_funccal = fc->fc_caller; // Free all l: variables if not referred. - if (may_free_fc && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT) { - vars_clear(&fc->l_vars.dv_hashtab); + if (may_free_fc && fc->fc_l_vars.dv_refcount == DO_NOT_FREE_CNT) { + vars_clear(&fc->fc_l_vars.dv_hashtab); } else { free_fc = false; } @@ -675,25 +738,25 @@ static void cleanup_function_call(funccall_T *fc) // If the a:000 list and the l: and a: dicts are not referenced and // there is no closure using it, we can free the funccall_T and what's // in it. - if (may_free_fc && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) { - vars_clear_ext(&fc->l_avars.dv_hashtab, false); + if (may_free_fc && fc->fc_l_avars.dv_refcount == DO_NOT_FREE_CNT) { + vars_clear_ext(&fc->fc_l_avars.dv_hashtab, false); } else { free_fc = false; // Make a copy of the a: variables, since we didn't do that above. - TV_DICT_ITER(&fc->l_avars, di, { + TV_DICT_ITER(&fc->fc_l_avars, di, { tv_copy(&di->di_tv, &di->di_tv); }); } - if (may_free_fc && fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated) + if (may_free_fc && fc->fc_l_varlist.lv_refcount // NOLINT(runtime/deprecated) == DO_NOT_FREE_CNT) { - fc->l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated) + fc->fc_l_varlist.lv_first = NULL; // NOLINT(runtime/deprecated) } else { free_fc = false; // Make a copy of the a:000 items, since we didn't do that above. - TV_LIST_ITER(&fc->l_varlist, li, { + TV_LIST_ITER(&fc->fc_l_varlist, li, { tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); }); } @@ -706,7 +769,7 @@ static void cleanup_function_call(funccall_T *fc) // "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; + fc->fc_caller = previous_funccal; previous_funccal = fc; if (want_garbage_collect) { @@ -729,26 +792,23 @@ static void cleanup_function_call(funccall_T *fc) /// @param[in] force When true, we are exiting. static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) { - funccall_T **pfc; - int i; - if (fc == NULL) { return; } fc->fc_refcount--; if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) { - for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { + for (funccall_T **pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->fc_caller) { if (fc == *pfc) { - *pfc = fc->caller; + *pfc = fc->fc_caller; free_funccal_contents(fc); return; } } } - for (i = 0; i < fc->fc_funcs.ga_len; i++) { - if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) { - ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; + for (int i = 0; i < fc->fc_ufuncs.ga_len; i++) { + if (((ufunc_T **)(fc->fc_ufuncs.ga_data))[i] == fp) { + ((ufunc_T **)(fc->fc_ufuncs.ga_data))[i] = NULL; } } } @@ -759,7 +819,7 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) /// @return true if the entry was deleted, false if it wasn't found. static bool func_remove(ufunc_T *fp) { - hashitem_T *hi = hash_find(&func_hashtab, (char *)UF2HIKEY(fp)); + hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp)); if (HASHITEM_EMPTY(hi)) { return false; } @@ -824,6 +884,27 @@ static void func_clear_free(ufunc_T *fp, bool force) func_free(fp); } +/// Allocate a funccall_T, link it in current_funccal and fill in "fp" and "rettv". +/// Must be followed by one call to remove_funccal() or cleanup_function_call(). +funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv) +{ + funccall_T *fc = xcalloc(1, sizeof(funccall_T)); + fc->fc_caller = current_funccal; + current_funccal = fc; + fc->fc_func = fp; + func_ptr_ref(fp); + fc->fc_rettv = rettv; + return fc; +} + +/// Restore current_funccal. +void remove_funccal(void) +{ + funccall_T *fc = current_funccal; + current_funccal = fc->fc_caller; + free_funccal(fc); +} + /// Call a user function /// /// @param fp Function to call. @@ -838,11 +919,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett FUNC_ATTR_NONNULL_ARG(1, 3, 4) { bool using_sandbox = false; - funccall_T *fc; int save_did_emsg; static int depth = 0; dictitem_T *v; - int fixvar_idx = 0; // index in fixvar[] + int fixvar_idx = 0; // index in fc_fixvar[] int ai; bool islambda = false; char numbuf[NUMBUFLEN]; @@ -873,40 +953,32 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // check for CTRL-C hit line_breakcheck(); // prepare the funccall_T structure - fc = xcalloc(1, sizeof(funccall_T)); - fc->caller = current_funccal; - current_funccal = fc; - fc->func = fp; - fc->rettv = rettv; - fc->level = ex_nesting_level; + funccall_T *fc = create_funccal(fp, rettv); + fc->fc_level = ex_nesting_level; // Check if this function has a breakpoint. - fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0); - fc->dbg_tick = debug_tick; - + fc->fc_breakpoint = dbg_find_breakpoint(false, fp->uf_name, 0); + fc->fc_dbg_tick = debug_tick; // Set up fields for closure. - ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1); - func_ptr_ref(fp); + ga_init(&fc->fc_ufuncs, sizeof(ufunc_T *), 1); if (strncmp(fp->uf_name, "<lambda>", 8) == 0) { islambda = true; } - // Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables + // Note about using fc->fc_fixvar[]: This is an array of FIXVAR_CNT variables // with names up to VAR_SHORT_LEN long. This avoids having to alloc/free // each argument variable and saves a lot of time. // // Init l: variables. - init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); + init_var_dict(&fc->fc_l_vars, &fc->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 = (dictitem_T *)&fc->fixvar[fixvar_idx++]; -#ifndef __clang_analyzer__ + v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++]; name = (char *)v->di_key; STRCPY(name, "self"); -#endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key); + hash_add(&fc->fc_l_vars.dv_hashtab, v->di_key); v->di_tv.v_type = VAR_DICT; v->di_tv.v_lock = VAR_UNLOCKED; v->di_tv.vval.v_dict = selfdict; @@ -916,38 +988,36 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // Init a: variables, unless none found (in lambda). // Set a:0 to "argcount" less number of named arguments, if >= 0. // Set a:000 to a list with room for the "..." arguments. - init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); + init_var_dict(&fc->fc_l_avars, &fc->fc_l_avars_var, VAR_SCOPE); if ((fp->uf_flags & FC_NOARGS) == 0) { - add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], "0", + add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++], "0", (varnumber_T)(argcount >= fp->uf_args.ga_len ? argcount - fp->uf_args.ga_len : 0)); } - fc->l_avars.dv_lock = VAR_FIXED; + fc->fc_l_avars.dv_lock = VAR_FIXED; if ((fp->uf_flags & FC_NOARGS) == 0) { // Use "name" to avoid a warning from some compiler that checks the // destination size. - v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; -#ifndef __clang_analyzer__ + v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++]; name = (char *)v->di_key; STRCPY(name, "000"); -#endif v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; - hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key); + hash_add(&fc->fc_l_avars.dv_hashtab, v->di_key); v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; - v->di_tv.vval.v_list = &fc->l_varlist; + v->di_tv.vval.v_list = &fc->fc_l_varlist; } - tv_list_init_static(&fc->l_varlist); - tv_list_set_lock(&fc->l_varlist, VAR_FIXED); + tv_list_init_static(&fc->fc_l_varlist); + tv_list_set_lock(&fc->fc_l_varlist, VAR_FIXED); // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. // Set a:N to the "..." arguments. // Skipped when no a: variables used (in lambda). if ((fp->uf_flags & FC_NOARGS) == 0) { - add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++], "firstline", (varnumber_T)firstline); - add_nr_var(&fc->l_avars, (dictitem_T *)&fc->fixvar[fixvar_idx++], + add_nr_var(&fc->fc_l_avars, (dictitem_T *)&fc->fc_fixvar[fixvar_idx++], "lastline", (varnumber_T)lastline); } bool default_arg_err = false; @@ -973,7 +1043,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett default_expr = ((char **)(fp->uf_def_args.ga_data)) [ai + fp->uf_def_args.ga_len]; - if (eval1(&default_expr, &def_rettv, true) == FAIL) { + if (eval1(&default_expr, &def_rettv, &EVALARG_EVALUATE) == FAIL) { default_arg_err = true; break; } @@ -988,7 +1058,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett name = numbuf; } if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) { - v = (dictitem_T *)&fc->fixvar[fixvar_idx++]; + v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++]; v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; } else { v = xmalloc(sizeof(dictitem_T) + strlen(name)); @@ -1010,17 +1080,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. tv_copy(&v->di_tv, &v->di_tv); - hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key); + hash_add(&fc->fc_l_vars.dv_hashtab, v->di_key); } else { - hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key); + hash_add(&fc->fc_l_avars.dv_hashtab, v->di_key); } if (ai >= 0 && ai < MAX_FUNC_ARGS) { - listitem_T *li = &fc->l_listitems[ai]; + listitem_T *li = &fc->fc_l_listitems[ai]; *TV_LIST_ITEM_TV(li) = argvars[i]; TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED; - tv_list_append(&fc->l_varlist, li); + tv_list_append(&fc->fc_l_varlist, li); } } @@ -1037,7 +1107,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett no_wait_return++; verbose_enter_scroll(); - smsg(_("calling %s"), SOURCING_NAME); + smsg(0, _("calling %s"), SOURCING_NAME); if (p_verbose >= 14) { msg_puts("("); for (int i = 0; i < argcount; i++) { @@ -1045,7 +1115,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett msg_puts(", "); } if (argvars[i].v_type == VAR_NUMBER) { - msg_outnum((long)argvars[i].vval.v_number); + msg_outnum((int)argvars[i].vval.v_number); } else { // Do not want errors such as E724 here. emsg_off++; @@ -1075,7 +1145,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool func_not_yet_profiling_but_should = do_profiling_yes - && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL); + && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL); if (func_not_yet_profiling_but_should) { started_profiling = true; @@ -1085,7 +1155,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett bool func_or_func_caller_profiling = do_profiling_yes && (fp->uf_profiling - || (fc->caller != NULL && fc->caller->func->uf_profiling)); + || (fc->fc_caller != NULL && fc->fc_caller->fc_func->uf_profiling)); if (func_or_func_caller_profiling) { fp->uf_tm_count++; @@ -1110,7 +1180,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // A Lambda always has the command "return {expr}". It is much faster // to evaluate {expr} directly. ex_nesting_level++; - (void)eval1(&p, rettv, true); + (void)eval1(&p, rettv, &EVALARG_EVALUATE); ex_nesting_level--; } else { // call do_cmdline() to execute the lines @@ -1118,6 +1188,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT); } + // Invoke functions added with ":defer". + handle_defer_one(current_funccal); + RedrawingDisabled--; // when the function was aborted because of an error, return -1 @@ -1130,15 +1203,15 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett if (func_or_func_caller_profiling) { call_start = profile_end(call_start); - call_start = profile_sub_wait(wait_start, call_start); // -V614 + call_start = profile_sub_wait(wait_start, call_start); fp->uf_tm_total = profile_add(fp->uf_tm_total, call_start); fp->uf_tm_self = profile_self(fp->uf_tm_self, call_start, fp->uf_tm_children); - if (fc->caller != NULL && fc->caller->func->uf_profiling) { - fc->caller->func->uf_tm_children = - profile_add(fc->caller->func->uf_tm_children, call_start); - fc->caller->func->uf_tml_children = - profile_add(fc->caller->func->uf_tml_children, call_start); + if (fc->fc_caller != NULL && fc->fc_caller->fc_func->uf_profiling) { + fc->fc_caller->fc_func->uf_tm_children = + profile_add(fc->fc_caller->fc_func->uf_tm_children, call_start); + fc->fc_caller->fc_func->uf_tml_children = + profile_add(fc->fc_caller->fc_func->uf_tml_children, call_start); } if (started_profiling) { // make a ":profdel func" stop profiling the function @@ -1152,10 +1225,10 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett verbose_enter_scroll(); if (aborting()) { - smsg(_("%s aborted"), SOURCING_NAME); - } else if (fc->rettv->v_type == VAR_NUMBER) { - smsg(_("%s returning #%" PRId64 ""), - SOURCING_NAME, (int64_t)fc->rettv->vval.v_number); + smsg(0, _("%s aborted"), SOURCING_NAME); + } else if (fc->fc_rettv->v_type == VAR_NUMBER) { + smsg(0, _("%s returning #%" PRId64 ""), + SOURCING_NAME, (int64_t)fc->fc_rettv->vval.v_number); } else { char buf[MSG_BUF_LEN]; @@ -1163,7 +1236,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett // have some idea how it starts and ends. smsg() would always // truncate it at the end. Don't want errors such as E724 here. emsg_off++; - char *s = encode_tv2string(fc->rettv, NULL); + char *s = encode_tv2string(fc->fc_rettv, NULL); char *tofree = s; emsg_off--; if (s != NULL) { @@ -1171,7 +1244,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN); s = buf; } - smsg(_("%s returning %s"), SOURCING_NAME, s); + smsg(0, _("%s returning %s"), SOURCING_NAME, s); xfree(tofree); } } @@ -1194,7 +1267,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett no_wait_return++; verbose_enter_scroll(); - smsg(_("continuing in %s"), SOURCING_NAME); + smsg(0, _("continuing in %s"), SOURCING_NAME); msg_puts("\n"); // don't overwrite this either verbose_leave_scroll(); @@ -1230,6 +1303,21 @@ static bool func_name_refcount(const char *name) return isdigit((uint8_t)(*name)) || *name == '<'; } +/// Check the argument count for user function "fp". +/// @return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise. +static int check_user_func_argcount(ufunc_T *fp, int argcount) + FUNC_ATTR_NONNULL_ALL +{ + const int regular_args = fp->uf_args.ga_len; + + if (argcount < regular_args - fp->uf_def_args.ga_len) { + return FCERR_TOOFEW; + } else if (!fp->uf_varargs && argcount > regular_args) { + return FCERR_TOOMANY; + } + return FCERR_UNKNOWN; +} + /// Call a user function after checking the arguments. static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict) @@ -1242,12 +1330,11 @@ static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, ty if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) { *funcexe->fe_doesrange = true; } - int error; - if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) { - error = FCERR_TOOFEW; - } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) { - error = FCERR_TOOMANY; - } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) { + int error = check_user_func_argcount(fp, argcount); + if (error != FCERR_UNKNOWN) { + return error; + } + if ((fp->uf_flags & FC_DICT) && selfdict == NULL) { error = FCERR_DICT; } else { // Call the user function. @@ -1301,8 +1388,8 @@ void free_all_functions(void) // Clean up the current_funccal chain and the funccal stack. while (current_funccal != NULL) { - tv_clear(current_funccal->rettv); - cleanup_function_call(current_funccal); // -V595 + tv_clear(current_funccal->fc_rettv); + cleanup_function_call(current_funccal); if (current_funccal == NULL && funccal_stack != NULL) { restore_funccal(); } @@ -1434,12 +1521,16 @@ varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argv /// Give an error message for the result of a function. /// Nothing if "error" is FCERR_NONE. -static void user_func_error(int error, const char *name) - FUNC_ATTR_NONNULL_ALL +static void user_func_error(int error, const char *name, funcexe_T *funcexe) + FUNC_ATTR_NONNULL_ARG(2) { switch (error) { case FCERR_UNKNOWN: - emsg_funcname(N_("E117: Unknown function: %s"), name); + if (funcexe->fe_found_var) { + semsg(_(e_not_callable_type_str), name); + } else { + emsg_funcname(e_unknown_function_str, name); + } break; case FCERR_NOTMETHOD: emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); @@ -1451,7 +1542,7 @@ static void user_func_error(int error, const char *name) emsg_funcname(_(e_toomanyarg), name); break; case FCERR_TOOFEW: - emsg_funcname(N_("E119: Not enough arguments for function: %s"), name); + emsg_funcname(_(e_toofewarg), name); break; case FCERR_SCRIPT: emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name); @@ -1510,7 +1601,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t int argv_base = 0; partial_T *partial = funcexe->fe_partial; - // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) + // Initialize rettv so that it is safe for caller to invoke tv_clear(rettv) // even when call_func() returns FAIL. rettv->v_type = VAR_UNKNOWN; @@ -1523,7 +1614,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t if (fp == NULL) { // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. - name = xstrnsave(funcname, (size_t)len); + name = xmemdupz(funcname, (size_t)len); fname = fname_trans_sid(name, fname_buf, &tofree, &error); } @@ -1576,7 +1667,7 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t XFREE_CLEAR(name); funcname = "v:lua"; } - } else if (fp != NULL || !builtin_fu |