improvements to exception messages and printing

Description

Problems

  • 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

Principles

  • 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

Proposed impl changes

  • 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

Discussion

  • 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

Patch Status

  • 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

    • Compiler

      • 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.

    • clojure.main

      • 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

Also see: https://dev.clojure.org/display/design/Exception+handling+update

Screener's Notes

See example-errors.txt attachment for resulting error message examples.

Environment

None

Attachments

13
  • 05 Sep 2018, 04:40 PM
  • 05 Sep 2018, 03:05 AM
  • 04 Sep 2018, 08:23 PM
  • 29 Aug 2018, 01:14 AM
  • 22 Aug 2018, 11:01 PM
  • 22 Aug 2018, 03:11 PM
  • 21 Aug 2018, 04:05 PM
  • 16 Aug 2018, 06:39 PM
  • 16 Aug 2018, 06:16 PM
  • 11 Aug 2018, 10:08 PM
  • 12 Jul 2018, 06:12 AM
  • 09 Jul 2018, 05:59 PM

Activity

Alex Miller September 5, 2018 at 3:05 AM

Added -9 patch that fixes test errors in -8 patch

Alex Miller September 5, 2018 at 1:42 AM

Applied spec patch...

Alex Miller August 22, 2018 at 11:01 PM

Added -7 that fixes a double-period on error reading from source file.

Alex Miller August 22, 2018 at 3:11 PM

-6 patch fixes a couple bugs in printing read errors from source files.

Alex Miller August 21, 2018 at 4:05 PM

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.

Completed

Details

Assignee

Reporter

Approval

Patch

Priority

Fix versions

Created July 9, 2018 at 5:50 PM
Updated September 5, 2018 at 4:40 PM
Resolved September 5, 2018 at 4:40 PM