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

欢迎!有关如何操作更多信息,请查看关于页面。

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 specs.test.alpha/failure :instrument, :clojure.spec.test.alpha/caller {:file "form-init8333540581183382896.clj", :line 548, :var-scope expound.alpha/eval27394}}

  ;; ^--- 注意有一个 :clojure specs.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

据我所知,对于工具化失败和宏规格错误应包含函数(或宏)的 fspec,就像我在 CLJ-2218 中之前建议的那样。

FSPEC 有一些有用的信息(比如规格化函数的命名空间合格名称符号和行号等),这些信息存储在其元数据中,因此规格错误报告者可以使用它们以统一的方式为工具化失败和宏规格错误提供更丰富、更精确的错误消息。

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 报告)
...