exceptionerror-handlingmacrosracket

My Racket exception handling macro does not locate source of error


I'm trying to implement parameterized error handling. If an error occurs and the parameter SAFE is true, then an error is raised an the program halts. But if SAFE is false, a warning is printed instead and the program continues.

#lang racket
(require syntax/parse/define)

;; raises parameterized error message
(define-simple-macro
  (err param:id msg)
  (if (param)
      (error msg)
      (display (format "Here be dragons: ~a\n" msg))))

;; a problematic procedure
(define (bad-thing?) (thunk #t))

;; default value of SAFE = #t
(define SAFE (make-parameter #t))

;; prints warning
(parameterize ([SAFE #f])
  (when (bad-thing?)
    (err SAFE "better watch out.")))

;; raises error
(when (bad-thing?)
  (err SAFE "Hell's bells, an exception has occurred!"))

The problem with the err macro is that when an error is raised, it traces back to where the macro is defined instead of where the problematic procedure bad-thing? was invoked.

There has to be a better way! Can somebody point me in the right direction?


Solution

  • If you use the base syntax-parse instead of a higher level wrapper to get the syntax object representing the macro call site, you can then wrap the error in syntax/loc to have the location appear as it:

    #lang racket
    (require (for-syntax syntax/parse))
    
    ;; raises parameterized error message
    (define-syntax (err stx)
      (syntax-parse stx
        [(err param:id msg)
         #`(if (param)
             #,(syntax/loc stx (error msg))
             (display (format "Here be dragons: ~a\n" msg)))]))
    
    ;; a problematic procedure
    (define (bad-thing?) (thunk #t))
    
    ;; default value of SAFE = #t
    (define SAFE (make-parameter #t))
    
    ;; prints warning
    (parameterize ([SAFE #f])
      (when (bad-thing?)
        (err SAFE "better watch out.")))
    
    ;; raises error
    (when (bad-thing?)
      (err SAFE "Hell's bells, an exception has occurred!"))