Welcome! Please see the About page for a little more info on how this works.

0 votes
in Spec by

Summary:

1) Given instrumented function {{map-f}} that is called lazily, e.g. as {{(def ls (map map-f (range)))}}.
2) Also given instrumented function {{varargs-f}}, a varargs function. When you pass a {{LazySeq}} to {{varargs-f}} some or all elements are realized as a consequence of conforming.
3) The problem: when calling {{(apply varargs-f ls)}} some invalid calls to {{map-f}} can go unnoticed.

Repro example:

In the following code {{map-f}} is expected to throw when called with something else than a {{Symbol}}. However the call to {{map-f}} with a {{String}} slips through.

`
(ns repro
(:require
[clojure.spec.alpha :as s]
[clojure.spec.test.alpha :as stest]))

(defn map-f [x]
(println "called my-fn with type" (type x))
(println (deref #'clojure.spec.test.alpha/instrument-enabled))
{x 1})

(s/fdef map-f :args (s/cat :x symbol?))

(defn varargs-f [& maps]
true)

(s/fdef varargs-f :args (s/cat :maps (s/* map?)))

(defn repro [& args]
(apply varargs-f (map map-f args)))

(stest/instrument)

(repro 'foo 'bar "baz")
`

Output:

called my-fn with type clojure.lang.Symbol true called my-fn with type clojure.lang.Symbol true called my-fn with type java.lang.String ;; <-- nil

Cause:

When {{varargs-f}}'s arguments are realized as a result of conforming, some calls to {{map-fn}} are made in the scope of {{with-instrument-disabled}}: https://github.com/clojure/spec.alpha/blob/f23ea614b3cb658cff0044a027cacdd76831edcf/src/main/clojure/clojure/spec/test/alpha.clj#L140

Background:

I ran into this issue when spec'ing {{merge-with}}. Spec'ed fns in some test namespaces didn't throw anymore, because this line in clojure.test has a similar problem with regards to spec as described above: https://github.com/clojure/clojure/blob/28efe345d5e995dc152a0286fb0be81443a0d9ac/src/clj/clojure/test.clj#L775

CLJ-2443.patch contains a fix, but I realize it may not be perfect yet. Therefore I provided CLJ-2443-test.patch that only contains the unit test which can be used to test an alternative solution.

4 Answers

0 votes
by

Comment made by: borkdude

CLJ-2443.patch fixes this problem by conforming a Cons type argument outside the scope of with-instrument-disabled.
Test provided. Conforming this patch works with speculative (https://github.com/slipset/speculative).

0 votes
by

Comment made by: borkdude

CLJ-2443-test only contains the test, not the fix itself.

0 votes
by

Comment made by: borkdude

Updated description with CLJ-2443-test.patch

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2443 (reported by borkdude)
...