diff options
Diffstat (limited to 'src/nvim/ex_eval.c')
-rw-r--r-- | src/nvim/ex_eval.c | 196 |
1 files changed, 105 insertions, 91 deletions
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index f76e60f6c5..d2a1d53b78 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,6 +1,3 @@ -// 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 - /// @file ex_eval.c /// /// Functions for Ex command line for the +eval feature. @@ -11,33 +8,34 @@ #include <stdio.h> #include <string.h> -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/charset.h" #include "nvim/debugger.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_eval_defs.h" +#include "nvim/func_attr.h" #include "nvim/gettext.h" #include "nvim/globals.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/option_defs.h" -#include "nvim/pos.h" +#include "nvim/option_vars.h" #include "nvim/regexp.h" #include "nvim/runtime.h" #include "nvim/strings.h" -#include "nvim/types.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_eval.c.generated.h" #endif +static const char e_multiple_else[] = N_("E583: Multiple :else"); +static const char e_multiple_finally[] = N_("E607: Multiple :finally"); + // Exception handling terms: // // :try ":try" command ─┐ @@ -154,11 +152,10 @@ int aborted_in_try(void) /// When several messages appear in the same command, the first is usually the /// most specific one and used as the exception value. The "severe" flag can be /// set to true, if a later but severer message should be used instead. -bool cause_errthrow(const char *mesg, bool severe, bool *ignore) +bool cause_errthrow(const char *mesg, bool multiline, bool severe, bool *ignore) FUNC_ATTR_NONNULL_ALL { msglist_T *elem; - msglist_T **plist; // Do nothing when displaying the interrupt message or reporting an // uncaught exception (which has already been discarded then) at the top @@ -239,21 +236,20 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore) // returned. - Throw only the first of several errors in a row, except // a severe error is following. if (msg_list != NULL) { - plist = msg_list; + msglist_T **plist = msg_list; while (*plist != NULL) { plist = &(*plist)->next; } elem = xmalloc(sizeof(msglist_T)); elem->msg = xstrdup(mesg); + elem->multiline = multiline; elem->next = NULL; elem->throw_msg = NULL; *plist = elem; if (plist == msg_list || severe) { - char *tmsg; - // Skip the extra "Vim " prefix for message "E458". - tmsg = elem->msg; + char *tmsg = elem->msg; if (strncmp(tmsg, "Vim E", 5) == 0 && ascii_isdigit(tmsg[5]) && ascii_isdigit(tmsg[6]) @@ -278,11 +274,9 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore) /// Free a "msg_list" and the messages it contains. static void free_msglist(msglist_T *l) { - msglist_T *messages, *next; - - messages = l; + msglist_T *messages = l; while (messages != NULL) { - next = messages->next; + msglist_T *next = messages->next; xfree(messages->msg); xfree(messages->sfile); xfree(messages); @@ -378,12 +372,12 @@ int do_intthrow(cstack_T *cstack) /// Get an exception message that is to be stored in current_exception->value. char *get_exception_string(void *value, except_type_T type, char *cmdname, int *should_free) { - char *ret, *mesg; - char *p, *val; + char *ret; if (type == ET_ERROR) { + char *val; *should_free = true; - mesg = ((msglist_T *)value)->throw_msg; + char *mesg = ((msglist_T *)value)->throw_msg; if (cmdname != NULL && *cmdname != NUL) { size_t cmdlen = strlen(cmdname); ret = xstrnsave("Vim(", 4 + cmdlen + 2 + strlen(mesg)); @@ -398,7 +392,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, int * // msg_add_fname may have been used to prefix the message with a file // name in quotes. In the exception value, put the file name in // parentheses and move it to the end. - for (p = mesg;; p++) { + for (char *p = mesg;; p++) { if (*p == NUL || (*p == 'E' && ascii_isdigit(p[1]) @@ -441,9 +435,6 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, int * /// exception. static int throw_exception(void *value, except_type_T type, char *cmdname) { - except_T *excp; - int should_free; - // Disallow faking Interrupt or error exceptions as user exceptions. They // would be treated differently from real interrupt or error exceptions // when no active try block is found, see do_cmdline(). @@ -456,7 +447,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname) } } - excp = xmalloc(sizeof(except_T)); + except_T *excp = xmalloc(sizeof(except_T)); if (type == ET_ERROR) { // Store the original message and prefix the exception value with @@ -464,6 +455,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname) excp->messages = (msglist_T *)value; } + int should_free; excp->value = get_exception_string(value, type, cmdname, &should_free); if (excp->value == NULL && should_free) { goto nomem; @@ -495,7 +487,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname) if (debug_break_level > 0 || *p_vfile == NUL) { msg_scroll = true; // always scroll up, don't overwrite } - smsg(_("Exception thrown: %s"), excp->value); + smsg(0, _("Exception thrown: %s"), excp->value); msg_puts("\n"); // don't overwrite this either if (debug_break_level > 0 || *p_vfile == NUL) { @@ -525,8 +517,6 @@ fail: /// caught and the catch clause has been ended normally. static void discard_exception(except_T *excp, bool was_finished) { - char *saved_IObuff; - if (current_exception == excp) { current_exception = NULL; } @@ -538,7 +528,7 @@ static void discard_exception(except_T *excp, bool was_finished) if (p_verbose >= 13 || debug_break_level > 0) { int save_msg_silent = msg_silent; - saved_IObuff = xstrdup(IObuff); + char *saved_IObuff = xstrdup(IObuff); if (debug_break_level > 0) { msg_silent = false; // display messages } else { @@ -548,7 +538,7 @@ static void discard_exception(except_T *excp, bool was_finished) if (debug_break_level > 0 || *p_vfile == NUL) { msg_scroll = true; // always scroll up, don't overwrite } - smsg(was_finished ? _("Exception finished: %s") : _("Exception discarded: %s"), excp->value); + smsg(0, was_finished ? _("Exception finished: %s") : _("Exception discarded: %s"), excp->value); msg_puts("\n"); // don't overwrite this either if (debug_break_level > 0 || *p_vfile == NUL) { cmdline_row = msg_row; @@ -615,7 +605,7 @@ static void catch_exception(except_T *excp) if (debug_break_level > 0 || *p_vfile == NUL) { msg_scroll = true; // always scroll up, don't overwrite } - smsg(_("Exception caught: %s"), excp->value); + smsg(0, _("Exception caught: %s"), excp->value); msg_puts("\n"); // don't overwrite this either if (debug_break_level > 0 || *p_vfile == NUL) { @@ -663,6 +653,40 @@ static void finish_exception(except_T *excp) discard_exception(excp, true); } +/// Save the current exception state in "estate" +void exception_state_save(exception_state_T *estate) +{ + estate->estate_current_exception = current_exception; + estate->estate_did_throw = did_throw; + estate->estate_need_rethrow = need_rethrow; + estate->estate_trylevel = trylevel; + estate->estate_did_emsg = did_emsg; +} + +/// Restore the current exception state from "estate" +void exception_state_restore(exception_state_T *estate) +{ + // Handle any outstanding exceptions before restoring the state + if (did_throw) { + handle_did_throw(); + } + current_exception = estate->estate_current_exception; + did_throw = estate->estate_did_throw; + need_rethrow = estate->estate_need_rethrow; + trylevel = estate->estate_trylevel; + did_emsg = estate->estate_did_emsg; +} + +/// Clear the current exception state +void exception_state_clear(void) +{ + current_exception = NULL; + did_throw = false; + need_rethrow = false; + trylevel = 0; + did_emsg = 0; +} + // Flags specifying the message displayed by report_pending. #define RP_MAKE 0 #define RP_RESUME 1 @@ -677,7 +701,6 @@ static void report_pending(int action, int pending, void *value) { char *mesg; char *s; - int save_msg_silent; assert(value || !(pending & CSTP_THROW)); @@ -727,13 +750,13 @@ static void report_pending(int action, int pending, void *value) } } - save_msg_silent = msg_silent; + int save_msg_silent = msg_silent; if (debug_break_level > 0) { msg_silent = false; // display messages } no_wait_return++; msg_scroll = true; // always scroll up, don't overwrite - smsg(mesg, s); + smsg(0, mesg, s); msg_puts("\n"); // don't overwrite this either cmdline_row = msg_row; no_wait_return--; @@ -797,17 +820,20 @@ void report_discard_pending(int pending, void *value) void ex_eval(exarg_T *eap) { typval_T tv; + evalarg_T evalarg; + + fill_evalarg_from_eap(&evalarg, eap, eap->skip); - if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK) { + if (eval0(eap->arg, &tv, eap, &evalarg) == OK) { tv_clear(&tv); } + + clear_evalarg(&evalarg, eap); } /// Handle ":if". void ex_if(exarg_T *eap) { - int skip; - int result; cstack_T *const cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) { @@ -816,10 +842,10 @@ void ex_if(exarg_T *eap) cstack->cs_idx++; cstack->cs_flags[cstack->cs_idx] = 0; - skip = CHECK_SKIP; + int skip = CHECK_SKIP; bool error; - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + int result = eval_to_bool(eap->arg, &error, eap, skip); if (!skip && !error) { if (result) { @@ -860,7 +886,6 @@ void ex_endif(exarg_T *eap) /// Handle ":else" and ":elseif". void ex_else(exarg_T *eap) { - bool result = false; cstack_T *const cstack = eap->cstack; bool skip = CHECK_SKIP; @@ -876,7 +901,7 @@ void ex_else(exarg_T *eap) skip = true; } else if (cstack->cs_flags[cstack->cs_idx] & CSF_ELSE) { if (eap->cmdidx == CMD_else) { - eap->errmsg = _("E583: multiple :else"); + eap->errmsg = _(e_multiple_else); return; } eap->errmsg = _("E584: :elseif after :else"); @@ -907,6 +932,7 @@ void ex_else(exarg_T *eap) } if (eap->cmdidx == CMD_elseif) { + bool result = false; bool error; // When skipping we ignore most errors, but a missing expression is // wrong, perhaps it should have been "else". @@ -914,7 +940,7 @@ void ex_else(exarg_T *eap) if (skip && *eap->arg != '"' && ends_excmd(*eap->arg)) { semsg(_(e_invexpr2), eap->arg); } else { - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); + result = eval_to_bool(eap->arg, &error, eap, skip); } // When throwing error exceptions, we want to throw always the first @@ -941,13 +967,12 @@ void ex_else(exarg_T *eap) void ex_while(exarg_T *eap) { bool error; - int skip; - int result; cstack_T *const cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) { eap->errmsg = _("E585: :while/:for nesting too deep"); } else { + int result; // The loop flag is set when we have jumped back from the matching // ":endwhile" or ":endfor". When not set, need to initialise this // cstack entry. @@ -959,14 +984,13 @@ void ex_while(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; - skip = CHECK_SKIP; - if (eap->cmdidx == CMD_while) { - // ":while bool-expr" - result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); - } else { + int skip = CHECK_SKIP; + if (eap->cmdidx == CMD_while) { // ":while bool-expr" + result = eval_to_bool(eap->arg, &error, eap, skip); + } else { // ":for var in list-expr" + evalarg_T evalarg; + fill_evalarg_from_eap(&evalarg, eap, skip); void *fi; - - // ":for var in list-expr" if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0) { // Jumping here from a ":continue" or ":endfor": use the // previously evaluated list. @@ -974,7 +998,7 @@ void ex_while(exarg_T *eap) error = false; } else { // Evaluate the argument and get the info in a structure. - fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip); + fi = eval_for_line(eap->arg, &error, eap, &evalarg); cstack->cs_forinfo[cstack->cs_idx] = fi; } @@ -989,6 +1013,7 @@ void ex_while(exarg_T *eap) free_for_info(fi); cstack->cs_forinfo[cstack->cs_idx] = NULL; } + clear_evalarg(&evalarg, eap); } // If this cstack entry was just initialised and is active, set the @@ -1013,7 +1038,6 @@ void ex_while(exarg_T *eap) /// Handle ":continue" void ex_continue(exarg_T *eap) { - int idx; cstack_T *const cstack = eap->cstack; if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { @@ -1023,7 +1047,7 @@ void ex_continue(exarg_T *eap) // conditional not in its finally clause (which is then to be executed // next). Therefore, deactivate all conditionals except the ":while" // itself (if reached). - idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false); + int idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false); assert(idx >= 0); if (cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR)) { rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel); @@ -1043,7 +1067,6 @@ void ex_continue(exarg_T *eap) /// Handle ":break" void ex_break(exarg_T *eap) { - int idx; cstack_T *const cstack = eap->cstack; if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { @@ -1053,7 +1076,7 @@ void ex_break(exarg_T *eap) // conditional not in its finally clause (which is then to be // executed next) is found. In the latter case, make the ":break" // pending for execution at the ":endtry". - idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, true); + int idx = cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, true); if (idx >= 0 && !(cstack->cs_flags[idx] & (CSF_WHILE | CSF_FOR))) { cstack->cs_pending[idx] = CSTP_BREAK; report_make_pending(CSTP_BREAK, NULL); @@ -1065,10 +1088,8 @@ void ex_break(exarg_T *eap) void ex_endwhile(exarg_T *eap) { cstack_T *const cstack = eap->cstack; - int idx; - char *err; + const char *err; int csf; - int fl; if (eap->cmdidx == CMD_endwhile) { err = e_while; @@ -1081,7 +1102,7 @@ void ex_endwhile(exarg_T *eap) if (cstack->cs_looplevel <= 0 || cstack->cs_idx < 0) { eap->errmsg = _(err); } else { - fl = cstack->cs_flags[cstack->cs_idx]; + int fl = cstack->cs_flags[cstack->cs_idx]; if (!(fl & csf)) { // If we are in a ":while" or ":for" but used the wrong endloop // command, do not rewind to the next enclosing ":for"/":while". @@ -1098,6 +1119,7 @@ void ex_endwhile(exarg_T *eap) eap->errmsg = _(e_endtry); } // Try to find the matching ":while" and report what's missing. + int idx; for (idx = cstack->cs_idx; idx > 0; idx--) { fl = cstack->cs_flags[idx]; if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) { @@ -1136,12 +1158,11 @@ void ex_endwhile(exarg_T *eap) /// Handle ":throw expr" void ex_throw(exarg_T *eap) { - const char *arg = (const char *)eap->arg; + char *arg = eap->arg; char *value; if (*arg != NUL && *arg != '|' && *arg != '\n') { - value = eval_to_string_skip(arg, (const char **)&eap->nextcmd, - (bool)eap->skip); + value = eval_to_string_skip(arg, eap, eap->skip); } else { emsg(_(e_argreq)); value = NULL; @@ -1163,10 +1184,8 @@ void ex_throw(exarg_T *eap) /// used for rethrowing an uncaught exception. void do_throw(cstack_T *cstack) { - int idx; int inactivate_try = false; - // // Cleanup and deactivate up to the next surrounding try conditional that // is not in its finally clause. Normally, do not deactivate the try // conditional itself, so that its ACTIVE flag can be tested below. But @@ -1174,7 +1193,7 @@ void do_throw(cstack_T *cstack) // deactivate the try conditional, too, as if the conversion had been done, // and reset the did_emsg or got_int flag, so this won't happen again at // the next surrounding try conditional. - // + #ifndef THROW_ON_ERROR_TRUE if (did_emsg && !THROW_ON_ERROR) { inactivate_try = true; @@ -1187,7 +1206,7 @@ void do_throw(cstack_T *cstack) got_int = false; } #endif - idx = cleanup_conditionals(cstack, 0, inactivate_try); + int idx = cleanup_conditionals(cstack, 0, inactivate_try); if (idx >= 0) { // If this try conditional is active and we are before its first // ":catch", set THROWN so that the ":catch" commands will check @@ -1220,7 +1239,6 @@ void do_throw(cstack_T *cstack) /// Handle ":try" void ex_try(exarg_T *eap) { - int skip; cstack_T *const cstack = eap->cstack; if (cstack->cs_idx == CSTACK_LEN - 1) { @@ -1231,7 +1249,7 @@ void ex_try(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; - skip = CHECK_SKIP; + int skip = CHECK_SKIP; if (!skip) { // Set ACTIVE and TRUE. TRUE means that the corresponding ":catch" @@ -1271,12 +1289,9 @@ void ex_catch(exarg_T *eap) int idx = 0; bool give_up = false; bool skip = false; - bool caught = false; char *end; - char save_char = 0; char *save_cpo; regmatch_T regmatch; - int prev_got_int; cstack_T *const cstack = eap->cstack; char *pat; @@ -1319,6 +1334,7 @@ void ex_catch(exarg_T *eap) } if (!give_up) { + bool caught = false; // Don't do something when no exception has been thrown or when the // corresponding try block never got active (because of an inactive // surrounding conditional or after an error or interrupt or throw). @@ -1344,6 +1360,7 @@ void ex_catch(exarg_T *eap) // the original exception, replace it by an interrupt exception, // and don't catch it in this try block. if (!dbg_check_skipped(eap) || !do_intthrow(cstack)) { + char save_char = 0; // Terminate the pattern and avoid the 'l' flag in 'cpoptions' // while compiling it. if (end != NULL) { @@ -1351,7 +1368,7 @@ void ex_catch(exarg_T *eap) *end = NUL; } save_cpo = p_cpo; - p_cpo = empty_option; + p_cpo = empty_string_option; // Disable error messages, it will make current exception // invalid emsg_off++; @@ -1365,14 +1382,13 @@ void ex_catch(exarg_T *eap) if (regmatch.regprog == NULL) { semsg(_(e_invarg2), pat); } else { - // // Save the value of got_int and reset it. We don't want // a previous interruption cancel matching, only hitting // CTRL-C while matching should abort it. - // - prev_got_int = got_int; + + int prev_got_int = got_int; got_int = false; - caught = vim_regexec_nl(®match, current_exception->value, (colnr_T)0); + caught = vim_regexec_nl(®match, current_exception->value, 0); got_int |= prev_got_int; vim_regfree(regmatch.regprog); } @@ -1415,7 +1431,6 @@ void ex_catch(exarg_T *eap) void ex_finally(exarg_T *eap) { int idx; - int skip = false; int pending = CSTP_NONE; cstack_T *const cstack = eap->cstack; @@ -1439,7 +1454,7 @@ void ex_finally(exarg_T *eap) if (cstack->cs_flags[idx] & CSF_FINALLY) { // Give up for a multiple ":finally" and ignore it. - eap->errmsg = _("E607: multiple :finally"); + eap->errmsg = _(e_multiple_finally); return; } rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR, @@ -1451,7 +1466,7 @@ void ex_finally(exarg_T *eap) // ":finally". After every other error (did_emsg or the conditional // errors detected above) or after an interrupt (got_int) or an // exception (did_throw), the finally clause must be executed. - skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + int skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!skip) { // When debugging or a breakpoint was encountered, display the @@ -1497,8 +1512,8 @@ void ex_finally(exarg_T *eap) } else { pending |= (did_throw ? CSTP_THROW : 0); } - pending |= did_emsg ? CSTP_ERROR : 0; - pending |= got_int ? CSTP_INTERRUPT : 0; + pending |= did_emsg ? CSTP_ERROR : 0; + pending |= got_int ? CSTP_INTERRUPT : 0; assert(pending >= CHAR_MIN && pending <= CHAR_MAX); cstack->cs_pending[cstack->cs_idx] = (char)pending; @@ -1644,8 +1659,9 @@ void ex_endtry(exarg_T *eap) if (!skip) { report_resume_pending(pending, - (pending == CSTP_RETURN) ? rettv : - (pending & CSTP_THROW) ? (void *)current_exception : NULL); + (pending == CSTP_RETURN) + ? rettv + : (pending & CSTP_THROW) ? (void *)current_exception : NULL); switch (pending) { case CSTP_NONE: break; @@ -1979,20 +1995,18 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev /// Handle ":endfunction" when not after a ":function" void ex_endfunction(exarg_T *eap) { - emsg(_("E193: :endfunction not inside a function")); + semsg(_(e_str_not_inside_function), ":endfunction"); } /// @return true if the string "p" looks like a ":while" or ":for" command. int has_loop_cmd(char *p) { - int len; - // skip modifiers, white space and ':' - for (;;) { + while (true) { while (*p == ' ' || *p == '\t' || *p == ':') { p++; } - len = modifier_len(p); + int len = modifier_len(p); if (len == 0) { break; } |