Improve the error API
Define new functions to manipulate error descriptors and add support for `signal` to *re*signal a previous error. * src/eval.c (Fsignal): Make the second arg optional and document the possibility of passing a whole error descriptor to re-signal it. (signal_or_quit): Fix a few corner case issues when DATA is `nil` and ERROR_SYMBOL is an error descriptor. * lisp/subr.el (error-type-p, error--p, error-type, error-data) (error-has-type-p, error-slot-value): New function. * doc/lispref/control.texi (Handling Errors): Prefer "error descriptor" to "error description". Use the new single-arg call to `signal` to re-throw an error. Document `error-type`, `error-data` and `error-slot-value`. (Error Symbols): Document the new functions `error-type-p` and `error-has-type-p`.
This commit is contained in:
@@ -2447,12 +2447,13 @@ as the overall value.
|
||||
The argument @var{var} is a variable. @code{condition-case} does not
|
||||
bind this variable when executing the @var{protected-form}, only when it
|
||||
handles an error. At that time, it binds @var{var} locally to an
|
||||
@dfn{error description}, which is a list giving the particulars of the
|
||||
error. The error description has the form @code{(@var{error-symbol}
|
||||
@dfn{error descriptor}, also sometimes called error description,
|
||||
which is a list giving the particulars of the error.
|
||||
The error descriptor has the form @code{(@var{error-symbol}
|
||||
. @var{data})}. The handler can refer to this list to decide what to
|
||||
do. For example, if the error is for failure opening a file, the file
|
||||
name is the second element of @var{data}---the third element of the
|
||||
error description.
|
||||
error descriptor.
|
||||
|
||||
If @var{var} is @code{nil}, that means no variable is bound. Then the
|
||||
error symbol and associated data are not available to the handler.
|
||||
@@ -2469,15 +2470,31 @@ Sometimes it is necessary to re-throw a signal caught by
|
||||
how to do that:
|
||||
|
||||
@example
|
||||
(signal (car err) (cdr err))
|
||||
(signal err)
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
where @code{err} is the error description variable, the first argument
|
||||
where @code{err} is the error descriptor variable, the first argument
|
||||
to @code{condition-case} whose error condition you want to re-throw.
|
||||
@xref{Definition of signal}.
|
||||
@end defspec
|
||||
|
||||
@defun error-type error
|
||||
This function returns the error symbol of the error descriptor @var{error}.
|
||||
@end defun
|
||||
|
||||
@defun error-data error
|
||||
This function returns the data of the error descriptor @var{error}.
|
||||
@end defun
|
||||
|
||||
@defun error-slot-value error pos
|
||||
This function returns the value in the field number @var{pos} of the error
|
||||
descriptor @var{error}. The fields are numbered starting with 1. E.g.,
|
||||
for an error of type @code{wrong-type-argument}, @code{(error-slot-value
|
||||
@var{error} 2)} returns the object that failed the type test, and
|
||||
@code{(error-slot-value @var{error} 1)} returns the predicate that failed.
|
||||
@end defun
|
||||
|
||||
@defun error-message-string error-descriptor
|
||||
This function returns the error message string for a given error
|
||||
descriptor. It is useful if you want to handle an error by printing the
|
||||
@@ -2615,7 +2632,7 @@ Emacs searches all the active @code{condition-case} and
|
||||
specifies one or more of these condition names. When the innermost
|
||||
matching handler is one installed by @code{handler-bind}, the
|
||||
@var{handler} function is called with a single argument holding the
|
||||
error description.
|
||||
error descriptor.
|
||||
|
||||
Contrary to what happens with @code{condition-case}, @var{handler} is
|
||||
called in the dynamic context where the error happened. This means it
|
||||
@@ -2799,6 +2816,18 @@ make it possible to categorize errors at various levels of generality
|
||||
when you write an error handler. Using error symbols alone would
|
||||
eliminate all but the narrowest level of classification.
|
||||
|
||||
@defun error-type-p symbol
|
||||
This function returns non-@code{nil} if @var{symbol} is a valid
|
||||
error condition name.
|
||||
@end defun
|
||||
|
||||
@defun error-has-type-p error condition
|
||||
This function tests whether @var{condition} is a parent of the error
|
||||
symbol of the error descriptor @var{error}.
|
||||
It returns non-@code{nil} if the type of the error descriptor
|
||||
@var{error} belongs to the condition name @var{condition}.
|
||||
@end defun
|
||||
|
||||
@xref{Standard Errors}, for a list of the main error symbols
|
||||
and their conditions.
|
||||
|
||||
|
||||
7
etc/NEWS
7
etc/NEWS
@@ -4009,6 +4009,13 @@ that will provide an Xref backend when used.
|
||||
|
||||
* Lisp Changes in Emacs 31.1
|
||||
|
||||
+++
|
||||
** Extended error API
|
||||
The API to manipulate error descriptors has been improved.
|
||||
You can now do (signal err) instead (signal (car err) (cdr err)).
|
||||
And there are new functions: 'error-type-p', 'error-type',
|
||||
'error-has-type-p', and 'error-slot-value'.
|
||||
|
||||
+++
|
||||
** 'secure-hash' now supports generating SHA-3 message digests.
|
||||
The list returned by 'secure-hash-algorithms' now contains the symbols
|
||||
|
||||
32
lisp/subr.el
32
lisp/subr.el
@@ -568,8 +568,40 @@ Defaults to `error'."
|
||||
(cons parent (get parent 'error-conditions)))))
|
||||
(put name 'error-conditions
|
||||
(delete-dups (copy-sequence (cons name conditions))))
|
||||
;; FIXME: Make `error-message-string' more flexible, e.g. allow
|
||||
;; the message to be specified by a `format' string or a function.
|
||||
(when message (put name 'error-message message))))
|
||||
|
||||
(defun error-type-p (symbol)
|
||||
"Return non-nil if SYMBOL is a condition type."
|
||||
(get symbol 'error-conditions))
|
||||
|
||||
(defun error--p (object)
|
||||
"Return non-nil if OBJECT looks like a valid error descriptor."
|
||||
(let ((type (car-safe object)))
|
||||
(and type (symbolp type) (listp (cdr object))
|
||||
(error-type-p type))))
|
||||
|
||||
(defalias 'error-type #'car
|
||||
"Return the symbol which represents the type of ERROR.
|
||||
\n(fn ERROR)")
|
||||
|
||||
(defalias 'error-data #'cdr
|
||||
"Return the slots attached to ERROR, as a list.
|
||||
\n(fn ERROR)")
|
||||
|
||||
(defun error-has-type-p (error condition)
|
||||
"Return non-nil if ERROR is of type CONDITION (or a subtype of it)."
|
||||
(unless (error--p error)
|
||||
(signal 'wrong-type-argument (list #'error--p error)))
|
||||
(or (eq condition t)
|
||||
(memq condition (get (car error) 'error-conditions))))
|
||||
|
||||
(defalias 'error-slot-value #'elt
|
||||
"Access the SLOT of object ERROR.
|
||||
Slots are specified by position, and slot 0 is the error symbol.
|
||||
\n(fn ERROR SLOT)")
|
||||
|
||||
;; We put this here instead of in frame.el so that it's defined even on
|
||||
;; systems where frame.el isn't loaded.
|
||||
(defun frame-configuration-p (object)
|
||||
|
||||
12
src/eval.c
12
src/eval.c
@@ -1871,10 +1871,15 @@ probably_quit (void)
|
||||
unbind_to (gc_count, Qnil);
|
||||
}
|
||||
|
||||
DEFUN ("signal", Fsignal, Ssignal, 2, 2, 0,
|
||||
DEFUN ("signal", Fsignal, Ssignal, 1, 2, 0,
|
||||
doc: /* Signal an error. Args are ERROR-SYMBOL and associated DATA.
|
||||
This function does not return.
|
||||
|
||||
When signaling a new error, the DATA argument is mandatory.
|
||||
When re-signaling an error to propagate it to further handlers,
|
||||
DATA has to be omitted and the first argument has to be the whole
|
||||
error descriptor.
|
||||
|
||||
When `noninteractive' is non-nil (in particular, in batch mode), an
|
||||
unhandled error calls `kill-emacs', which terminates the Emacs
|
||||
session with a non-zero exit code.
|
||||
@@ -1942,13 +1947,14 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool continuable)
|
||||
/* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */
|
||||
/* FIXME: Here we still "split" the error object
|
||||
into its error-symbol and its error-data? */
|
||||
calln (Vsignal_hook_function, error_symbol, data);
|
||||
calln (Vsignal_hook_function, real_error_symbol,
|
||||
NILP (data) && CONSP (error) ? XCDR (error) : data);
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
conditions = Fget (real_error_symbol, Qerror_conditions);
|
||||
if (NILP (conditions))
|
||||
signal_error ("Invalid error symbol", error_symbol);
|
||||
signal_error ("Invalid error symbol", real_error_symbol);
|
||||
|
||||
/* Remember from where signal was called. Skip over the frame for
|
||||
`signal' itself. If a frame for `error' follows, skip that,
|
||||
|
||||
Reference in New Issue
Block a user