diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index a7d605cf0a0..3925bdd1b40 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -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. diff --git a/etc/NEWS b/etc/NEWS index c26baa03266..09fb35322d3 100644 --- a/etc/NEWS +++ b/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 diff --git a/lisp/subr.el b/lisp/subr.el index 0ad86fd30a1..3cf4e8276d6 100644 --- a/lisp/subr.el +++ b/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) diff --git a/src/eval.c b/src/eval.c index 7ca9d761a7e..0b451b2c891 100644 --- a/src/eval.c +++ b/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,