2024 Clojure 状态调查 中分享您的想法!

欢迎!请注意查看 关于 页面以了解更多此机制的信息。

0
Spec
当函数出现信息跟踪失败时,explain-data 包含 "调用者" 信息。然而,如果宏发生信息跟踪失败,则该信息丢失。

这个注释使我相信,预期行为是 explain-data 包含此信息,因此第三方错误打印程序可以显示它。

在下面的重现中,我设置了一个自定义打印机,仅用于捕获原始 explain-data(它不是一个有用的打印机,仅作为一种显示发生情况的手段)

重现


  (require '[clojure.spec.alpha :as s])
  (require '[clojure.spec.test.alpha :as st])
  (require '[clojure.specs.alpha :as s])


  (s/fdef my-fn
          :args (s/cat :x int?))
  (defn my-fn [x]
    x)

  (s/fdef my-macro
          :args (s/cat :x int?))
  (defmacro my-macro [x]
    x)

  (st/instrument)
  (def !ed (atom nil))
  (set! s/*explain-out* (fn [ed]
                          (reset! !ed ed)))
  (my-fn "")
  @!ed
  ;; {:clojure.spec.alpha/problems [{:path [:args :x], :pred clojure.core/int?, :val "", :via [], :in [0]}], :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x72029b0e "clojure.spec.alpha$regex_spec_impl$reify__2436@72029b0e"], :clojure.spec.alpha/value (""), :clojure.spec.alpha/args (""), :clojure.spec.alpha/failure :instrument, :clojure.spec.test.alpha/caller {:file "form-init8333540581183382896.clj", :line 548, :var-scope expound.alpha/eval27394}}

  ;; ^--- 注意有一个对 :clojure.spec.test.alpha/caller 的条目

  (my-macro "")
  @!ed

  ;; #:clojure.spec.alpha{:problems [{:path [:args :x], :pred clojure.core/int?, :val "", :via [], :in [0]}], :spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x479a6a73 "clojure.spec.alpha$regex_spec_impl$reify__2436@479a6a73"], :value (""), :args ("")}

  ;; ^--- 没有调用者信息

10 个答案

0

评论由:alexmiller 提供

您不能为宏创建断言,所以根据这样的描述,那份工单内容并不合理。但我猜您指的是在宏展开过程中的规格检查。

在宏检查的情况下,调用者信息由编译器知晓,并包含在包装的CompilerException中。我想这些信息可以从编译器传递到s/macroexpand-check中,可能产生与仪器化函数调用类似的结果。

0

评论者:bbrinck

啊,您是对的,谢谢您的澄清。

如果添加了调用者信息,那么在对规格在宏展开期间失败的情况进行错误报告时,添加一个特定的:clojure.spec.alpha/failure值也会有很大帮助。这将允许第三方工具在宏展开失败时显示特定类型的错误消息。

0

评论者:bbrinck

此外,能够访问宏名称的符号将有助于错误报告。

0

评论者:sohta

在我看来,对于仪器化失败和宏规格错误,{{explain-data}}应该包含函数(或宏)的fspec,就像我在CLJ-2218中之前建议的那样。

{{fspec}}的一些信息(如ns-qualified名称符号和行号等)包含在它的元数据中,因此规格错误报告器可以使用它们在统一的方式下为仪器化失败和宏规格错误提供更丰富和更精确的错误消息。

0

评论由:alexmiller 提供

在CLJ-2373之后,规格宏失败时的调用者信息将以包装的CompilerException形式传达(就像其他编译器和宏展开错误一样)。应考虑是否已经有效地解决了此问题。

0

评论者:bbrinck

我很高兴听说这将被包括在CompilerException中,但我想认为它仍然在spec explain-data中使用是有用的。

在打印规范错误时,如果能够在类似格式的打印微扩展规范错误和仪器错误(如果用户选择了自定义规范打印器),则将更加清晰。对于在微扩展错误和仪器错误的不同位置查找调用者信息可能会造成不便。

此外,规范打印器可以选择利用调用者信息做一些新颖的事情,例如在输出中添加颜色或直接打印实际代码而不是行号。对于规范打印器来说,能够对所有这些信息进行自定义将非常棒。

0
by

评论由:alexmiller 提供

问题是,在创建宏规范错误的时候,我们无法了解正确的调用者信息。那时的调用栈是编译器进行的宏扩展,而不是调用代码。宏扩展确实了解了那个上下文,并在包裹 CompilerException(这是 CompilerException 的主要目的)时添加了这个信息。

我认为要改变这一点,我们需要将信息通过信道传递到规范检查中。嗯,也许这是可能的。我会再次查看。

0
by

评论者:bbrinck

在最近的错误消息工作中(出色的工作!),符号似乎可用,但当前并未将其传递给 "explain-out" 函数:[链接](https://github.com/clojure/clojure/blob/b182982007df934394f0bc68b3a238ca9f200dd1/src/clj/clojure/main.clj#L268-L279)

是否有可能将此信息“关联”到传递给 "spec/explain-out" 的 "spec" 中?这将允许自定义打印机引用规范名称,这将会非常有用。

另一种方法是让用户在 REPL 中设置自定义错误处理器,但这比设置 "explain-out" 的第三方实现要复杂得多。

0
by
评论者:alexmiller

我不想让这一点只能通过 clojure 主 REPL 使用,如果使用其他 repl,那么最好在构建时就完成,我目前有更好的方法来做到这一点,但不知道是否还为 1.10 的时间太晚了。
0
by
参考:[链接](https://clojure.atlassian.net/browse/CLJ-2271)(由 bbrinck 报告)
...