aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2018-03-25 19:13:45 +0200
committerGitHub <noreply@github.com>2018-03-25 19:13:45 +0200
commita9c94f7bb05c53b1c81ec7e65f5df86947c12c58 (patch)
tree13cedb1f5c7500d889dbf30d8f9d1ce3057631b0
parentce5c2030bf69801c2585b60e69ba1a60f139ce86 (diff)
parent201a4ef11cc69d7d7c384db4d937d0d413811577 (diff)
downloadrneovim-a9c94f7bb05c53b1c81ec7e65f5df86947c12c58.tar.gz
rneovim-a9c94f7bb05c53b1c81ec7e65f5df86947c12c58.tar.bz2
rneovim-a9c94f7bb05c53b1c81ec7e65f5df86947c12c58.zip
Merge #7881 from ZyX-I/fix-7876
-rw-r--r--src/nvim/api/private/helpers.c11
-rw-r--r--src/nvim/api/private/helpers.h1
-rw-r--r--src/nvim/ex_docmd.c130
-rw-r--r--src/nvim/ex_eval.c323
-rw-r--r--src/nvim/globals.h8
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua16
6 files changed, 239 insertions, 250 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 1115faaeaf..12a4279dd7 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -47,13 +47,15 @@ typedef struct {
/// @param[out] tstate Location where try state should be saved.
void try_enter(TryState *const tstate)
{
+ // TODO(ZyX-I): Check whether try_enter()/try_leave() may use
+ // enter_cleanup()/leave_cleanup(). Or
+ // save_dbg_stuff()/restore_dbg_stuff().
*tstate = (TryState) {
.current_exception = current_exception,
.msg_list = (const struct msglist *const *)msg_list,
.private_msg_list = NULL,
.trylevel = trylevel,
.got_int = got_int,
- .did_throw = did_throw,
.need_rethrow = need_rethrow,
.did_emsg = did_emsg,
};
@@ -61,7 +63,6 @@ void try_enter(TryState *const tstate)
current_exception = NULL;
trylevel = 1;
got_int = false;
- did_throw = false;
need_rethrow = false;
did_emsg = false;
}
@@ -82,7 +83,6 @@ bool try_leave(const TryState *const tstate, Error *const err)
assert(trylevel == 0);
assert(!need_rethrow);
assert(!got_int);
- assert(!did_throw);
assert(!did_emsg);
assert(msg_list == &tstate->private_msg_list);
assert(*msg_list == NULL);
@@ -91,7 +91,6 @@ bool try_leave(const TryState *const tstate, Error *const err)
current_exception = tstate->current_exception;
trylevel = tstate->trylevel;
got_int = tstate->got_int;
- did_throw = tstate->did_throw;
need_rethrow = tstate->need_rethrow;
did_emsg = tstate->did_emsg;
return ret;
@@ -127,7 +126,7 @@ bool try_end(Error *err)
did_emsg = false;
if (got_int) {
- if (did_throw) {
+ if (current_exception) {
// If we got an interrupt, discard the current exception
discard_current_exception();
}
@@ -146,7 +145,7 @@ bool try_end(Error *err)
if (should_free) {
xfree(msg);
}
- } else if (did_throw) {
+ } else if (current_exception) {
api_set_error(err, kErrorTypeException, "%s", current_exception->value);
discard_current_exception();
}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 87f334ac30..0634764f13 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -93,7 +93,6 @@ typedef struct {
const struct msglist *const *msg_list;
int trylevel;
int got_int;
- int did_throw;
int need_rethrow;
int did_emsg;
} TryState;
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 99495aaa61..4b37abab9e 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -133,7 +133,6 @@ struct dbg_stuff {
char_u *vv_throwpoint;
int did_emsg;
int got_int;
- int did_throw;
int need_rethrow;
int check_cstack;
except_T *current_exception;
@@ -165,12 +164,11 @@ static void save_dbg_stuff(struct dbg_stuff *dsp)
dsp->vv_exception = v_exception(NULL);
dsp->vv_throwpoint = v_throwpoint(NULL);
- /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */
- dsp->did_emsg = did_emsg; did_emsg = FALSE;
- dsp->got_int = got_int; got_int = FALSE;
- dsp->did_throw = did_throw; did_throw = FALSE;
- dsp->need_rethrow = need_rethrow; need_rethrow = FALSE;
- dsp->check_cstack = check_cstack; check_cstack = FALSE;
+ // Necessary for debugging an inactive ":catch", ":finally", ":endtry".
+ dsp->did_emsg = did_emsg; did_emsg = false;
+ dsp->got_int = got_int; got_int = false;
+ dsp->need_rethrow = need_rethrow; need_rethrow = false;
+ dsp->check_cstack = check_cstack; check_cstack = false;
dsp->current_exception = current_exception; current_exception = NULL;
}
@@ -184,7 +182,6 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp)
(void)v_throwpoint(dsp->vv_throwpoint);
did_emsg = dsp->did_emsg;
got_int = dsp->got_int;
- did_throw = dsp->did_throw;
need_rethrow = dsp->need_rethrow;
check_cstack = dsp->check_cstack;
current_exception = dsp->current_exception;
@@ -400,16 +397,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
initial_trylevel = trylevel;
- /*
- * "did_throw" will be set to TRUE when an exception is being thrown.
- */
- did_throw = FALSE;
- /*
- * "did_emsg" will be set to TRUE when emsg() is used, in which case we
- * cancel the whole command line, and any if/endif or loop.
- * If force_abort is set, we cancel everything.
- */
- did_emsg = FALSE;
+ current_exception = NULL;
+ // "did_emsg" will be set to TRUE when emsg() is used, in which case we
+ // cancel the whole command line, and any if/endif or loop.
+ // If force_abort is set, we cancel everything.
+ did_emsg = false;
/*
* KeyTyped is only set when calling vgetc(). Reset it here when not
@@ -659,7 +651,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* not to use a cs_line[] from an entry that isn't a ":while"
* or ":for": It would make "current_line" invalid and can
* cause a crash. */
- if (!did_emsg && !got_int && !did_throw
+ if (!did_emsg && !got_int && !current_exception
&& cstack.cs_idx >= 0
&& (cstack.cs_flags[cstack.cs_idx]
& (CSF_WHILE | CSF_FOR))
@@ -707,7 +699,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
}
/*
- * A ":finally" makes did_emsg, got_int, and did_throw pending for
+ * A ":finally" makes did_emsg, got_int and current_exception pending for
* being restored at the ":endtry". Reset them here and set the
* ACTIVE and FINALLY flags, so that the finally clause gets executed.
* This includes the case where a missing ":endif", ":endwhile" or
@@ -715,10 +707,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
*/
if (cstack.cs_lflags & CSL_HAD_FINA) {
cstack.cs_lflags &= ~CSL_HAD_FINA;
- report_make_pending(cstack.cs_pending[cstack.cs_idx]
- & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW),
- did_throw ? (void *)current_exception : NULL);
- did_emsg = got_int = did_throw = FALSE;
+ report_make_pending((cstack.cs_pending[cstack.cs_idx]
+ & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)),
+ current_exception);
+ did_emsg = got_int = false;
+ current_exception = NULL;
cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
}
@@ -726,15 +719,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* within this loop. */
trylevel = initial_trylevel + cstack.cs_trylevel;
- /*
- * If the outermost try conditional (across function calls and sourced
- * files) is aborted because of an error, an interrupt, or an uncaught
- * exception, cancel everything. If it is left normally, reset
- * force_abort to get the non-EH compatible abortion behavior for
- * the rest of the script.
- */
- if (trylevel == 0 && !did_emsg && !got_int && !did_throw)
- force_abort = FALSE;
+ // If the outermost try conditional (across function calls and sourced
+ // files) is aborted because of an error, an interrupt, or an uncaught
+ // exception, cancel everything. If it is left normally, reset
+ // force_abort to get the non-EH compatible abortion behavior for
+ // the rest of the script.
+ if (trylevel == 0 && !did_emsg && !got_int && !current_exception) {
+ force_abort = false;
+ }
/* Convert an interrupt to an exception if appropriate. */
(void)do_intthrow(&cstack);
@@ -749,11 +741,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* - there is a command after '|', inside a :if, :while, :for or :try, or
* looping for ":source" command or function call.
*/
- while (!((got_int
- || (did_emsg && force_abort) || did_throw
- )
- && cstack.cs_trylevel == 0
- )
+ while (!((got_int || (did_emsg && force_abort) || current_exception)
+ && cstack.cs_trylevel == 0)
&& !(did_emsg
/* Keep going when inside try/catch, so that the error can be
* deal with, except when it is a syntax error, it may cause
@@ -775,7 +764,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
* If a sourced file or executed function ran to its end, report the
* unclosed conditional.
*/
- if (!got_int && !did_throw
+ if (!got_int && !current_exception
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -815,17 +804,16 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
? (char_u *)"endfunction" : (char_u *)NULL);
if (trylevel == 0) {
- /*
- * When an exception is being thrown out of the outermost try
- * conditional, discard the uncaught exception, disable the conversion
- * of interrupts or errors to exceptions, and ensure that no more
- * commands are executed.
- */
- if (did_throw) {
- void *p = NULL;
- char_u *saved_sourcing_name;
+ // When an exception is being thrown out of the outermost try
+ // conditional, discard the uncaught exception, disable the conversion
+ // of interrupts or errors to exceptions, and ensure that no more
+ // commands are executed.
+ if (current_exception) {
+ void *p = NULL;
+ char_u *saved_sourcing_name;
int saved_sourcing_lnum;
- struct msglist *messages = NULL, *next;
+ struct msglist *messages = NULL;
+ struct msglist *next;
/*
* If the uncaught exception is a user exception, report it as an
@@ -885,22 +873,22 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline,
suppress_errthrow = TRUE;
}
- /*
- * The current cstack will be freed when do_cmdline() returns. An uncaught
- * exception will have to be rethrown in the previous cstack. If a function
- * has just returned or a script file was just finished and the previous
- * cstack belongs to the same function or, respectively, script file, it
- * will have to be checked for finally clauses to be executed due to the
- * ":return" or ":finish". This is done in do_one_cmd().
- */
- if (did_throw)
- need_rethrow = TRUE;
+ // The current cstack will be freed when do_cmdline() returns. An uncaught
+ // exception will have to be rethrown in the previous cstack. If a function
+ // has just returned or a script file was just finished and the previous
+ // cstack belongs to the same function or, respectively, script file, it
+ // will have to be checked for finally clauses to be executed due to the
+ // ":return" or ":finish". This is done in do_one_cmd().
+ if (current_exception) {
+ need_rethrow = true;
+ }
if ((getline_equal(fgetline, cookie, getsourceline)
&& ex_nesting_level > source_level(real_cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
&& ex_nesting_level > func_level(real_cookie) + 1)) {
- if (!did_throw)
- check_cstack = TRUE;
+ if (!current_exception) {
+ check_cstack = true;
+ }
} else {
/* When leaving a function, reduce nesting level. */
if (getline_equal(fgetline, cookie, get_func_line))
@@ -1480,10 +1468,11 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
char_u *after_modifier = ea.cmd;
- ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0
- && !(cstack->cs_flags[cstack->
- cs_idx]
- & CSF_ACTIVE));
+ ea.skip = (did_emsg
+ || got_int
+ || current_exception
+ || (cstack->cs_idx >= 0
+ && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
/* Count this line for profiling if ea.skip is FALSE. */
if (do_profiling == PROF_YES && !ea.skip) {
@@ -1782,13 +1771,14 @@ static char_u * do_one_cmd(char_u **cmdlinep,
));
- /* forced commands */
+ // Forced commands.
if (*p == '!' && ea.cmdidx != CMD_substitute
&& ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) {
- ++p;
- ea.forceit = TRUE;
- } else
- ea.forceit = FALSE;
+ p++;
+ ea.forceit = true;
+ } else {
+ ea.forceit = false;
+ }
/*
* 6. Parse arguments.
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index 4010a088c8..e23945c842 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -45,22 +45,21 @@
* there or even outside the try conditional. Try conditionals may be nested.
*/
-/*
- * Configuration whether an exception is thrown on error or interrupt. When
- * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
- * interrupt (got_int) under an active try conditional terminates the script
- * after the non-active finally clauses of all active try conditionals have been
- * executed. Otherwise, errors and/or interrupts are converted into catchable
- * exceptions (did_throw additionally set), which terminate the script only if
- * not caught. For user exceptions, only did_throw is set. (Note: got_int can
- * be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not
- * a reliant test that the exception currently being thrown is an interrupt
- * exception. Similarly, did_emsg can be set afterwards on an error in an
- * (unskipped) conditional command inside an inactive conditional, so did_throw
- * && did_emsg is not a reliant test that the exception currently being thrown
- * 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.
- */
+// Configuration whether an exception is thrown on error or interrupt. When
+// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
+// interrupt (got_int) under an active try conditional terminates the script
+// after the non-active finally clauses of all active try conditionals have been
+// executed. Otherwise, errors and/or interrupts are converted into catchable
+// exceptions, which terminate the script only if not caught. For user
+// exceptions, only current_exception is set. (Note: got_int can be set
+// asynchronously afterwards by a SIGINT, so current_exception && got_int is not
+// a reliant test that the exception currently being thrown is an interrupt
+// exception. Similarly, did_emsg can be set afterwards on an error in an
+// (unskipped) conditional command inside an inactive conditional, so
+// current_exception && did_emsg is not a reliant test that the exception
+// currently being thrown 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
@@ -68,6 +67,15 @@
#define THROW_ON_INTERRUPT true
#define THROW_ON_INTERRUPT_TRUE
+// Don't do something after an error, interrupt, or throw, or when
+// there is a surrounding conditional and it was not active.
+#define CHECK_SKIP \
+ (did_emsg \
+ || got_int \
+ || current_exception \
+ || (cstack->cs_idx > 0 \
+ && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))
+
#define discard_pending_return(p) tv_free((typval_T *)(p))
/*
@@ -94,7 +102,7 @@ static int cause_abort = FALSE;
*/
int aborting(void)
{
- return (did_emsg && force_abort) || got_int || did_throw;
+ return (did_emsg && force_abort) || got_int || current_exception;
}
/*
@@ -178,8 +186,9 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore)
* currently throwing an exception, do nothing. The message text will
* then be stored to v:errmsg by emsg() without displaying it.
*/
- if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw)
- return FALSE;
+ if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) {
+ return false;
+ }
/*
* Ignore an interrupt message when inside a try conditional or when an
@@ -208,12 +217,13 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore)
* exception currently being thrown to prevent it from being caught. Just
* execute finally clauses and terminate.
*/
- if (did_throw) {
- /* When discarding an interrupt exception, reset got_int to prevent the
- * same interrupt being converted to an exception again and discarding
- * the error exception we are about to throw here. */
- if (current_exception->type == ET_INTERRUPT)
- got_int = FALSE;
+ if (current_exception) {
+ // When discarding an interrupt exception, reset got_int to prevent the
+ // same interrupt being converted to an exception again and discarding
+ // the error exception we are about to throw here.
+ if (current_exception->type == ET_INTERRUPT) {
+ got_int = false;
+ }
discard_current_exception();
}
@@ -333,45 +343,44 @@ void do_errthrow(struct condstack *cstack, char_u *cmdname)
*/
int do_intthrow(struct condstack *cstack)
{
- /*
- * If no interrupt occurred or no try conditional is active and no exception
- * is being thrown, do nothing (for compatibility of non-EH scripts).
- */
- if (!got_int || (trylevel == 0 && !did_throw))
- return FALSE;
+ // If no interrupt occurred or no try conditional is active and no exception
+ // is being thrown, do nothing (for compatibility of non-EH scripts).
+ if (!got_int || (trylevel == 0 && !current_exception)) {
+ return false;
+ }
-#ifdef THROW_TEST /* avoid warning for condition always true */
+#ifdef THROW_TEST // avoid warning for condition always true
if (!THROW_ON_INTERRUPT) {
- /*
- * The interrupt aborts everything except for executing finally clauses.
- * Discard any user or error or interrupt exception currently being
- * thrown.
- */
- if (did_throw)
+ // The interrupt aborts everything except for executing finally clauses.
+ // Discard any user or error or interrupt exception currently being
+ // thrown.
+ if (current_exception) {
discard_current_exception();
- } else
+ }
+ } else {
#endif
- {
- /*
- * Throw an interrupt exception, so that everything will be aborted
- * (except for executing finally clauses), until the interrupt exception
- * is caught; if still uncaught at the top level, the script processing
- * will be terminated then. - If an interrupt exception is already
- * being thrown, do nothing.
- *
- */
- if (did_throw) {
- if (current_exception->type == ET_INTERRUPT)
- return FALSE;
+ // Throw an interrupt exception, so that everything will be aborted
+ // (except for executing finally clauses), until the interrupt exception
+ // is caught; if still uncaught at the top level, the script processing
+ // will be terminated then. - If an interrupt exception is already
+ // being thrown, do nothing.
+
+ if (current_exception) {
+ if (current_exception->type == ET_INTERRUPT) {
+ return false;
+ }
- /* An interrupt exception replaces any user or error exception. */
+ // An interrupt exception replaces any user or error exception.
discard_current_exception();
}
- if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL)
+ if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) {
do_throw(cstack);
+ }
+#ifdef THROW_TEST
}
+#endif
- return TRUE;
+ return true;
}
// Get an exception message that is to be stored in current_exception->value.
@@ -565,8 +574,7 @@ void discard_current_exception(void)
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
current_exception = NULL;
- did_throw = FALSE;
- need_rethrow = FALSE;
+ need_rethrow = false;
}
/*
@@ -795,15 +803,7 @@ void ex_if(exarg_T *eap)
++cstack->cs_idx;
cstack->cs_flags[cstack->cs_idx] = 0;
- /*
- * Don't do something after an error, interrupt, or throw, or when there
- * is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
bool error;
result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip);
@@ -854,15 +854,7 @@ void ex_else(exarg_T *eap)
int result;
struct condstack *cstack = eap->cstack;
- /*
- * Don't do something after an error, interrupt, or throw, or when there is
- * a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (cstack->cs_idx < 0
|| (cstack->cs_flags[cstack->cs_idx]
@@ -952,15 +944,7 @@ void ex_while(exarg_T *eap)
cstack->cs_flags[cstack->cs_idx] =
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
- /*
- * Don't do something after an error, interrupt, or throw, or when
- * there is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (eap->cmdidx == CMD_while) {
/*
* ":while bool-expr"
@@ -1233,8 +1217,6 @@ void do_throw(struct condstack *cstack)
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
cstack->cs_exception[idx] = current_exception;
}
-
- did_throw = TRUE;
}
/*
@@ -1253,15 +1235,7 @@ void ex_try(exarg_T *eap)
cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
- /*
- * Don't do something after an error, interrupt, or throw, or when there
- * is a surrounding conditional and it was not active.
- */
- skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0
- && !(cstack->cs_flags[cstack->
- cs_idx -
- 1] &
- CSF_ACTIVE));
+ skip = CHECK_SKIP;
if (!skip) {
/* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch"
@@ -1353,8 +1327,9 @@ void ex_catch(exarg_T *eap)
* corresponding try block never got active (because of an inactive
* surrounding conditional or after an error or interrupt or throw).
*/
- if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE))
- skip = TRUE;
+ if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) {
+ skip = true;
+ }
/*
* Check for a match only if an exception is thrown but not caught by
@@ -1413,10 +1388,10 @@ void ex_catch(exarg_T *eap)
}
if (caught) {
- /* Make this ":catch" clause active and reset did_emsg, got_int,
- * and did_throw. Put the exception on the caught stack. */
+ /* Make this ":catch" clause active and reset did_emsg and got_int.
+ * Put the exception on the caught stack. */
cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT;
- did_emsg = got_int = did_throw = FALSE;
+ did_emsg = got_int = false;
catch_exception((except_T *)cstack->cs_exception[idx]);
/* It's mandatory that the current exception is stored in the cstack
* so that it can be discarded at the next ":catch", ":finally", or
@@ -1426,6 +1401,10 @@ void ex_catch(exarg_T *eap)
if (cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_catch()");
}
+ // Discarding current_exceptions happens based on what is stored in
+ // cstack->cs_exception, *all* calls to discard_current_exception() are
+ // (and must be) guarded by current_exception check.
+ current_exception = NULL;
} else {
/*
* If there is a preceding catch clause and it caught the exception,
@@ -1484,7 +1463,7 @@ void ex_finally(exarg_T *eap)
* interrupt or throw) or for a ":finally" without ":try" or a multiple
* ":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.
+ * exception (current_exception), the finally clause must be executed.
*/
skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
@@ -1511,30 +1490,31 @@ void ex_finally(exarg_T *eap)
cleanup_conditionals(cstack, CSF_TRY, FALSE);
/*
- * Make did_emsg, got_int, did_throw pending. If set, they overrule
- * a pending ":continue", ":break", ":return", or ":finish". Then
- * we have particularly to discard a pending return value (as done
+ * Make did_emsg, got_int, current_exception pending. If set, they
+ * overrule a pending ":continue", ":break", ":return", or ":finish".
+ * Then we have particularly to discard a pending return value (as done
* by the call to cleanup_conditionals() above when did_emsg or
* got_int is set). The pending values are restored by the
* ":endtry", except if there is a new error, interrupt, exception,
* ":continue", ":break", ":return", or ":finish" in the following
* finally clause. A missing ":endwhile", ":endfor" or ":endif"
- * detected here is treated as if did_emsg and did_throw had
+ * detected here is treated as if did_emsg and current_exception had
* already been set, respectively in case that the error is not
- * converted to an exception, did_throw had already been unset.
+ * converted to an exception, current_exception had already been unset.
* We must not set did_emsg here since that would suppress the
* error message.
*/
- if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) {
+ if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) {
if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) {
report_discard_pending(CSTP_RETURN,
cstack->cs_rettv[cstack->cs_idx]);
discard_pending_return(cstack->cs_rettv[cstack->cs_idx]);
}
- if (pending == CSTP_ERROR && !did_emsg)
- pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0;
- else
- pending |= did_throw ? CSTP_THROW : 0;
+ if (pending == CSTP_ERROR && !did_emsg) {
+ pending |= (THROW_ON_ERROR ? CSTP_THROW : 0);
+ } else {
+ pending |= (current_exception ? CSTP_THROW : 0);
+ }
pending |= did_emsg ? CSTP_ERROR : 0;
pending |= got_int ? CSTP_INTERRUPT : 0;
assert(pending >= CHAR_MIN && pending <= CHAR_MAX);
@@ -1547,14 +1527,15 @@ void ex_finally(exarg_T *eap)
* exception. When emsg() is called for a missing ":endif" or
* a missing ":endwhile"/":endfor" detected here, the
* exception will be discarded. */
- if (did_throw && cstack->cs_exception[cstack->cs_idx]
- != current_exception)
+ if (current_exception
+ && cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_finally()");
+ }
}
/*
* Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
- * got_int, and did_throw and make the finally clause active.
+ * got_int, and current_exception and make the finally clause active.
* This will happen after emsg() has been called for a missing
* ":endif" or a missing ":endwhile"/":endfor" detected here, so
* that the following finally clause will be executed even then.
@@ -1589,7 +1570,7 @@ void ex_endtry(exarg_T *eap)
// made inactive by a ":continue", ":break", ":return", or ":finish" in
// the finally clause. The latter case need not be tested since then
// anything pending has already been discarded.
- skip = (did_emsg || got_int || did_throw
+ skip = (did_emsg || got_int || current_exception
|| !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE));
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
@@ -1606,12 +1587,13 @@ void ex_endtry(exarg_T *eap)
/*
* If an exception is being thrown, discard it to prevent it from
* being rethrown at the end of this function. It would be
- * discarded by the error message, anyway. Resets did_throw.
+ * discarded by the error message, anyway. Resets current_exception.
* This does not affect the script termination due to the error
* since "trylevel" is decremented after emsg() has been called.
*/
- if (did_throw)
+ if (current_exception) {
discard_current_exception();
+ }
} else {
idx = cstack->cs_idx;
@@ -1621,9 +1603,11 @@ void ex_endtry(exarg_T *eap)
* a finally clause, we need to rethrow it after closing the try
* conditional.
*/
- if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE)
- && !(cstack->cs_flags[idx] & CSF_FINALLY))
- rethrow = TRUE;
+ if (current_exception
+ && (cstack->cs_flags[idx] & CSF_TRUE)
+ && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
+ }
}
/* If there was no finally clause, show the user when debugging or
@@ -1644,11 +1628,12 @@ void ex_endtry(exarg_T *eap)
if (got_int) {
skip = TRUE;
(void)do_intthrow(cstack);
- /* The do_intthrow() call may have reset did_throw or
- * cstack->cs_pending[idx].*/
- rethrow = FALSE;
- if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY))
- rethrow = TRUE;
+ // The do_intthrow() call may have reset current_exception or
+ // cstack->cs_pending[idx].
+ rethrow = false;
+ if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ rethrow = true;
+ }
}
}
@@ -1710,26 +1695,30 @@ void ex_endtry(exarg_T *eap)
do_finish(eap, FALSE);
break;
- /* When the finally clause was entered due to an error,
- * interrupt or throw (as opposed to a ":continue", ":break",
- * ":return", or ":finish"), restore the pending values of
- * did_emsg, got_int, and did_throw. This is skipped, if there
- * was a new error, interrupt, throw, ":continue", ":break",
- * ":return", or ":finish". in the finally clause. */
+ // When the finally clause was entered due to an error,
+ // interrupt or throw (as opposed to a ":continue", ":break",
+ // ":return", or ":finish"), restore the pending values of
+ // did_emsg, got_int, and current_exception. This is skipped, if there
+ // was a new error, interrupt, throw, ":continue", ":break",
+ // ":return", or ":finish". in the finally clause.
default:
- if (pending & CSTP_ERROR)
- did_emsg = TRUE;
- if (pending & CSTP_INTERRUPT)
- got_int = TRUE;
- if (pending & CSTP_THROW)
- rethrow = TRUE;
+ if (pending & CSTP_ERROR) {
+ did_emsg = true;
+ }
+ if (pending & CSTP_INTERRUPT) {
+ got_int = true;
+ }
+ if (pending & CSTP_THROW) {
+ rethrow = true;
+ }
break;
}
}
- if (rethrow)
- /* Rethrow the current exception (within this cstack). */
+ if (rethrow) {
+ // Rethrow the current exception (within this cstack).
do_throw(cstack);
+ }
}
}
@@ -1759,33 +1748,34 @@ void enter_cleanup(cleanup_T *csp)
int pending = CSTP_NONE;
/*
- * Postpone did_emsg, got_int, did_throw. The pending values will be
+ * Postpone did_emsg, got_int, current_exception. The pending values will be
* restored by leave_cleanup() except if there was an aborting error,
* interrupt, or uncaught exception after this function ends.
*/
- if (did_emsg || got_int || did_throw || need_rethrow) {
- csp->pending = (did_emsg ? CSTP_ERROR : 0)
- | (got_int ? CSTP_INTERRUPT : 0)
- | (did_throw ? CSTP_THROW : 0)
- | (need_rethrow ? CSTP_THROW : 0);
-
- /* If we are currently throwing an exception (did_throw), save it as
- * well. On an error not yet converted to an exception, update
- * "force_abort" and reset "cause_abort" (as do_errthrow() would do).
- * This is needed for the do_cmdline() call that is going to be made
- * for autocommand execution. We need not save *msg_list because
- * there is an extra instance for every call of do_cmdline(), anyway.
+ if (did_emsg || got_int || current_exception || need_rethrow) {
+ csp->pending = (did_emsg ? CSTP_ERROR : 0)
+ | (got_int ? CSTP_INTERRUPT : 0)
+ | (current_exception ? CSTP_THROW : 0)
+ | (need_rethrow ? CSTP_THROW : 0);
+
+ /* If we are currently throwing an exception, save it as well. On an error
+ * not yet converted to an exception, update "force_abort" and reset
+ * "cause_abort" (as do_errthrow() would do). This is needed for the
+ * do_cmdline() call that is going to be made for autocommand execution. We
+ * need not save *msg_list because there is an extra instance for every call
+ * of do_cmdline(), anyway.
*/
- if (did_throw || need_rethrow)
+ if (current_exception || need_rethrow) {
csp->exception = current_exception;
- else {
+ } else {
csp->exception = NULL;
if (did_emsg) {
force_abort |= cause_abort;
cause_abort = FALSE;
}
}
- did_emsg = got_int = did_throw = need_rethrow = FALSE;
+ did_emsg = got_int = need_rethrow = false;
+ current_exception = NULL;
/* Report if required by the 'verbose' option or when debugging. */
report_make_pending(pending, csp->exception);
@@ -1857,19 +1847,20 @@ void leave_cleanup(cleanup_T *csp)
force_abort = FALSE;
}
- /*
- * Restore the pending values of did_emsg, got_int, and did_throw.
- */
- if (pending & CSTP_ERROR)
- did_emsg = TRUE;
- if (pending & CSTP_INTERRUPT)
- got_int = TRUE;
- if (pending & CSTP_THROW)
- need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */
+ // Restore the pending values of did_emsg, got_int, and current_exception.
+ if (pending & CSTP_ERROR) {
+ did_emsg = true;
+ }
+ if (pending & CSTP_INTERRUPT) {
+ got_int = true;
+ }
+ if (pending & CSTP_THROW) {
+ need_rethrow = true; // current_exception will be set by do_one_cmd()
+ }
- /* Report if required by the 'verbose' option or when debugging. */
- report_resume_pending(pending,
- (pending & CSTP_THROW) ? (void *)current_exception : NULL);
+ // Report if required by the 'verbose' option or when debugging.
+ report_resume_pending(
+ pending, ((pending & CSTP_THROW) ? (void *)current_exception : NULL));
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index e857f5ff5b..31bde4aa1e 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -317,17 +317,11 @@ EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */
/*
* The exception currently being thrown. Used to pass an exception to
* a different cstack. Also used for discarding an exception before it is
- * caught or made pending. Only valid when did_throw is TRUE.
+ * caught or made pending.
*/
EXTERN except_T *current_exception;
/*
- * did_throw: An exception is being thrown. Reset when the exception is caught
- * or as long as it is pending in a finally clause.
- */
-EXTERN int did_throw INIT(= FALSE);
-
-/*
* need_rethrow: set to TRUE when a throw that cannot be handled in do_cmdline()
* must be propagated to the cstack of the previously called do_cmdline().
*/
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
index ffb6a26aef..f9cec903a0 100644
--- a/test/functional/ui/cmdline_highlight_spec.lua
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -737,6 +737,22 @@ describe('Command-line coloring', function()
feed('<CR><CR>')
eq('', meths.get_var('out'))
end)
+ it('does not crash when callback has caught not-a-editor-command exception',
+ function()
+ source([[
+ function CaughtExc(cmdline) abort
+ try
+ gibberish
+ catch
+ " Do nothing
+ endtry
+ return []
+ endfunction
+ ]])
+ set_color_cb('CaughtExc')
+ start_prompt('1')
+ eq(1, meths.eval('1'))
+ end)
end)
describe('Ex commands coloring support', function()
it('works', function()