Coverage report: /home/ellis/comp/core/std/condition.lisp
Kind | Covered | All | % |
expression | 54 | 275 | 19.6 |
branch | 0 | 4 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; condition.lisp --- Conditions and other exception handlers
4
(in-package :std/condition)
5
(declaim (optimize (speed 3)))
7
(defvar *error-message* "An error occured"
8
"The default error message used in STD-ERROR conditions.")
10
"A list of condition handlers - often useful in asynchronous contexts.")
13
(declaim (inline car-eql))
14
(defun car-eql (a cons)
15
"Return T if the CAR of CONS is EQL to A."
18
(defmacro nyi! (&optional comment)
20
(error "Not Yet Implemented!")
21
(when ',comment (print ',comment))))
23
;;; Standard Conditions
24
(define-condition std-error (error)
25
((message :initarg :message
26
:initform *error-message*
27
:reader error-message))
28
(:documentation "Standard Error")
29
(:report (lambda (condition stream)
30
(format stream "~A" (error-message condition)))))
32
(defun std-error (&rest args)
33
"Signal an error of type STD-ERROR."
37
:message (format nil "~A: ~A" *error-message* args)))
39
(define-condition std-warning (warning)
40
((message :initarg :message
42
:reader std-warning-message))
43
(:documentation "Standard Warning")
45
(lambda (condition stream)
46
(when (std-warning-message condition)
47
(format stream "~X" (std-warning-message condition))))))
49
(defun std-warning (&optional message)
50
(warn 'std-warning :message message))
53
(defmacro deferror (name (&rest parent-types) (&rest slot-specs) &rest options)
54
"Define an error condition."
55
(let ((fun (member :auto options :test #'car-eql))
56
(%ancestors (flatten (mapcar (lambda (x)
57
(mapcar 'sb-mop:class-name
58
(sb-mop:class-precedence-list (find-class x))))
61
(setf options (remove (car fun) options))
62
(setf fun (cadar fun)))
64
(eval-when (:compile-toplevel :execute)
65
(define-condition ,name ,(or parent-types '(std-error)) ,slot-specs ,@options))
68
((or (member 'simple-error ',%ancestors)
69
(member 'simple-condition ',%ancestors))
70
(def-simple-error-reporter ,name))
72
(member 'invalid-item ',%ancestors)
73
(member 'invalid-argument ',%ancestors))
74
(def-invalid-item-reporter ,name))
76
(define-error-reporter ,name ',fun))
77
(t (define-error-reporter ,name)))))))
79
(defmacro define-error-reporter (err &optional (message *error-message*))
80
`(defun ,err (&rest args)
81
,(format nil "Signal an error of type ~A with ARGS." err)
85
:message (format nil "~A: ~A" ,message args))))
87
(defmacro def-simple-error-reporter (name)
89
(defun ,name (fmt &rest args)
90
,(format nil "Signal an error of type ~A with FMT string and ARGS." name)
95
:format-arguments args))))
97
(defmacro def-invalid-item-reporter (name)
98
`(defun ,name (item &optional reason)
99
,(format nil "Signal an error of type ~A." name)
101
"Ignore and continue"
104
(when reason (list :reason reason)))))
107
(defmacro defwarning (name (&rest parent-types) (&rest slot-specs) &rest options)
108
"Define an warning condition."
109
(let ((fun (member :auto options :test #'car-eql)))
110
(when fun (setq options (remove (car fun) options)))
112
(eval-when (:compile-toplevel :execute)
113
(define-condition ,name ,(or parent-types '(std-warning)) ,slot-specs ,@options))
115
(if (or (find 'simple-warning ',parent-types)
116
(find 'simple-condition ',parent-types))
117
(def-simple-warning-reporter ,name)
118
(def-warning-reporter ,name))))))
120
(defmacro def-warning-reporter (name)
121
`(defun ,name (&optional message)
122
,(format nil "Signal a warning of type ~A with optional MESSAGE." name)
127
(defmacro def-simple-warning-reporter (name)
128
`(defun ,name (fmt &rest args)
129
,(format nil "Signal an error of type ~A with FMT string and ARGS." name)
133
:format-arguments args)))
136
(defun required-argument (&optional name)
137
"Signals an error for a missing argument of NAME. Intended for
138
use as an initialization form for structure and class-slots, and
139
a default value for required keyword arguments."
140
(error "Required argument ~@[~S ~]missing." name))
143
(define-condition simple-style-warning (simple-warning style-warning)
145
(:documentation "Simple style warnings."))
147
(defun simple-style-warning (message &rest args)
148
"Signal a SIMPLE-STYLE-WARNING using format-contorl MESSAGE and format-arguments ARGS."
149
(warn 'simple-style-warning :format-control message :format-arguments args))
151
;; We don't specify a :report for simple-reader-error to let the
152
;; underlying implementation report the line and column position for
153
;; us. Unfortunately this way the message from simple-error is not
154
;; displayed, unless there's special support for that in the
155
;; implementation. But even then it's still inspectable from the
157
(define-condition simple-reader-error (sb-int:simple-reader-error)
159
(:documentation "Simple reader errors."))
161
(defun simple-reader-error (stream message &rest args)
162
"Signal an error of type SIMPLE-READER-ERROR."
163
(error 'simple-reader-error
165
:format-control message
166
:format-arguments args))
168
(define-condition simple-parse-error (simple-error parse-error)
170
(:documentation "Simple parse errors."))
172
(defun simple-parse-error (message &rest args)
173
"Signal an error of type SIMPLE-PARSE-ERROR."
174
(error 'simple-parse-error
175
:format-control message
176
:format-arguments args))
178
(define-condition simple-program-error (simple-error program-error)
180
(:documentation "Simple program errors."))
182
(defun simple-program-error (message &rest args)
183
"Signal an error of type SIMPLE-PROGRAM-ERROR."
184
(error 'simple-program-error
185
:format-control message
186
:format-arguments args))
188
(define-condition circular-dependencies (simple-error)
191
:initform (error "Must specify items")
192
:reader error-items))
193
(:report (lambda (condition stream)
194
(declare (ignore condition))
195
(format stream "Circular dependency detected")))
196
(:documentation "A condition which is signalled when a circular dependency is encountered."))
198
(define-condition unknown-argument (error)
201
:initform (error "Must specify argument name")
205
:initform (error "Must specify argument kind")
207
(:report (lambda (condition stream)
208
(format stream "Unknown argument ~A of kind ~A"
209
(error-name condition)
210
(error-kind condition))))
211
(:documentation "A condition which is signalled when an unknown argument is encountered."))
213
(defun unknown-argument-p (value)
214
"Return T if VALUE is a condition of type UNKNOWN-ARGUMENT."
215
(typep value 'unknown-argument))
217
(define-condition missing-argument (simple-error)
220
:initform (error "Must specify argument item")
222
(:report (lambda (condition stream)
223
(declare (ignore condition))
224
(format stream "Missing argument")))
225
(:documentation "A condition which is signalled when an option expects an argument, but none was provided"))
227
(defun missing-argument-p (value)
228
"Return T if VALUE is a condition of type MISSING-ARGUMENT."
229
(typep value 'missing-argument))
231
(define-condition invalid-item ()
234
:initform (error "Must specify argument item")
236
:documentation "The item which is identified as invalid")
239
:initform (error "Must specify reason")
241
:documentation "The reason why this item is invalid"))
242
(:documentation "A condition which is signalled when an argument is identified as invalid."))
244
(define-condition invalid-argument (simple-error invalid-item) ()
245
(:report (lambda (condition stream)
246
(format stream "Invalid argument: ~A~%Reason: ~A" (error-item condition) (error-reason condition))))
247
(:documentation "Invalid argument errors."))
249
(define-condition conflicting-arguments (simple-error invalid-item) ()
250
(:report (lambda (condition stream)
251
(format stream "Conflicting arguments: ~A~%Reason: ~A" (error-item condition) (error-reason condition))))
252
(:documentation "Conflicting argument errors."))
254
(define-condition unknown-token (std-error)
255
((token :reader error-token :initarg :token))
256
(:documentation "Unknown token errors."))
258
(defmethod print-object ((c unknown-token) stream)
259
(when (slot-boundp c 'token)
260
(format stream "Unknown token: ~A.~%" (error-token c)))
264
(defmacro ignore-some-conditions ((&rest conditions) &body body)
265
"Similar to CL:IGNORE-ERRORS but the (unevaluated) CONDITIONS
266
list determines which specific conditions are to be ignored."
269
,@(loop for condition in conditions collect
270
`(,condition (c) (values nil c)))))
272
(defmacro unwind-protect-case ((&optional abort-flag) protected-form &body clauses)
273
"Like CL:UNWIND-PROTECT, but you can specify the circumstances that
274
the cleanup CLAUSES are run.
276
clauses ::= (:NORMAL form*)* | (:ABORT form*)* | (:ALWAYS form*)*
278
Clauses can be given in any order, and more than one clause can be
279
given for each circumstance. The clauses whose denoted circumstance
280
occured, are executed in the order the clauses appear.
282
ABORT-FLAG is the name of a variable that will be bound to T in
283
CLAUSES if the PROTECTED-FORM aborted preemptively, and to NIL
288
(unwind-protect-case ()
290
(:normal (format t \"This is only evaluated if PROTECTED-FORM executed normally.~%\"))
291
(:abort (format t \"This is only evaluated if PROTECTED-FORM aborted preemptively.~%\"))
292
(:always (format t \"This is evaluated in either case.~%\")))
294
(unwind-protect-case (aborted-p)
296
(:always (perform-cleanup-if aborted-p)))
298
(check-type abort-flag (or null symbol))
299
(let ((gflag (gensym "FLAG+")))
301
(unwind-protect (multiple-value-prog1 ,protected-form (setf ,gflag nil))
302
(let ,(and abort-flag `((,abort-flag ,gflag)))
303
,@(loop for (cleanup-kind . forms) in clauses
304
collect (ecase cleanup-kind
305
(:normal `(when (not ,gflag) ,@forms))
306
(:abort `(when ,gflag ,@forms))
307
(:always `(progn ,@forms)))))))))
311
(defvar *catch-errors-p* nil
312
"When non-nil catch and log errors instead of invoking the debugger.")
314
(defgeneric maybe-invoke-debugger (condition)
315
(:documentation "This generic function is called whenever a
316
condition CONDITION is signaled in Hunchentoot. You might want to
317
specialize it on specific condition classes for debugging purposes.")
319
"The default method invokes the debugger with CONDITION if
320
*CATCH-ERRORS-P* is NIL."
321
(unless *catch-errors-p*
322
(invoke-debugger condition))))
324
(defmacro with-debugger (&body body)
325
"Executes BODY and invokes the debugger if an error is signaled and
326
*CATCH-ERRORS-P* is NIL."
327
`(handler-bind ((error #'maybe-invoke-debugger))
330
(defmacro ignore-errors* (&body body)
331
"Like IGNORE-ERRORS, but observes *CATCH-ERRORS-P*."
332
`(ignore-errors (with-debugger ,@body)))
334
(defmacro handler-case* (expression &rest clauses)
335
"Like HANDLER-CASE, but observes *CATCH-ERRORS-P*."
336
`(handler-case (with-debugger ,expression)
339
(defun get-backtrace ()
340
"Returns a string with a backtrace of what the Lisp system thinks is
341
the 'current' error."
343
(with-output-to-string (s)
344
(sb-debug:print-backtrace :stream s))
346
(format nil "Could not generate backtrace: ~A." condition))))
349
(define-condition meta-condition () ()
350
(:documentation "A condition which is signalled somewhere within the CLOS/MOP machinery."))
352
(define-condition missing-method (error meta-condition)
354
(:documentation "Missing CLOS method errors."))
356
(define-condition missing-methods (error meta-condition)
358
(:documentation "Multiple missing CLOS methods errors."))
361
(define-condition wrapped-condition ()
362
((value :type condition :reader wrapped-condition-value))
364
"A container for transporting conditions - usually to another thread."))
366
(defun wrap-condition (condition)
367
"Wrap a condition. A non-error condition may also be wrapped, though it
368
will still be signaled with `signal'."
371
:value (ctypecase condition
372
(symbol (make-condition condition))
373
(condition condition))))
375
(define-condition wrapped-error (wrapped-condition) ()
376
(:documentation "A container for transporting errors - usually to another thread."))
378
(defun wrap-error (condition)
379
"Wrap an error. A non-error condition may also be wrapped, though it
380
will still be signaled with `error'."
383
:value (ctypecase condition
384
(symbol (make-condition condition))
385
(condition condition))))