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

欢迎!请参阅 关于 页面,了解更多关于其工作方式的信息。

0
规范
当一个函数的仪器失效时,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发表

您不能对一个宏进行代码覆盖率检查,所以这个工单部分按原样写是不合理的。但我推测你的意思是扩展宏时的规范检查。

在宏检查的情况下,调用者信息由编译器知道,并包含在包装编译器异常中。我想这个信息可以被传递给s/macroexpand-check(从编译器那里),并且可能产生与代码覆盖率检查类似的结果。

0

评论由:bbrinck发表

是的,你说得对,谢谢澄清。

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

0

评论由:bbrinck发表

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

0

评论由:sohta发表

我认为,对于代码覆盖率检查失败和宏规范错误,应该包含函数(或宏)的规范(fspec),正如我在CLJ-2218中之前所建议的。

fspec具有一些有用的信息(例如规范函数的ns-qualified名称符号和行号等),它们位于其元数据中,因此规范错误报告程序可以使用它们以统一的方式为代码覆盖率检查和宏规范错误提供更加丰富和精确的错误消息。

0

评论由:alexmiller发表

在CLJ-2373之后,对于规范宏失败的调用者信息将通过包装编译器异常传达(就像其他编译器和宏扩展错误一样)。应考虑是否已有效地解决此问题。

0

评论由:bbrinck发表

很高兴听到这将被包含在 CompilerException 中,但我认为将其包含在规范解释数据中仍然是有用的。

当打印规范错误时,如果用户选择了自定义规范打印器,那么将宏展开规范错误和监视器错误以类似格式打印会更清晰。在宏展开错误和监视器错误的不同位置查找调用者信息可能会导致混乱。

此外,规范打印器可能会选择使用调用者信息以新奇的方式做事情,如添加颜色到输出或打印实际代码而不是行号。如果能将这些信息全部提供给规范打印器来自定义将是非常好的。

0

评论由:alexmiller发表

问题是,在创建宏规范错误的地方,我们没有任何方法知道合适的调用者信息。那时的堆栈是编译器宏展开,而不是调用代码。宏展开确实知道这个上下文,并且这是它添加到 CompilerException 中的内容(这是 CompilerException 的主要目的)。

我认为要改变这一点,我们需要将信息通过非带外的形式传递到规范检查中。嗯,可能这是可行的。我将再次检查它。

0

评论由:bbrinck发表

随着最近错误信息工作(伟大的工作!)符号似乎可用,但它目前并未传递到 "explain-out" 函数:https://github.com/clojure/clojure/blob/b182982007df934394f0bc68b3a238ca9f200dd1/src/clj/clojure/main.clj#L268-L279

是否可以将此 "assoc" 到传递到 "spec/explain-out" 的 "spec" 中?这将允许自定义打印机引用规范名称,这将非常有用。

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

0
评论者:alexmiller

我不想让它只适用于 clojure 主 repl - 更好是将其应用于使用其他 repl 的情况下,因此最好在构建时完成。我现在对如何操作有了更好的理解,但不确定是否为 1.10 版本已太迟。
0
参考资料:https://clojure.atlassian.net/browse/CLJ-2271(报告人:bbrinck)
...