Discussions about "make errors better" often conflate error message, stacktraces, exception data
Errors do not clearly indicate in what "phase" of execution they occur (read, compile, macroexpand, evaluation, printing)
Errors during macroexpansion do not capture the location in caller source (like read and compile exceptions do), and thus the wrong "location" is reported
Clojure prints data into Throwable messages
big and ugly
outside user control
exception messages should be small strings
with the expectation that getMessage will be printed
totally controlled by throwing code
never print arbitrary data into a message
flow data all the way to the edge
ex-info & ex-data
macroexpand spec errors have a well known format
edge printing should be concise by default, configurable by user
edge functions like repl-caught respect print and spec bindings
concise summary by default
all the details when you want
stop printing data into message strings
ExceptionInfo.toString should list keys, not entire map contents
spec.alpha/macroexpand-check should stop including explain-out in message
and instead print data (configurably) at the edges repl/pst and main/repl-caught
print spec per explain-out
print ExceptionInfo keys
make CompilerException wrappers special at print time instead of construct time
CE message is "CompilerException + file/line"
wrapped message is whatever it is
edge printers should print both
what should tools do?
leverage the data provided in the exception chain to do whatever they want
can use main/repl-caught as a guide
what happens to programs that parse exception messages?
probably should not do this (Clojure's tests do this but try to minimize it)
this proposal changes some wrapper message text, but that was never part of the contract
clj-2373-spec-alpha-2.patch - for spec.alpha - don't print explain data into message (callers can do what they want with that)
clj-2373-9.patch - for clojure
LispReader - made ReaderException line/column fields public so CompilerEx can swipe them
CompilerException now implements IExceptionInfo and works with ex-data
Created well-known keys :clojure.error/source,line,column,phase,symbol
Created well-known phases :read, :macroexpand, :compile
Left existing CompilerException fields, but stored rest into data map
Construct new syntax error messages on construction
Use toString() to encapsulate combining wrapper and cause into a message
On read, spec, macroexpand exceptions, add appropriate wrapping
Tweaked existing compiler exception calls as much as possible to record symbol (more could potentially be done here - in some cases we have forms or non-symbol things we could report)
Moves "Cause:" down to second line except for macroexpand spec errors
Var - expose a method to get the symbol for a var. Many people have requested this functionality via a core function, maybe this is a step in that direction.
add init-cause (private) function to get initial root (rather than existing weird root-cause)
add ex-str function (public) to construct the message to print based on an ex. tools could use this. Embeds some of the phase/printing logic but piggiebacks on CompilerException.toString() for some as well. Handles spec problem ex-data specially - this could be genericized but I have not tried to do that here.
change repl-caught to rely on ex-str
change main repl code to catch and wrap read and print exceptions to specialize errors
test* - in general these are tweaks to test to check the cause message rather than the wrapper message with the help of a new assertion type defined in test-helper
See example-errors.txt attachment for resulting error message examples.
Added -5 patch which removes the macroexpand sub-phase differences and isolates that into the message construction. Also cleans up the repl read exception wrapping to not confusingly wrap in CompilerException.
-6 patch fixes a couple bugs in printing read errors from source files.
Added -7 that fixes a double-period on error reading from source file.
Applied spec patch...
Added -9 patch that fixes test errors in -8 patch