API: better way to capture abort-causing non-exception errors

This condition is not perfectly reliable:
    (did_emsg && force_abort && !current_exception)

The more proper way to check for abort-causing non-exception errors is
to set up `msg_list` using the "pattern" given by do_cmdline().
This commit is contained in:
Justin M. Keyes
2018-05-09 03:02:12 +02:00
parent c9f3174075
commit 32b0470b03
4 changed files with 28 additions and 26 deletions

View File

@@ -260,7 +260,7 @@ theend:
/// Evaluates a VimL expression (:help expression). /// Evaluates a VimL expression (:help expression).
/// Dictionaries and Lists are recursively expanded. /// Dictionaries and Lists are recursively expanded.
/// ///
/// On execution error: fails with generic error; v:errmsg is not updated. /// On execution error: fails with VimL error, does not update v:errmsg.
/// ///
/// @param expr VimL expression string /// @param expr VimL expression string
/// @param[out] err Error details, if any /// @param[out] err Error details, if any
@@ -269,7 +269,6 @@ Object nvim_eval(String expr, Error *err)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
Object rv = OBJECT_INIT; Object rv = OBJECT_INIT;
// Evaluate the expression
try_start(); try_start();
typval_T rettv; typval_T rettv;
@@ -278,11 +277,9 @@ Object nvim_eval(String expr, Error *err)
} }
if (!try_end(err)) { if (!try_end(err)) {
// No errors, convert the result
rv = vim_to_object(&rettv); rv = vim_to_object(&rettv);
} }
// Free the Vim object
tv_clear(&rettv); tv_clear(&rettv);
return rv; return rv;
@@ -315,7 +312,9 @@ Object nvim_execute_lua(String code, Array args, Error *err)
/// @return Result of the function call /// @return Result of the function call
static Object _call_function(String fn, Array args, dict_T *self, Error *err) static Object _call_function(String fn, Array args, dict_T *self, Error *err)
{ {
static int recursive = 0; // recursion depth
Object rv = OBJECT_INIT; Object rv = OBJECT_INIT;
if (args.size > MAX_FUNC_ARGS) { if (args.size > MAX_FUNC_ARGS) {
api_set_error(err, kErrorTypeValidation, api_set_error(err, kErrorTypeValidation,
"Function called with too many arguments"); "Function called with too many arguments");
@@ -331,25 +330,36 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
} }
} }
try_start(); // `msg_list` controls the collection of abort-causing non-exception errors,
msg_first_ignored_err = NULL; // which would otherwise be ignored. This pattern is from do_cmdline().
struct msglist **saved_msg_list = msg_list;
struct msglist *private_msg_list;
msg_list = &private_msg_list;
private_msg_list = NULL;
// Initialize `force_abort` and `suppress_errthrow` at the top level.
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
current_exception = NULL;
// `did_emsg` is set by emsg(), which cancels execution.
did_emsg = false;
}
recursive++;
try_start();
typval_T rettv; typval_T rettv;
int dummy; int dummy;
int r = call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size, // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
vim_args, NULL, curwin->w_cursor.lnum, // (see above) to capture abort-causing non-exception errors.
curwin->w_cursor.lnum, &dummy, true, NULL, self); (void)call_func((char_u *)fn.data, (int)fn.size, &rettv, (int)args.size,
// call_func() retval is deceptive; must also check did_emsg et al. vim_args, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum,
if (msg_first_ignored_err &dummy, true, NULL, self);
&& (r == FAIL || (did_emsg && force_abort && !current_exception))) {
api_set_error(err, kErrorTypeException, msg_first_ignored_err);
}
if (!try_end(err)) { if (!try_end(err)) {
rv = vim_to_object(&rettv); rv = vim_to_object(&rettv);
} }
xfree(msg_first_ignored_err);
msg_first_ignored_err = NULL;
tv_clear(&rettv); tv_clear(&rettv);
msg_list = saved_msg_list; // Restore the exception context.
recursive--;
free_vim_args: free_vim_args:
while (i > 0) { while (i > 0) {

View File

@@ -6242,8 +6242,8 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
/// new_argcount = argv_func(current_argcount, argv, called_func_argcount) /// new_argcount = argv_func(current_argcount, argv, called_func_argcount)
/// ///
/// @return FAIL if function cannot be called, else OK (even if an error /// @return FAIL if function cannot be called, else OK (even if an error
/// occurred while executing the function! Use `msg_first_ignored_err` /// occurred while executing the function! Set `msg_list` to capture
/// to get the error) /// the error, see do_cmdline()).
int int
call_func( call_func(
const char_u *funcname, // name of the function const char_u *funcname, // name of the function

View File

@@ -67,7 +67,6 @@ static char_u *confirm_msg_tail; /* tail of confirm_msg */
MessageHistoryEntry *first_msg_hist = NULL; MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL; MessageHistoryEntry *last_msg_hist = NULL;
char *msg_first_ignored_err = NULL;
static int msg_hist_len = 0; static int msg_hist_len = 0;
static FILE *verbose_fd = NULL; static FILE *verbose_fd = NULL;
@@ -505,9 +504,6 @@ int emsg(const char_u *s_)
if (cause_errthrow((char_u *)s, severe, &ignore) == true) { if (cause_errthrow((char_u *)s, severe, &ignore) == true) {
if (!ignore) { if (!ignore) {
did_emsg = true; did_emsg = true;
if (msg_first_ignored_err == NULL) {
msg_first_ignored_err = xstrdup(s);
}
} }
return true; return true;
} }

View File

@@ -85,10 +85,6 @@ extern MessageHistoryEntry *first_msg_hist;
/// Last message /// Last message
extern MessageHistoryEntry *last_msg_hist; extern MessageHistoryEntry *last_msg_hist;
/// Abort-causing non-exception error ignored by emsg(), needed by callers
/// (RPC API) of call_func() to get error details when messages are disabled.
extern char *msg_first_ignored_err;
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.h.generated.h" # include "message.h.generated.h"
#endif #endif