cursive
cursive copied to clipboard
Review error messages in Cursive
Recently in Slack, @grav asked about stack traces being printed in AssertionErrors (here). In the podcast he linked to, @puredanger discussed how tools including Cursive can interfere with the error messages produced by Clojure itself. I'm going to review the general classes of errors to ensure Cursive does something sensible with each of them.
This should include a review of the error handling in Clojure's AOT compilation, which doesn't handle all errors very well.
Testing matrix:
Clojure version: 1.8, 1.9, 1.10.1 Environment: clojure.main REPL, socket REPL, nREPL. Method: direct typing into REPL, send form from file
Errors (from https://clojure.atlassian.net/browse/CLJ-2373):
;; Error during read
user=> :::5
Syntax error reading source at (2:0). Cause: Invalid token: :::5
;; Macroexpand spec error
user=> (let [x])
Syntax error macroexpanding clojure.core/let at (2:1). Cause: Call to clojure.core/let did not conform to spec.
[x] - failed: even-number-of-forms? at: [:bindings] spec: :clojure.core.specs.alpha/bindings
;; Macroexpand intentional check error
user=> (cond 1)
Syntax error macroexpanding cond at (3:1).
Cause: cond requires an even number of forms
;; Macroexpand unexpected error
user=> (defmulti 5 class)
Unexpected error macroexpanding defmulti at (4:1).
Cause: ClassCastException java.lang.Long cannot be cast to clojure.lang.IObj
;; Compile error
user=> (def 5)
Syntax error compiling def at (5:1).
Cause: First argument to def must be a Symbol
;; Evaluation error
user=> (/ 1 0)
Evaluation error at clojure.lang.Numbers.divide (Numbers.java:163). ArithmeticException Divide by zero
;; Spec function invocation error
user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as stest])
nil
user=> (defn f [a] a)
#'user/f
user=> (s/fdef f :args (s/cat :a int?))
user/f
user=> (stest/instrument `f)
[user/f]
user=> (f "oops")
Evaluation error at clojure.spec.test.alpha/spec-checking-fn/conform!--3024 (alpha.clj:132). ExceptionInfo Call to #'user/f did not conform to spec.
"oops" - failed: int? at: [:a]
;; Assertion failure
user=> (assert false)
Evaluation error at user/eval145 (NO_SOURCE_FILE:12). AssertionError Assert failed: false
;; Print error in repl
user=> (deftype T [a])
user.T
user=> (defmethod print-method T [_ w] (throw (Exception. "boom")))
#object[clojure.lang.MultiFn 0x5ed828d "clojure.lang.MultiFn@5ed828d"]
user=> (->T 1)
Error printing return value at user/eval152/fn--153 (NO_SOURCE_FILE:14). Exception boom
The big thing that Cursive used to do was also print a (largely irrelevant) stack trace (which clojure.main does not).