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

欢迎!请查看 关于 页面以了解更多关于这里是怎样工作的信息。

0
Spec
当函数的仪器出现故障时,explain-data 包含 "caller" 信息。然而,如果仪器故障是针对宏的,则此信息会丢失。

这条评论让我相信,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 中。我想这可能可以通过 Compiler 传送给 s/macroexpand-check,并可能产生与已仪器化函数调用类似的结果。

0

评论由:bbrinck

啊,你说得对,感谢澄清。

如果添加了调用者信息,对于在宏扩展期间失败的规范,添加一个特定的 :clojure.spec.alpha/failure 值将非常有用。这将允许第三方工具在宏扩展失败时显示特定类型的错误消息。

0

评论由:bbrinck

此外,了解宏名称的符号将有助于错误报告。

0

评论由:sohta

据我所知,对于仪器化失败和宏规范错误,应包含与 CLJ-2218 中建议的相同的 function (或 macro) 的 fspec。

{{fspec}} 包含有关规范函数的一些有用信息(例如 ns-qualified 名称符号和行号等),这些信息存储在其元数据中,因此规范错误报告器可以使用它们以统一的方式为仪器化失败和宏规范错误生成更丰富、更精确的错误消息。

0

评论由:alexmiller

在 CLJ-2373 之后,规范宏失败的调用信息将通过包装器 CompilerException 进行传递(正如其他编译器和宏扩展错误一样)。应考虑这个问题是否因此得到了有效解决。

0
by

评论由:bbrinck

很高兴听到这将被包含在CompilerException中,但我认为把它包含在spec explain-data中仍然很有用。

当打印spec错误时,如果用户选择了自定义的spec打印机,那么将宏展开spec错误和instrumentation错误以类似格式打印会更清晰。在不同的地方寻找宏展开错误和instrumentation错误的调用者信息可能会很麻烦。

此外,spec打印机可以选择使用此调用者信息做一些新奇的事,如为输出添加颜色或打印实际代码而不是行号。如果所有这些信息都可以供spec打印机自定义,那就太好了。

0
by

评论由:alexmiller

问题是,当宏spec错误被创建时,我们没有任何方式知道正确的调用者信息。在那个点上调用栈是编译器的宏展开,而不是调用代码。宏展开确实知道这个上下文,并且这就是它用CompilerException包装时添加的内容(这是CompilerException的主要目的)。

我认为我们需要通过某种方式将这方面的信息传输到spec检查中。也许这是可能的。我会再次看看。

0
by

评论由:bbrinck

在最近的错误消息工作中(伟大的工作!)符号似乎可用,但它目前并没有传递给“explain-out”函数:[查看链接](https://github.com/clojure/clojure/blob/b182982007df934394f0bc68b3a238ca9f200dd1/src/clj/clojure/main.clj#L268-L279)

是否可以将它“assoc”到传递给“spec/explain-out”的“spec”中?这将允许自定义打印机引用spec的名称,这将会非常有用。

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

0
by
_Comment made by: alexmiller_

我情愿不要让这个过程只对Clojure主REPL有用 - 如果使用其他REPL,最好是始终进行,所以最好是构建时进行。我现在对如何做到这一点有了更好的把握,但不知道是否为1.10版本做得太晚了。
0
参考:https://clojure.atlassian.net/browse/CLJ-2271(由 bbrinck 报告)
...