diff options
Diffstat (limited to 'src/nvim/ex_eval.c')
| -rw-r--r-- | src/nvim/ex_eval.c | 363 | 
1 files changed, 179 insertions, 184 deletions
| diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 65112c4dd8..e23945c842 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -1,3 +1,6 @@ +// 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 +  // TODO(ZyX-I): move to eval/executor  /// @file ex_eval.c @@ -42,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 @@ -65,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))  /* @@ -91,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;  }  /* @@ -175,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 @@ -205,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();    } @@ -330,51 +343,49 @@ 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. - */ -char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free) +// Get an exception message that is to be stored in current_exception->value. +char_u *get_exception_string(void *value, except_type_T type, char_u *cmdname, +                             int *should_free)  {    char_u      *ret, *mesg;    char_u      *p, *val; @@ -432,13 +443,11 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should  } -/* - * Throw a new exception.  Return FAIL when out of memory or it was tried to - * throw an illegal user exception.  "value" is the exception string for a - * user or interrupt exception, or points to a message list in case of an - * error exception. - */ -static int throw_exception(void *value, int type, char_u *cmdname) +// Throw a new exception.  Return FAIL when out of memory or it was tried to +// throw an illegal user exception.  "value" is the exception string for a +// user or interrupt exception, or points to a message list in case of an +// error exception. +static int throw_exception(void *value, except_type_T type, char_u *cmdname)  {    except_T    *excp;    int should_free; @@ -517,7 +526,7 @@ static void discard_exception(except_T *excp, int was_finished)    char_u              *saved_IObuff;    if (excp == NULL) { -    EMSG(_(e_internal)); +    internal_error("discard_exception()");      return;    } @@ -561,10 +570,11 @@ static void discard_exception(except_T *excp, int was_finished)   */  void discard_current_exception(void)  { -  discard_exception(current_exception, FALSE); +  discard_exception(current_exception, false); +  // 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;  }  /* @@ -617,8 +627,9 @@ static void catch_exception(except_T *excp)   */  static void finish_exception(except_T *excp)  { -  if (excp != caught_stack) -    EMSG(_(e_internal)); +  if (excp != caught_stack) { +    internal_error("finish_exception()"); +  }    caught_stack = caught_stack->caught;    if (caught_stack != NULL) {      set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1); @@ -792,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); @@ -851,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] @@ -949,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" @@ -1230,8 +1217,6 @@ void do_throw(struct condstack *cstack)      cstack->cs_flags[idx] &= ~CSF_ACTIVE;      cstack->cs_exception[idx] = current_exception;    } - -  did_throw = TRUE;  }  /* @@ -1250,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" @@ -1350,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 @@ -1410,18 +1388,23 @@ 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         * ":endtry" or when the catch clause is left by a ":continue",         * ":break", ":return", ":finish", error, interrupt, or another         * exception. */ -      if (cstack->cs_exception[cstack->cs_idx] != current_exception) -        EMSG(_(e_internal)); +      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, @@ -1480,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); @@ -1507,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); @@ -1543,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) -          EMSG(_(e_internal)); +        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. @@ -1585,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)) { @@ -1602,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; @@ -1617,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 @@ -1640,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; +        }        }      } @@ -1706,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); +    }    }  } @@ -1755,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); @@ -1853,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));    }  } | 
