cursive-ide / cursive

Cursive: The IDE for beautiful Clojure code
579 stars 7 forks source link

Review error messages in Cursive #2760

Open cursive-ide opened 1 year ago

cursive-ide commented 1 year ago

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
puredanger commented 1 year ago

The big thing that Cursive used to do was also print a (largely irrelevant) stack trace (which clojure.main does not).