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

欢迎!请查看 关于 页面以获取更多关于此如何工作的信息。

0
Spec
当一个函数出现 instrumentation 失败时,explain-data 会包含 "caller" 信息。然而,如果 instrumentation 失败是针对宏的,那么这些信息就会缺失。

这个评论使我相信,预期行为是 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}} 有一些有用的信息,比如规范函数的命名空间限定名称符号和行号等,存储在其元数据中。因此,规范错误报告器可以使用这些信息在统一的方式下为插桩失败和宏规范错误提供更丰富、更精确的错误消息。

0

评论者:alexmiller

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

0

评论者:bbrinck

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

当打印spec错误时,如果可以以类似格式打印宏展开spec错误和instrumentation错误,将会更清晰(如果用户选择自定义spec打印机)。在宏展开错误和instrumentation错误中查找调用者信息可能变得混乱。

此外,spec打印机可以选择对调用者信息做新颖的事情,比如在输出中添加颜色或打印实际代码而不是行号。如果能将这些信息全部提供给spec打印机进行自定义,那就更好了。

0

评论者:alexmiller

问题是,在创建宏spec错误的时候,我们没有办法知道正确的调用者信息。在那个点,栈是编译器的宏展开,而不是调用的代码。Macroexpansion知道这个上下文,这就是它包装在CompilerException中的原因(这也是CompilerException的主要目的)。

我认为要改变这一点,我们需要将信息通过非带算法传递给spec检查。嗯,这可能是有可能的。我会再看看。

0

评论者: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
_评论者:alexmiller_

我不想让它只为clojure主REPL的tgough使用 - 如果在其他REPL中使用的话,最好在构造时就这样做。我现在更好地了解如何做这件事,但不知道是否还赶得上1.10版本。
0
by
参考: https://clojure.atlassian.net/browse/CLJ-2271(bbrinck 报告)
...