bhb / expound

Human-optimized error messages for clojure.spec
Eclipse Public License 1.0
924 stars 24 forks source link

Small *print-length* and/or *print-level* sometimes yield NPE #217

Closed alexandergunnarson closed 3 years ago

alexandergunnarson commented 3 years ago

I ran into this when setting *print-length* and *print-level*, such that the string that pprint-str outputs within highlighted-value doesn't actually contain the spec-failing part of the value.

A minimal example:

(def the-value (range 10))
;; Fails on the last element of the range
(def the-spec (clojure.spec.alpha/coll-of #(< % 9)))
(def the-explanation (clojure.spec.alpha/explain-data the-spec the-value))

(binding [*print-length* 5]
  (expound.alpha/printer the-explanation))

Yields an NPE:

#error {
 :cause nil
 :via
 [{:type java.lang.NullPointerException
   :message nil
   :at [clojure.string$replace invokeStatic "string.clj" 101]}]
 :trace
 [[clojure.string$replace invokeStatic "string.clj" 101]
  [clojure.string$replace invoke "string.clj" 75]
  [expound.printer$highlighted_value invokeStatic "printer.cljc" 423]
  [expound.printer$highlighted_value invoke "printer.cljc" 411]
  [expound.alpha$value_in_context invokeStatic "alpha.cljc" 103]
  [expound.alpha$value_in_context invoke "alpha.cljc" 83]
  [clojure.lang.AFn applyToHelper "AFn.java" 171]
  [clojure.lang.AFn applyTo "AFn.java" 144]
  [deft.untyped.spec.core$spec_checking_fn$fn__150452 doInvoke "core.cljc" 634]
  [clojure.lang.RestFn applyTo "RestFn.java" 137]
  [clojure.core$apply invokeStatic "core.clj" 675]
  [clojure.core$partial$fn__5858 doInvoke "core.clj" 2626]
  [clojure.lang.RestFn invoke "RestFn.java" 467]
  [expound.alpha$value_PLUS_conformed_value invokeStatic "alpha.cljc" 308]
  [expound.alpha$value_PLUS_conformed_value invoke "alpha.cljc" 296]
  [expound.alpha$eval72263$fn__72264 invoke "alpha.cljc" 314]
  [clojure.lang.MultiFn invoke "MultiFn.java" 261]
  [expound.alpha$format_err invokeStatic "alpha.cljc" 335]
  [expound.alpha$format_err invoke "alpha.cljc" 331]
  [expound.alpha$eval72543$fn__72544 invoke "alpha.cljc" 802]
  [clojure.lang.MultiFn invoke "MultiFn.java" 261]
  [expound.alpha$print_explain_data$iter__72550__72554$fn__72555$fn__72556 invoke "alpha.cljc" 850]
  [expound.alpha$print_explain_data$iter__72550__72554$fn__72555 invoke "alpha.cljc" 848]
  [clojure.lang.LazySeq sval "LazySeq.java" 42]
  [clojure.lang.LazySeq seq "LazySeq.java" 51]
  [clojure.lang.RT seq "RT.java" 535]
  [clojure.core$seq__5420 invokeStatic "core.clj" 139]
  [clojure.core$apply invokeStatic "core.clj" 662]
  [clojure.core$apply invoke "core.clj" 662]
  [expound.alpha$print_explain_data invokeStatic "alpha.cljc" 847]
  [expound.alpha$print_explain_data invoke "alpha.cljc" 833]
  [expound.alpha$printer_str invokeStatic "alpha.cljc" 988]
  [expound.alpha$printer_str invoke "alpha.cljc" 970]
  [expound.alpha$custom_printer$fn__72619 invoke "alpha.cljc" 1028]
  [expound.alpha$printer invokeStatic "alpha.cljc" 1036]
  [expound.alpha$printer invoke "alpha.cljc" 1033]
  [clojure.lang.AFn applyToHelper "AFn.java" 154]
  [clojure.lang.AFn applyTo "AFn.java" 144]
  [deft.untyped.spec.core$spec_checking_fn$fn__150452 doInvoke "core.cljc" 634]
  [clojure.lang.RestFn invoke "RestFn.java" 408]
  [user$eval152293$fn__152294 invoke "form-init14274858799706833167.clj" 2]
  [user$eval152293 invokeStatic "form-init14274858799706833167.clj" 1]
  [user$eval152293 invoke "form-init14274858799706833167.clj" 1]
  [clojure.lang.Compiler eval "Compiler.java" 7181]
  [clojure.lang.Compiler eval "Compiler.java" 7171]
  [clojure.lang.Compiler eval "Compiler.java" 7136]
  [clojure.core$eval invokeStatic "core.clj" 3202]
  [clojure.core$eval invoke "core.clj" 3198]
  [clojure.main$repl$read_eval_print__9112$fn__9115 invoke "main.clj" 437]
  [clojure.main$repl$read_eval_print__9112 invoke "main.clj" 437]
  [clojure.main$repl$fn__9121 invoke "main.clj" 458]
  [clojure.main$repl invokeStatic "main.clj" 458]
  [clojure.main$repl doInvoke "main.clj" 368]
  [clojure.lang.RestFn invoke "RestFn.java" 1523]
  [clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__1151 invoke "interruptible_eval.clj" 87]
  [clojure.lang.AFn applyToHelper "AFn.java" 152]
  [clojure.lang.AFn applyTo "AFn.java" 144]
  [clojure.core$apply invokeStatic "core.clj" 667]
  [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1977]
  [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1977]
  [clojure.lang.RestFn invoke "RestFn.java" 425]
  [clojure.tools.nrepl.middleware.interruptible_eval$evaluate invokeStatic "interruptible_eval.clj" 85]
  [clojure.tools.nrepl.middleware.interruptible_eval$evaluate invoke "interruptible_eval.clj" 55]
  [clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__1197$fn__1200 invoke "interruptible_eval.clj" 222]
  [clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__1192 invoke "interruptible_eval.clj" 190]
  [clojure.lang.AFn run "AFn.java" 22]
  [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1130]
  [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 630]
  [java.lang.Thread run "Thread.java" 832]]}

One possible fix for this is to unset *print-length* and *print-level*:

(binding [*print-length* nil
          *print-level*  nil]
  ...)

Loosely related to https://github.com/bhb/expound/issues/15.

bhb commented 3 years ago

@alexandergunnarson Thanks for reporting this! I'll take a look

alexandergunnarson commented 3 years ago

No problem — thanks!

bhb commented 3 years ago

@alexandergunnarson Thanks for reporting this! This has been merged into master.

And thanks @vemv for the fix!

vemv commented 3 years ago

🍻!

alexandergunnarson commented 3 years ago

No problem @bhb, and thanks for the fix @vemv!