2024 状态 Clojure 问卷调查中分享您的看法!

欢迎!请参阅关于页面以了解更多关于此如何运作的信息。

0 个投票
Spec
当函数的仪表化失败时,explain-data 包含 "caller" 信息。然而,如果仪表化失败是指宏,则该信息将丢失。

此评论使我相信,解释数据的预期行为是包含此信息,以便第三方错误打印程序可以显示它。

在下面的复现中,我设置了一个自定义打印器,仅用于捕获原始 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

依我之见,对于仪器化失败和宏规范错误,应包含函数(或宏)的 fspec,正如我在 CLJ-2218 中之前所建议的那样。

一个 {{fspec}} 在其元数据中包含有关规范函数的一些有用信息(如 ns 限定名称符号和行号等),因此规范错误报告器可以使用这些信息为仪器化失败和宏规范错误提供更丰富、更精确的错误信息,并保持统一性。

0 个投票

评论者:alexmiller

在 CLJ-2373 之后,对于规范宏失败时的调用者信息将通过包装器 CompilerException 传递(就像其他编译器和宏展开错误一样)。应考虑此问题是否因此问题得到了有效解决。

0 个投票

评论者:bbrinck

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

当打印一个规范错误时,如果可以将宏扩展规范错误和仪表化错误以相似格式打印(如果用户选择自定义规范打印程序),则会更加清晰。在宏扩展错误和仪表化错误的不同位置查找调用者信息可能会造成困扰。

此外,规范打印程序可以选择使用这些调用者信息进行一些新颖的处理,例如在输出中添加颜色或打印实际代码而不是行号。这将对规范打印程序进行定制非常有用。

0 个投票

评论者:alexmiller

这里的难点在于,在创建宏规范错误的那一刻,我们无法了解正确的调用者信息。那个点处的堆栈是编译器宏扩展,而不是调用代码。宏扩展确实知道这个上下文,并且它就是在包装带有 CompilerException(这是 CompilerException 的主要用途)时添加的。

我认为为了改变这一点,我们需要将信息以非绑定的方式传递给规范检查。好吧,这可能是有可能的。我会再次检查它。

0 个投票

评论者: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 个投票
由:alexmiller 提出

我不希望这仅在 clojure 主 REPL 中可用 - 如果在其他 REPL 中使用,则会更好,因此最好在构建时进行。我现在对如何实施这一点有了更好的了解,但不知道是否太晚了,无法用于 1.10。
0 个投票
参考:https://clojure.atlassian.net/browse/CLJ-2271(报告者:bbrinck)
...