Uploaded image for project: 'Clojure'
  1. CLJ-2373

improvements to exception messages and printing

    Details

    • Approval:
      Ok
    • Patch:
      Code

      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.

        Attachments

        1. clj-2373-2.patch
          7 kB
        2. clj-2373-3.patch
          14 kB
        3. clj-2373-4.patch
          27 kB
        4. clj-2373-5.patch
          28 kB
        5. clj-2373-6.patch
          28 kB
        6. clj-2373-7.patch
          27 kB
        7. clj-2373-8.patch
          28 kB
        8. clj-2373-9.patch
          29 kB
        9. clj-2373-clojure-1.patch
          5 kB
        10. clj-2373-spec-alpha-1.patch
          1 kB
        11. clj-2373-spec-alpha-2.patch
          2 kB
        12. example-errors.txt
          2 kB
        13. example-errors-1.9.txt
          2 kB

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              stu Stuart Halloway
            • Votes:
              11 Vote for this issue
              Watchers:
              10 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: